package it.cavallium.dbengine.lucene.searcher; import io.net5.buffer.api.Send; import it.cavallium.dbengine.database.LLKeyScore; import it.cavallium.dbengine.database.LLUtils; import it.cavallium.dbengine.database.disk.LLIndexSearchers; import it.cavallium.dbengine.database.disk.LLTempLMDBEnv; import it.cavallium.dbengine.lucene.FullDocs; import it.cavallium.dbengine.lucene.LLFieldDoc; import it.cavallium.dbengine.lucene.LuceneUtils; import it.cavallium.dbengine.lucene.collector.LMDBFullFieldDocCollector; import it.cavallium.dbengine.lucene.searcher.LLSearchTransformer.TransformerInput; import java.io.Closeable; import java.io.IOException; import java.util.List; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopDocsCollector; import org.apache.lucene.search.TopFieldCollector; import org.apache.lucene.search.TopScoreDocCollector; import org.warp.commonutils.log.Logger; import org.warp.commonutils.log.LoggerFactory; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public class OfficialSearcher implements MultiSearcher, Closeable { protected static final Logger logger = LoggerFactory.getLogger(OfficialSearcher.class); private final LLTempLMDBEnv env; public OfficialSearcher() throws IOException { this.env = new LLTempLMDBEnv(); } @Override public Mono> collectMulti(Mono> indexSearchersMono, LocalQueryParams queryParams, String keyFieldName, LLSearchTransformer transformer) { Mono queryParamsMono; if (transformer == LLSearchTransformer.NO_TRANSFORMATION) { queryParamsMono = Mono.just(queryParams); } else { queryParamsMono = LLUtils.usingSendResource(indexSearchersMono, indexSearchers -> transformer.transform(Mono .fromSupplier(() -> new TransformerInput(indexSearchers, queryParams))), true); } return queryParamsMono.flatMap(queryParams2 -> LLUtils.usingSendResource(indexSearchersMono, indexSearchers -> this // Search results .search(indexSearchers.shards(), queryParams2) // Compute the results .transform(fullDocsMono -> this.computeResults(fullDocsMono, indexSearchers, keyFieldName, queryParams2)) // Ensure that one LuceneSearchResult is always returned .single(), false)); } /** * Search effectively the raw results */ @SuppressWarnings({"unchecked", "rawtypes"}) private Mono search(Iterable indexSearchers, LocalQueryParams queryParams) { return Mono .fromCallable(() -> { LLUtils.ensureBlocking(); var totalHitsThreshold = queryParams.getTotalHitsThresholdInt(); if (queryParams.isSorted() && !queryParams.isSortedByScore()) { return TopFieldCollector.createSharedManager(queryParams.sort(), queryParams.limitInt(), null, totalHitsThreshold); } else { return TopScoreDocCollector.createSharedManager(queryParams.limitInt(), null, totalHitsThreshold); } }) .flatMap(sharedManager -> Flux .fromIterable(indexSearchers) .flatMap(shard -> Mono.fromCallable(() -> { LLUtils.ensureBlocking(); var collector = sharedManager.newCollector(); assert queryParams.complete() == collector.scoreMode().isExhaustive(); queryParams.getScoreModeOptional().ifPresent(scoreMode -> { assert scoreMode == collector.scoreMode(); }); shard.search(queryParams.query(), collector); return collector; })) .collectList() .flatMap(collectors -> Mono.fromCallable(() -> { LLUtils.ensureBlocking(); return sharedManager.reduce((List) collectors); })) ); } /** * Compute the results, extracting useful data */ private Mono> computeResults(Mono dataMono, LLIndexSearchers indexSearchers, String keyFieldName, LocalQueryParams queryParams) { return dataMono.map(data -> { var totalHitsCount = LuceneUtils.convertTotalHitsCount(data.totalHits); Flux hitsFlux = LuceneUtils .convertHits(Flux.fromArray(data.scoreDocs), indexSearchers.shards(), keyFieldName, true) .skip(queryParams.offsetLong()) .take(queryParams.limitLong(), true); return new LuceneSearchResult(totalHitsCount, hitsFlux, indexSearchers::close).send(); }); } @Override public void close() throws IOException { env.close(); } @Override public String getName() { return "official"; } }