78
78
import org .elasticsearch .rest .RestStatus ;
79
79
import org .elasticsearch .search .Scroll ;
80
80
import org .elasticsearch .search .SearchHit ;
81
+ import org .elasticsearch .search .SearchHits ;
81
82
import org .reactivestreams .Publisher ;
82
83
import org .springframework .data .elasticsearch .ElasticsearchException ;
83
84
import org .springframework .data .elasticsearch .client .ClientConfiguration ;
92
93
import org .springframework .http .HttpStatus ;
93
94
import org .springframework .http .MediaType ;
94
95
import org .springframework .http .client .reactive .ReactorClientHttpConnector ;
96
+ import org .springframework .lang .Nullable ;
95
97
import org .springframework .util .Assert ;
96
98
import org .springframework .util .ObjectUtils ;
97
99
import org .springframework .util .ReflectionUtils ;
@@ -339,41 +341,55 @@ public Flux<SearchHit> scroll(HttpHeaders headers, SearchRequest searchRequest)
339
341
}
340
342
341
343
throw new IllegalArgumentException (
342
- String .format ("Cannot handle '%s'. Please make sure to use a 'SearchRequest' or 'SearchScrollRequest'." ));
344
+ String .format ("Cannot handle '%s'. Please make sure to use a 'SearchRequest' or 'SearchScrollRequest'." , it ));
343
345
});
344
346
345
- ScrollState state = new ScrollState ();
347
+ return Flux . usingWhen ( Mono . fromSupplier ( ScrollState :: new ),
346
348
347
- Flux <SearchHit > searchHits = inbound .doOnNext (searchResponse -> {
348
- state .updateScrollId (searchResponse .getScrollId ());
349
- }).<SearchResponse > handle ((searchResponse , sink ) -> {
349
+ scrollState -> {
350
350
351
- if (searchResponse .getHits () != null && searchResponse .getHits ().getHits () != null
352
- && searchResponse .getHits ().getHits ().length == 0 ) {
351
+ Flux <SearchHit > searchHits = inbound .<SearchResponse > handle ((searchResponse , sink ) -> {
353
352
354
- inbound . onComplete ( );
355
- outbound . onComplete ();
353
+ scrollState . updateScrollId ( searchResponse . getScrollId () );
354
+ if ( isEmpty ( searchResponse . getHits ())) {
356
355
357
- } else {
356
+ inbound .onComplete ();
357
+ outbound .onComplete ();
358
358
359
- sink . next ( searchResponse );
359
+ } else {
360
360
361
- SearchScrollRequest searchScrollRequest = new SearchScrollRequest (state .getScrollId ()).scroll (scrollTimeout );
362
- request .next (searchScrollRequest );
363
- }
361
+ sink .next (searchResponse );
362
+
363
+ SearchScrollRequest searchScrollRequest = new SearchScrollRequest (scrollState .getScrollId ())
364
+ .scroll (scrollTimeout );
365
+ request .next (searchScrollRequest );
366
+ }
364
367
365
- }).map (SearchResponse ::getHits ) //
366
- .flatMap (Flux ::fromIterable ) //
367
- .doOnComplete (() -> {
368
+ }).map (SearchResponse ::getHits ) //
369
+ .flatMap (Flux ::fromIterable );
368
370
369
- ClearScrollRequest clearScrollRequest = new ClearScrollRequest ();
370
- clearScrollRequest .scrollIds (state .getScrollIds ());
371
+ return searchHits .doOnSubscribe (ignore -> exchange .subscribe (inbound ));
371
372
372
- // just send the request, resources get cleaned up anyways after scrollTimeout has been reached.
373
- sendRequest (clearScrollRequest , RequestCreator .clearScroll (), ClearScrollResponse .class , headers ).subscribe ();
374
- });
373
+ }, state -> cleanupScroll (headers , state ), //
374
+ state -> cleanupScroll (headers , state ), //
375
+ state -> cleanupScroll (headers , state )); //
376
+ }
377
+
378
+ private static boolean isEmpty (@ Nullable SearchHits hits ) {
379
+ return hits != null && hits .getHits () != null && hits .getHits ().length == 0 ;
380
+ }
375
381
376
- return searchHits .doOnSubscribe (ignore -> exchange .subscribe (inbound ));
382
+ private Publisher <?> cleanupScroll (HttpHeaders headers , ScrollState state ) {
383
+
384
+ if (state .getScrollIds ().isEmpty ()) {
385
+ return Mono .empty ();
386
+ }
387
+
388
+ ClearScrollRequest clearScrollRequest = new ClearScrollRequest ();
389
+ clearScrollRequest .scrollIds (state .getScrollIds ());
390
+
391
+ // just send the request, resources get cleaned up anyways after scrollTimeout has been reached.
392
+ return sendRequest (clearScrollRequest , RequestCreator .clearScroll (), ClearScrollResponse .class , headers );
377
393
}
378
394
379
395
/*
@@ -645,17 +661,20 @@ public Collection<ElasticsearchHost> hosts() {
645
661
*/
646
662
private static class ScrollState {
647
663
648
- private Object lock = new Object ();
664
+ private final Object lock = new Object ();
649
665
666
+ private final List <String > pastIds = new ArrayList <>(1 );
650
667
private String scrollId ;
651
- private List <String > pastIds = new ArrayList <>(1 );
652
668
653
669
String getScrollId () {
654
670
return scrollId ;
655
671
}
656
672
657
673
List <String > getScrollIds () {
658
- return Collections .unmodifiableList (pastIds );
674
+
675
+ synchronized (lock ) {
676
+ return Collections .unmodifiableList (new ArrayList <>(pastIds ));
677
+ }
659
678
}
660
679
661
680
void updateScrollId (String scrollId ) {
@@ -669,6 +688,5 @@ void updateScrollId(String scrollId) {
669
688
}
670
689
}
671
690
}
672
-
673
691
}
674
692
}
0 commit comments