Refactor lucene results

This commit is contained in:
Andrea Cavalli 2021-03-03 15:03:25 +01:00
parent 7f15a6e099
commit 319abeaf30
15 changed files with 392 additions and 195 deletions

View File

@ -7,6 +7,7 @@ import it.cavallium.dbengine.client.query.current.data.ScoreSort;
import it.cavallium.dbengine.database.LLDocument;
import it.cavallium.dbengine.database.LLItem;
import it.cavallium.dbengine.database.LLLuceneIndex;
import it.cavallium.dbengine.database.LLSignal;
import it.cavallium.dbengine.database.LLTerm;
import it.cavallium.dbengine.database.disk.LLLocalDatabaseConnection;
import it.cavallium.dbengine.lucene.LuceneUtils;
@ -46,11 +47,15 @@ public class IndicizationExample {
.build(),
"id"
))
.flatMap(results -> results
.flatMap(results -> Mono.from(results
.results()
.flatMap(r -> r)
.doOnNext(value -> System.out.println("Value: " + value))
.then(results.totalHitsCount())
.doOnNext(signal -> {
if (signal.isValue()) {
System.out.println("Value: " + signal.getValue());
}
})
.filter(LLSignal::isTotalHitsCount))
)
.doOnNext(count -> System.out.println("Total hits: " + count))
.doOnTerminate(() -> System.out.println("Completed"))
@ -122,7 +127,11 @@ public class IndicizationExample {
.flatMap(results -> LuceneUtils.mergeStream(results
.results(), MultiSort.topScoreRaw(), 10L)
.doOnNext(value -> System.out.println("Value: " + value))
.then(results.totalHitsCount())
.then(Mono.from(results
.results()
.flatMap(part -> part)
.filter(LLSignal::isTotalHitsCount)
.map(LLSignal::getTotalHitsCount)))
)
.doOnNext(count -> System.out.println("Total hits: " + count))
.doOnTerminate(() -> System.out.println("Completed"))

View File

@ -82,14 +82,39 @@ public class LuceneIndex<T, U> implements LLSnapshottable {
@Nullable MultiSort<SearchResultKey<T>> sort,
LLScoreMode scoreMode,
@Nullable Long limit) {
var mappedKeys = llSearchResult
Flux<Flux<LuceneSignal<SearchResultKey<T>>>> mappedKeys = llSearchResult
.results()
.map(flux -> flux.map(item -> new SearchResultKey<>(indicizer.getKey(item.getKey()), item.getScore())));
.map(flux -> flux.map(signal -> {
if (signal.isValue()) {
return LuceneSignal.value(
new SearchResultKey<T>(indicizer.getKey(signal.getValue().getKey()),
signal.getValue().getScore()
));
} else {
return LuceneSignal.totalHitsCount(signal.getTotalHitsCount());
}
}));
MultiSort<SearchResultKey<T>> finalSort;
if (scoreMode != LLScoreMode.COMPLETE_NO_SCORES && sort == null) {
sort = MultiSort.topScore();
finalSort = MultiSort.topScore();
} else {
finalSort = sort;
}
var sortedKeys = LuceneUtils.mergeStream(mappedKeys, sort, limit);
return new SearchResultKeys<>(llSearchResult.totalHitsCount(), sortedKeys);
MultiSort<LuceneSignal<SearchResultKey<T>>> mappedSort;
if (finalSort != null) {
mappedSort = new MultiSort<>(finalSort.getQuerySort(), (signal1, signal2) -> {
if (signal1.isValue() && signal2.isValue()) {
return finalSort.getResultSort().compare((signal1.getValue()), signal2.getValue());
} else {
return 0;
}
});
} else {
mappedSort = null;
}
Flux<LuceneSignal<SearchResultKey<T>>> sortedKeys = LuceneUtils.mergeStream(mappedKeys, mappedSort, limit);
return new SearchResultKeys<>(sortedKeys);
}
private SearchResult<T, U> transformLuceneResultWithValues(LLSearchResult llSearchResult,
@ -97,17 +122,39 @@ public class LuceneIndex<T, U> implements LLSnapshottable {
LLScoreMode scoreMode,
@Nullable Long limit,
ValueGetter<T, U> valueGetter) {
var mappedKeys = llSearchResult
Flux<Flux<LuceneSignal<SearchResultItem<T, U>>>> mappedKeys = llSearchResult
.results()
.map(flux -> flux.flatMapSequential(item -> {
var key = indicizer.getKey(item.getKey());
return valueGetter.get(key).map(value -> new SearchResultItem<>(key, value, item.getScore()));
.map(flux -> flux.flatMapSequential(signal -> {
if (signal.isValue()) {
var key = indicizer.getKey(signal.getValue().getKey());
return valueGetter
.get(key)
.map(value -> LuceneSignal.value(new SearchResultItem<>(key, value, signal.getValue().getScore())));
} else {
return Mono.just(LuceneSignal.totalHitsCount((signal.getTotalHitsCount())));
}
}));
MultiSort<SearchResultItem<T, U>> finalSort;
if (scoreMode != LLScoreMode.COMPLETE_NO_SCORES && sort == null) {
sort = MultiSort.topScoreWithValues();
finalSort = MultiSort.topScoreWithValues();
} else {
finalSort = sort;
}
var sortedKeys = LuceneUtils.mergeStream(mappedKeys, sort, limit);
return new SearchResult<>(llSearchResult.totalHitsCount(), sortedKeys);
MultiSort<LuceneSignal<SearchResultItem<T, U>>> mappedSort;
if (finalSort != null) {
mappedSort = new MultiSort<>(finalSort.getQuerySort(), (signal1, signal2) -> {
if (signal1.isValue() && signal2.isValue()) {
return finalSort.getResultSort().compare((signal1.getValue()), signal2.getValue());
} else {
return 0;
}
});
} else {
mappedSort = null;
}
var sortedKeys = LuceneUtils.mergeStream(mappedKeys, mappedSort, limit);
return new SearchResult<>(sortedKeys);
}
/**
@ -200,9 +247,10 @@ public class LuceneIndex<T, U> implements LLSnapshottable {
}
public Mono<Long> count(@Nullable CompositeSnapshot snapshot, Query query) {
return this.search(ClientQueryParams.<SearchResultKey<T>>builder().snapshot(snapshot).query(query).limit(0).build())
.flatMap(SearchResultKeys::totalHitsCount)
.single();
return Mono.from(this.search(ClientQueryParams.<SearchResultKey<T>>builder().snapshot(snapshot).query(query).limit(0).build())
.flatMapMany(SearchResultKeys::results)
.filter(LuceneSignal::isTotalHitsCount)
.map(LuceneSignal::getTotalHitsCount));
}
public boolean isLowMemoryMode() {

View File

@ -0,0 +1,22 @@
package it.cavallium.dbengine.client;
public interface LuceneSignal<T> {
boolean isValue();
boolean isTotalHitsCount();
T getValue();
long getTotalHitsCount();
static <T> LuceneSignalValue<T> value(T value) {
return new LuceneSignalValue<>(value);
}
static <T> LuceneSignalTotalHitsCount<T> totalHitsCount(long totalHitsCount) {
return new LuceneSignalTotalHitsCount<>(totalHitsCount);
}
<U> LuceneSignalTotalHitsCount<U> mapTotalHitsCount();
}

View File

@ -0,0 +1,36 @@
package it.cavallium.dbengine.client;
public class LuceneSignalTotalHitsCount<T> implements LuceneSignal<T> {
private final long totalHitsCount;
public LuceneSignalTotalHitsCount(long totalHitsCount) {
this.totalHitsCount = totalHitsCount;
}
@Override
public boolean isValue() {
return false;
}
@Override
public boolean isTotalHitsCount() {
return true;
}
@Override
public T getValue() {
throw new UnsupportedOperationException("This object is not value");
}
@Override
public long getTotalHitsCount() {
return totalHitsCount;
}
@Override
public <U> LuceneSignalTotalHitsCount<U> mapTotalHitsCount() {
//noinspection unchecked
return (LuceneSignalTotalHitsCount<U>) this;
}
}

View File

@ -0,0 +1,34 @@
package it.cavallium.dbengine.client;
public class LuceneSignalValue<T> implements LuceneSignal<T> {
private final T value;
public LuceneSignalValue(T value) {
this.value = value;
}
public boolean isValue() {
return true;
}
@Override
public boolean isTotalHitsCount() {
return false;
}
@Override
public T getValue() {
return value;
}
@Override
public long getTotalHitsCount() {
throw new UnsupportedOperationException("This object is not TotalHitsCount");
}
@Override
public <U> LuceneSignalTotalHitsCount<U> mapTotalHitsCount() {
throw new UnsupportedOperationException("This object is not TotalHitsCount");
}
}

View File

@ -5,6 +5,7 @@ import it.cavallium.dbengine.client.query.current.data.RandomSort;
import it.cavallium.dbengine.client.query.current.data.ScoreSort;
import it.cavallium.dbengine.client.query.current.data.Sort;
import it.cavallium.dbengine.database.LLKeyScore;
import it.cavallium.dbengine.database.LLSignal;
import java.util.Comparator;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
@ -67,8 +68,15 @@ public class MultiSort<T> {
return new MultiSort<>(RandomSort.of(), (a, b) -> 0);
}
public static MultiSort<LLKeyScore> topScoreRaw() {
return new MultiSort<>(ScoreSort.of(), Comparator.comparingDouble(LLKeyScore::getScore).reversed());
public static MultiSort<LLSignal> topScoreRaw() {
Comparator<LLKeyScore> comp = Comparator.comparingDouble(LLKeyScore::getScore).reversed();
return new MultiSort<>(ScoreSort.of(), (signal1, signal2) -> {
if (signal1.isValue() && signal2.isValue()) {
return comp.compare(signal1.getValue(), signal2.getValue());
} else {
return 0;
}
});
}
public static <T> MultiSort<SearchResultKey<T>> topScore() {

View File

@ -1,54 +1,35 @@
package it.cavallium.dbengine.client;
import java.util.Objects;
import java.util.StringJoiner;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
@EqualsAndHashCode
@ToString
public class SearchResult<T, U> {
private final Mono<Long> totalHitsCount;
private final Flux<SearchResultItem<T, U>> results;
private final Flux<LuceneSignal<SearchResultItem<T, U>>> results;
public SearchResult(Mono<Long> totalHitsCount, Flux<SearchResultItem<T, U>> results) {
this.totalHitsCount = totalHitsCount;
public SearchResult(Flux<LuceneSignal<SearchResultItem<T, U>>> results) {
this.results = results;
}
public static <T, U> SearchResult<T, U> empty() {
return new SearchResult<>(Mono.just(0L), Flux.empty());
return new SearchResult<>(Flux.just(LuceneSignal.totalHitsCount(0L)));
}
public Mono<Long> totalHitsCount() {
return this.totalHitsCount;
}
public Flux<SearchResultItem<T, U>> results() {
public Flux<LuceneSignal<SearchResultItem<T, U>>> results() {
return this.results;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SearchResult<?, ?> that = (SearchResult<?, ?>) o;
return Objects.equals(totalHitsCount, that.totalHitsCount) && Objects.equals(results, that.results);
}
@Override
public int hashCode() {
return Objects.hash(totalHitsCount, results);
}
@Override
public String toString() {
return new StringJoiner(", ", SearchResult.class.getSimpleName() + "[", "]")
.add("totalHitsCount=" + totalHitsCount)
.add("results=" + results)
.toString();
public Tuple2<Flux<SearchResultItem<T, U>>, Mono<Long>> splitShared() {
Flux<LuceneSignal<SearchResultItem<T, U>>> shared = results.publish().refCount(2);
return Tuples.of(
shared.filter(LuceneSignal::isValue).map(LuceneSignal::getValue).share(),
Mono.from(shared.filter(LuceneSignal::isTotalHitsCount).map(LuceneSignal::getTotalHitsCount)).cache()
);
}
}

View File

@ -1,63 +1,40 @@
package it.cavallium.dbengine.client;
import it.cavallium.dbengine.database.collections.Joiner.ValueGetter;
import java.util.Objects;
import java.util.StringJoiner;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@EqualsAndHashCode
@ToString
public class SearchResultKeys<T> {
private final Mono<Long> totalHitsCount;
private final Flux<SearchResultKey<T>> results;
private final Flux<LuceneSignal<SearchResultKey<T>>> results;
public SearchResultKeys(Mono<Long> totalHitsCount, Flux<SearchResultKey<T>> results) {
this.totalHitsCount = totalHitsCount;
public SearchResultKeys(Flux<LuceneSignal<SearchResultKey<T>>> results) {
this.results = results;
}
public static <T, U> SearchResultKeys<T> empty() {
return new SearchResultKeys<>(Mono.just(0L), Flux.empty());
return new SearchResultKeys<>(Flux.just(LuceneSignal.totalHitsCount(0L)));
}
public Mono<Long> totalHitsCount() {
return this.totalHitsCount;
}
public Flux<SearchResultKey<T>> results() {
public Flux<LuceneSignal<SearchResultKey<T>>> results() {
return this.results;
}
public <U> SearchResult<T, U> withValues(ValueGetter<T, U> valuesGetter) {
return new SearchResult<>(totalHitsCount,
results.flatMap(item -> valuesGetter
.get(item.getKey())
.map(value -> new SearchResultItem<>(item.getKey(), value, item.getScore())))
return new SearchResult<>(
results.flatMapSequential(item -> {
if (item.isValue()) {
return valuesGetter
.get(item.getValue().getKey())
.map(value -> LuceneSignal.value(new SearchResultItem<>(item.getValue().getKey(), value, item.getValue().getScore())));
} else {
return Mono.just(item.mapTotalHitsCount());
}
})
);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SearchResultKeys<?> that = (SearchResultKeys<?>) o;
return Objects.equals(totalHitsCount, that.totalHitsCount) && Objects.equals(results, that.results);
}
@Override
public int hashCode() {
return Objects.hash(totalHitsCount, results);
}
@Override
public String toString() {
return new StringJoiner(", ", SearchResultKeys.class.getSimpleName() + "[", "]")
.add("totalHitsCount=" + totalHitsCount)
.add("results=" + results)
.toString();
}
}

View File

@ -2,7 +2,7 @@ package it.cavallium.dbengine.database;
import java.util.Objects;
public class LLKeyScore {
public class LLKeyScore implements LLSignal {
private final String key;
private final float score;
@ -45,4 +45,24 @@ public class LLKeyScore {
", score=" + score +
'}';
}
@Override
public boolean isValue() {
return true;
}
@Override
public boolean isTotalHitsCount() {
return false;
}
@Override
public LLKeyScore getValue() {
return this;
}
@Override
public long getTotalHitsCount() {
throw new UnsupportedOperationException();
}
}

View File

@ -49,9 +49,11 @@ public interface LLLuceneIndex extends LLSnapshottable {
default Mono<Long> count(@Nullable LLSnapshot snapshot, Query query) {
QueryParams params = QueryParams.of(query, 0, Nullablefloat.empty(), NoSort.of(), ScoreMode.of(false, false));
return this.search(snapshot, params, null)
.flatMap(LLSearchResult::totalHitsCount)
.single();
return Mono.from(this.search(snapshot, params, null)
.flatMapMany(LLSearchResult::results)
.flatMap(s -> s)
.filter(LLSignal::isTotalHitsCount)
.map(LLSignal::getTotalHitsCount));
}
boolean isLowMemoryMode();

View File

@ -8,33 +8,22 @@ import reactor.core.publisher.Mono;
public class LLSearchResult {
private final Mono<Long> totalHitsCount;
private final Flux<Flux<LLKeyScore>> results;
private final Flux<Flux<LLSignal>> results;
public LLSearchResult(Mono<Long> totalHitsCount, Flux<Flux<LLKeyScore>> results) {
this.totalHitsCount = totalHitsCount;
public LLSearchResult(Flux<Flux<LLSignal>> results) {
this.results = results;
}
public static LLSearchResult empty() {
return new LLSearchResult(Mono.just(0L), Flux.just(Flux.empty()));
return new LLSearchResult(Flux.just(Flux.just(new LLTotalHitsCount(0L))));
}
@NotNull
public static BiFunction<LLSearchResult, LLSearchResult, LLSearchResult> accumulator() {
return (a, b) -> {
var mergedTotals = a.totalHitsCount.flatMap(aL -> b.totalHitsCount.map(bL -> aL + bL));
var mergedResults = Flux.merge(a.results, b.results);
return new LLSearchResult(mergedTotals, mergedResults);
};
return (a, b) -> new LLSearchResult(Flux.merge(a.results, b.results));
}
public Mono<Long> totalHitsCount() {
return this.totalHitsCount;
}
public Flux<Flux<LLKeyScore>> results() {
public Flux<Flux<LLSignal>> results() {
return this.results;
}
@ -50,11 +39,6 @@ public class LLSearchResult {
return false;
}
final LLSearchResult other = (LLSearchResult) o;
final Object this$totalHitsCount = this.totalHitsCount();
final Object other$totalHitsCount = other.totalHitsCount();
if (!Objects.equals(this$totalHitsCount, other$totalHitsCount)) {
return false;
}
final Object this$results = this.results();
final Object other$results = other.results();
return Objects.equals(this$results, other$results);
@ -63,14 +47,12 @@ public class LLSearchResult {
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $totalHitsCount = this.totalHitsCount();
result = result * PRIME + ($totalHitsCount == null ? 43 : $totalHitsCount.hashCode());
final Object $results = this.results();
result = result * PRIME + ($results == null ? 43 : $results.hashCode());
return result;
}
public String toString() {
return "LLSearchResult(totalHitsCount=" + this.totalHitsCount() + ", results=" + this.results() + ")";
return "LLSearchResult(results=" + this.results() + ")";
}
}

View File

@ -0,0 +1,20 @@
package it.cavallium.dbengine.database;
public interface LLSignal {
boolean isValue();
boolean isTotalHitsCount();
LLKeyScore getValue();
long getTotalHitsCount();
static LLSignal value(LLKeyScore value) {
return value;
}
static LLTotalHitsCount totalHitsCount(long totalHitsCount) {
return new LLTotalHitsCount(totalHitsCount);
}
}

View File

@ -0,0 +1,54 @@
package it.cavallium.dbengine.database;
import java.util.Objects;
import java.util.StringJoiner;
public class LLTotalHitsCount implements LLSignal {
private final long value;
public LLTotalHitsCount(long value) {
this.value = value;
}
@Override
public boolean isValue() {
return false;
}
@Override
public boolean isTotalHitsCount() {
return true;
}
public LLKeyScore getValue() {
throw new UnsupportedOperationException();
}
@Override
public long getTotalHitsCount() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LLTotalHitsCount that = (LLTotalHitsCount) o;
return value == that.value;
}
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public String toString() {
return new StringJoiner(", ", LLTotalHitsCount.class.getSimpleName() + "[", "]").add("count=" + value).toString();
}
}

View File

@ -9,8 +9,10 @@ import it.cavallium.dbengine.database.LLKeyScore;
import it.cavallium.dbengine.database.LLLuceneIndex;
import it.cavallium.dbengine.database.LLSearchCollectionStatisticsGetter;
import it.cavallium.dbengine.database.LLSearchResult;
import it.cavallium.dbengine.database.LLSignal;
import it.cavallium.dbengine.database.LLSnapshot;
import it.cavallium.dbengine.database.LLTerm;
import it.cavallium.dbengine.database.LLTotalHitsCount;
import it.cavallium.dbengine.database.LLUtils;
import it.cavallium.dbengine.lucene.LuceneUtils;
import it.cavallium.dbengine.lucene.ScheduledTaskLifecycle;
@ -29,7 +31,9 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Supplier;
@ -57,11 +61,6 @@ import org.warp.commonutils.log.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.GroupedFlux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
import reactor.core.publisher.Sinks.EmissionException;
import reactor.core.publisher.Sinks.EmitResult;
import reactor.core.publisher.Sinks.Many;
import reactor.core.publisher.Sinks.One;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
import reactor.util.function.Tuple2;
@ -546,77 +545,82 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
Query luceneQuery,
Sort luceneSort,
ScoreMode luceneScoreMode) {
var searchFlux = Flux.<LLSignal>create(sink -> {
AtomicBoolean cancelled = new AtomicBoolean();
AtomicLong requests = new AtomicLong();
Semaphore requestsAvailable = new Semaphore(0);
sink.onDispose(() -> {
cancelled.set(true);
requestsAvailable.release();
});
sink.onRequest(delta -> {
requests.addAndGet(delta);
requestsAvailable.release();
});
One<Long> totalHitsCountSink = Sinks.one();
Many<LLKeyScore> topKeysSink = Sinks
.many()
.unicast()
.onBackpressureBuffer();
var searchFlux = Mono.<Void>create(sink -> {
try {
if (doDistributedPre) {
allowOnlyQueryParsingCollectorStreamSearcher.search(indexSearcher, luceneQuery);
totalHitsCountSink.tryEmitValue(0L);
} else {
int boundedLimit = Math.max(0, limit > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) limit);
streamSearcher.search(indexSearcher,
luceneQuery,
boundedLimit,
luceneSort,
luceneScoreMode,
minCompetitiveScore,
keyFieldName,
keyScore -> {
EmitResult result = topKeysSink.tryEmitNext(fixKeyScore(keyScore, scoreDivisor));
if (result.isSuccess()) {
return HandleResult.CONTINUE;
} else {
if (result == EmitResult.FAIL_CANCELLED) {
logger.debug("Fail to emit next value: cancelled");
//noinspection BlockingMethodInNonBlockingContext
requestsAvailable.acquire();
requestsAvailable.release();
if (!cancelled.get()) {
if (doDistributedPre) {
//noinspection BlockingMethodInNonBlockingContext
allowOnlyQueryParsingCollectorStreamSearcher.search(indexSearcher, luceneQuery);
sink.next(new LLTotalHitsCount(0L));
} else {
int boundedLimit = Math.max(0, limit > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) limit);
//noinspection BlockingMethodInNonBlockingContext
streamSearcher.search(indexSearcher,
luceneQuery,
boundedLimit,
luceneSort,
luceneScoreMode,
minCompetitiveScore,
keyFieldName,
keyScore -> {
try {
while (requests.get() <= 0 && !cancelled.get()) {
requestsAvailable.acquire();
}
if (!cancelled.get()) {
requests.decrementAndGet();
sink.next(fixKeyScore(keyScore, scoreDivisor));
return HandleResult.CONTINUE;
} else {
return HandleResult.HALT;
}
} catch (Exception ex) {
sink.error(ex);
cancelled.set(true);
return HandleResult.HALT;
} else if (result == EmitResult.FAIL_TERMINATED) {
logger.debug("Fail to emit next value: terminated");
return HandleResult.HALT;
} else if (result == EmitResult.FAIL_ZERO_SUBSCRIBER) {
logger.error("Fail to emit next value: zero subscriber. You must subscribe to results before total hits if you specified a limit > 0!");
sink.error(new EmissionException(result));
throw new EmissionException(result);
} else {
throw new EmissionException(result);
}
},
totalHitsCount -> {
try {
while (requests.get() <= 0 && !cancelled.get()) {
requestsAvailable.acquire();
}
if (!cancelled.get()) {
requests.decrementAndGet();
sink.next(new LLTotalHitsCount(totalHitsCount));
}
} catch (Exception ex) {
sink.error(ex);
cancelled.set(true);
}
}
},
totalHitsCount -> {
EmitResult result = totalHitsCountSink.tryEmitValue(totalHitsCount);
if (result.isFailure()) {
if (result == EmitResult.FAIL_CANCELLED) {
logger.debug("Fail to emit total hits count: cancelled");
} else if (result == EmitResult.FAIL_TERMINATED) {
logger.debug("Fail to emit total hits count: terminated");
} else if (result == EmitResult.FAIL_ZERO_SUBSCRIBER) {
logger.debug("Fail to emit total hits count: zero subscriber");
} else {
sink.error(new EmissionException(result));
throw new EmissionException(result);
}
}
}
);
);
}
if (!cancelled.get()) {
sink.complete();
}
}
topKeysSink.tryEmitComplete();
sink.success();
} catch (IOException e) {
topKeysSink.tryEmitError(e);
totalHitsCountSink.tryEmitError(e);
sink.error(e);
} catch (Exception ex) {
sink.error(ex);
}
}).subscribeOn(luceneQueryScheduler).cache();
}).subscribeOn(luceneQueryScheduler);
return new LLSearchResult(
Mono.<Long>firstWithValue(searchFlux.then(Mono.empty()), totalHitsCountSink.asMono()),
Flux.<Flux<LLKeyScore>>merge(searchFlux.then(Mono.empty()), Flux.just(topKeysSink.asFlux()))
);
return new LLSearchResult(Flux.just(searchFlux));
}
@Override

View File

@ -257,7 +257,7 @@ public class LLLocalMultiLuceneIndex implements LLLuceneIndex {
var resultsWithTermination = result
.results()
.map(flux -> flux.doOnTerminate(() -> completedAction(actionId)));
return new LLSearchResult(result.totalHitsCount(), resultsWithTermination);
return new LLSearchResult(resultsWithTermination);
} else {
return result;
}
@ -318,7 +318,7 @@ public class LLLocalMultiLuceneIndex implements LLLuceneIndex {
var resultsWithTermination = result
.results()
.map(flux -> flux.doOnTerminate(() -> completedAction(actionId)));
return new LLSearchResult(result.totalHitsCount(), resultsWithTermination);
return new LLSearchResult(resultsWithTermination);
} else {
return result;
}