Search in sources :

Example 1 with Entry

use of org.opensearch.search.suggest.Suggest.Suggestion.Entry in project OpenSearch by opensearch-project.

the class Suggest method reduce.

public static List<Suggestion<? extends Entry<? extends Option>>> reduce(Map<String, List<Suggest.Suggestion>> groupedSuggestions) {
    List<Suggestion<? extends Entry<? extends Option>>> reduced = new ArrayList<>(groupedSuggestions.size());
    for (Map.Entry<String, List<Suggestion>> unmergedResults : groupedSuggestions.entrySet()) {
        List<Suggestion> value = unmergedResults.getValue();
        Class<? extends Suggestion> suggestionClass = null;
        for (Suggestion suggestion : value) {
            if (suggestionClass == null) {
                suggestionClass = suggestion.getClass();
            } else if (suggestionClass != suggestion.getClass()) {
                throw new IllegalArgumentException("detected mixed suggestion results, due to querying on old and new completion suggester," + " query on a single completion suggester version");
            }
        }
        Suggestion reduce = value.get(0).reduce(value);
        reduce.trim();
        reduced.add(reduce);
    }
    return reduced;
}
Also used : TermSuggestion(org.opensearch.search.suggest.term.TermSuggestion) CompletionSuggestion(org.opensearch.search.suggest.completion.CompletionSuggestion) PhraseSuggestion(org.opensearch.search.suggest.phrase.PhraseSuggestion) Entry(org.opensearch.search.suggest.Suggest.Suggestion.Entry) ArrayList(java.util.ArrayList) Option(org.opensearch.search.suggest.Suggest.Suggestion.Entry.Option) ArrayList(java.util.ArrayList) List(java.util.List) HashMap(java.util.HashMap) Map(java.util.Map)

Example 2 with Entry

use of org.opensearch.search.suggest.Suggest.Suggestion.Entry in project OpenSearch by opensearch-project.

the class SuggestionTests method doTestFromXContent.

@SuppressWarnings({ "rawtypes" })
private void doTestFromXContent(boolean addRandomFields) throws IOException {
    ToXContent.Params params = new ToXContent.MapParams(Collections.singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true"));
    for (Class<Suggestion<? extends Entry<? extends Option>>> type : SUGGESTION_TYPES) {
        Suggestion suggestion = createTestItem(type);
        XContentType xContentType = randomFrom(XContentType.values());
        boolean humanReadable = randomBoolean();
        BytesReference originalBytes = toShuffledXContent(suggestion, xContentType, params, humanReadable);
        BytesReference mutated;
        if (addRandomFields) {
            // - "contexts" is an object consisting of key/array pairs, we shouldn't add anything random there
            // - there can be inner search hits fields inside this option where we cannot add random stuff
            // - the root object should be excluded since it contains the named suggestion arrays
            // We also exclude options that contain SearchHits, as all unknown fields
            // on a root level of SearchHit are interpreted as meta-fields and will be kept.
            Predicate<String> excludeFilter = path -> path.isEmpty() || path.endsWith(CompletionSuggestion.Entry.Option.CONTEXTS.getPreferredName()) || path.endsWith("highlight") || path.contains("fields") || path.contains("_source") || path.contains("inner_hits") || path.contains("options");
            mutated = insertRandomFields(xContentType, originalBytes, excludeFilter, random());
        } else {
            mutated = originalBytes;
        }
        Suggestion parsed;
        try (XContentParser parser = createParser(xContentType.xContent(), mutated)) {
            ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser);
            ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser);
            ensureExpectedToken(XContentParser.Token.START_ARRAY, parser.nextToken(), parser);
            parsed = Suggestion.fromXContent(parser);
            assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
            assertNull(parser.nextToken());
        }
        assertEquals(suggestion.getName(), parsed.getName());
        assertEquals(suggestion.getEntries().size(), parsed.getEntries().size());
        // We don't parse size via xContent, instead we set it to -1 on the client side
        assertEquals(-1, parsed.getSize());
        assertToXContentEquivalent(originalBytes, toXContent(parsed, xContentType, params, humanReadable), xContentType);
    }
}
Also used : ToXContent(org.opensearch.common.xcontent.ToXContent) BytesReference(org.opensearch.common.bytes.BytesReference) TermSuggestion(org.opensearch.search.suggest.term.TermSuggestion) OpenSearchAssertions.assertToXContentEquivalent(org.opensearch.test.hamcrest.OpenSearchAssertions.assertToXContentEquivalent) BytesReference(org.opensearch.common.bytes.BytesReference) XContentTestUtils.insertRandomFields(org.opensearch.test.XContentTestUtils.insertRandomFields) ToXContent(org.opensearch.common.xcontent.ToXContent) Entry(org.opensearch.search.suggest.Suggest.Suggestion.Entry) Suggestion(org.opensearch.search.suggest.Suggest.Suggestion) Supplier(java.util.function.Supplier) XContentParser(org.opensearch.common.xcontent.XContentParser) RestSearchAction(org.opensearch.rest.action.search.RestSearchAction) XContentHelper.toXContent(org.opensearch.common.xcontent.XContentHelper.toXContent) NamedObjectNotFoundException(org.opensearch.common.xcontent.NamedObjectNotFoundException) Map(java.util.Map) Predicate(java.util.function.Predicate) OpenSearchTestCase(org.opensearch.test.OpenSearchTestCase) Set(java.util.Set) CompletionSuggestion(org.opensearch.search.suggest.completion.CompletionSuggestion) IOException(java.io.IOException) XContentParserUtils.ensureExpectedToken(org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken) PhraseSuggestion(org.opensearch.search.suggest.phrase.PhraseSuggestion) XContent(org.opensearch.common.xcontent.XContent) Option(org.opensearch.search.suggest.Suggest.Suggestion.Entry.Option) NamedXContentRegistry(org.opensearch.common.xcontent.NamedXContentRegistry) JsonXContent(org.opensearch.common.xcontent.json.JsonXContent) XContentType(org.opensearch.common.xcontent.XContentType) DeprecationHandler(org.opensearch.common.xcontent.DeprecationHandler) Collections(java.util.Collections) Text(org.opensearch.common.text.Text) TermSuggestion(org.opensearch.search.suggest.term.TermSuggestion) Suggestion(org.opensearch.search.suggest.Suggest.Suggestion) CompletionSuggestion(org.opensearch.search.suggest.completion.CompletionSuggestion) PhraseSuggestion(org.opensearch.search.suggest.phrase.PhraseSuggestion) Entry(org.opensearch.search.suggest.Suggest.Suggestion.Entry) XContentType(org.opensearch.common.xcontent.XContentType) Option(org.opensearch.search.suggest.Suggest.Suggestion.Entry.Option) XContentParser(org.opensearch.common.xcontent.XContentParser)

Example 3 with Entry

use of org.opensearch.search.suggest.Suggest.Suggestion.Entry in project OpenSearch by opensearch-project.

the class SuggestionTests method createTestItem.

@SuppressWarnings({ "unchecked", "rawtypes" })
public static Suggestion<? extends Entry<? extends Option>> createTestItem(Class<? extends Suggestion> type) {
    String name = randomAlphaOfLengthBetween(5, 10);
    // note: size will not be rendered via "toXContent", only passed on internally on transport layer
    int size = randomInt();
    Supplier<Entry> entrySupplier;
    Suggestion suggestion;
    if (type == TermSuggestion.class) {
        suggestion = new TermSuggestion(name, size, randomFrom(SortBy.values()));
        entrySupplier = () -> SuggestionEntryTests.createTestItem(TermSuggestion.Entry.class);
    } else if (type == PhraseSuggestion.class) {
        suggestion = new PhraseSuggestion(name, size);
        entrySupplier = () -> SuggestionEntryTests.createTestItem(PhraseSuggestion.Entry.class);
    } else if (type == CompletionSuggestion.class) {
        suggestion = new CompletionSuggestion(name, size, randomBoolean());
        entrySupplier = () -> SuggestionEntryTests.createTestItem(CompletionSuggestion.Entry.class);
    } else {
        throw new UnsupportedOperationException("type not supported [" + type + "]");
    }
    int numEntries;
    if (frequently()) {
        if (type == CompletionSuggestion.class) {
            // CompletionSuggestion can have max. one entry
            numEntries = 1;
        } else {
            numEntries = randomIntBetween(1, 5);
        }
    } else {
        // also occasionally test zero entries
        numEntries = 0;
    }
    for (int i = 0; i < numEntries; i++) {
        suggestion.addTerm(entrySupplier.get());
    }
    return suggestion;
}
Also used : TermSuggestion(org.opensearch.search.suggest.term.TermSuggestion) Suggestion(org.opensearch.search.suggest.Suggest.Suggestion) CompletionSuggestion(org.opensearch.search.suggest.completion.CompletionSuggestion) PhraseSuggestion(org.opensearch.search.suggest.phrase.PhraseSuggestion) Entry(org.opensearch.search.suggest.Suggest.Suggestion.Entry) PhraseSuggestion(org.opensearch.search.suggest.phrase.PhraseSuggestion) CompletionSuggestion(org.opensearch.search.suggest.completion.CompletionSuggestion) TermSuggestion(org.opensearch.search.suggest.term.TermSuggestion)

Example 4 with Entry

use of org.opensearch.search.suggest.Suggest.Suggestion.Entry in project OpenSearch by opensearch-project.

the class PhraseSuggester method innerExecute.

/*
     * More Ideas:
     *   - add ability to find whitespace problems -> we can build a poor mans decompounder with our index based on a automaton?
     *   - add ability to build different error models maybe based on a confusion matrix?
     *   - try to combine a token with its subsequent token to find / detect word splits (optional)
     *      - for this to work we need some way to defined the position length of a candidate
     *   - phonetic filters could be interesting here too for candidate selection
     */
@Override
public Suggestion<? extends Entry<? extends Option>> innerExecute(String name, PhraseSuggestionContext suggestion, IndexSearcher searcher, CharsRefBuilder spare) throws IOException {
    double realWordErrorLikelihood = suggestion.realworldErrorLikelihood();
    final PhraseSuggestion response = new PhraseSuggestion(name, suggestion.getSize());
    final IndexReader indexReader = searcher.getIndexReader();
    List<PhraseSuggestionContext.DirectCandidateGenerator> generators = suggestion.generators();
    final int numGenerators = generators.size();
    final List<CandidateGenerator> gens = new ArrayList<>(generators.size());
    for (int i = 0; i < numGenerators; i++) {
        PhraseSuggestionContext.DirectCandidateGenerator generator = generators.get(i);
        DirectSpellChecker directSpellChecker = generator.createDirectSpellChecker();
        Terms terms = MultiTerms.getTerms(indexReader, generator.field());
        if (terms != null) {
            gens.add(new DirectCandidateGenerator(directSpellChecker, generator.field(), generator.suggestMode(), indexReader, realWordErrorLikelihood, generator.size(), generator.preFilter(), generator.postFilter(), terms));
        }
    }
    final String suggestField = suggestion.getField();
    final Terms suggestTerms = MultiTerms.getTerms(indexReader, suggestField);
    if (gens.size() > 0 && suggestTerms != null) {
        final NoisyChannelSpellChecker checker = new NoisyChannelSpellChecker(realWordErrorLikelihood, suggestion.getRequireUnigram(), suggestion.getTokenLimit());
        final BytesRef separator = suggestion.separator();
        WordScorer wordScorer = suggestion.model().newScorer(indexReader, suggestTerms, suggestField, realWordErrorLikelihood, separator);
        Result checkerResult;
        try (TokenStream stream = tokenStream(suggestion.getAnalyzer(), suggestion.getText(), spare, suggestion.getField())) {
            checkerResult = checker.getCorrections(stream, new MultiCandidateGeneratorWrapper(suggestion.getShardSize(), gens.toArray(new CandidateGenerator[gens.size()])), suggestion.maxErrors(), suggestion.getShardSize(), wordScorer, suggestion.confidence(), suggestion.gramSize());
        }
        PhraseSuggestion.Entry resultEntry = buildResultEntry(suggestion, spare, checkerResult.cutoffScore);
        response.addTerm(resultEntry);
        final BytesRefBuilder byteSpare = new BytesRefBuilder();
        final TemplateScript.Factory scriptFactory = suggestion.getCollateQueryScript();
        final boolean collatePrune = (scriptFactory != null) && suggestion.collatePrune();
        for (int i = 0; i < checkerResult.corrections.length; i++) {
            Correction correction = checkerResult.corrections[i];
            spare.copyUTF8Bytes(correction.join(SEPARATOR, byteSpare, null, null));
            boolean collateMatch = true;
            if (scriptFactory != null) {
                // Checks if the template query collateScript yields any documents
                // from the index for a correction, collateMatch is updated
                final Map<String, Object> vars = suggestion.getCollateScriptParams();
                vars.put(SUGGESTION_TEMPLATE_VAR_NAME, spare.toString());
                QueryShardContext shardContext = suggestion.getShardContext();
                final String querySource = scriptFactory.newInstance(vars).execute();
                try (XContentParser parser = XContentFactory.xContent(querySource).createParser(shardContext.getXContentRegistry(), LoggingDeprecationHandler.INSTANCE, querySource)) {
                    QueryBuilder innerQueryBuilder = AbstractQueryBuilder.parseInnerQueryBuilder(parser);
                    final ParsedQuery parsedQuery = shardContext.toQuery(innerQueryBuilder);
                    collateMatch = Lucene.exists(searcher, parsedQuery.query());
                }
            }
            if (!collateMatch && !collatePrune) {
                continue;
            }
            Text phrase = new Text(spare.toString());
            Text highlighted = null;
            if (suggestion.getPreTag() != null) {
                spare.copyUTF8Bytes(correction.join(SEPARATOR, byteSpare, suggestion.getPreTag(), suggestion.getPostTag()));
                highlighted = new Text(spare.toString());
            }
            if (collatePrune) {
                resultEntry.addOption(new PhraseSuggestion.Entry.Option(phrase, highlighted, (float) (correction.score), collateMatch));
            } else {
                resultEntry.addOption(new PhraseSuggestion.Entry.Option(phrase, highlighted, (float) (correction.score)));
            }
        }
    } else {
        response.addTerm(buildResultEntry(suggestion, spare, Double.MIN_VALUE));
    }
    return response;
}
Also used : TokenStream(org.apache.lucene.analysis.TokenStream) ParsedQuery(org.opensearch.index.query.ParsedQuery) ArrayList(java.util.ArrayList) QueryBuilder(org.opensearch.index.query.QueryBuilder) AbstractQueryBuilder(org.opensearch.index.query.AbstractQueryBuilder) Result(org.opensearch.search.suggest.phrase.NoisyChannelSpellChecker.Result) Entry(org.opensearch.search.suggest.Suggest.Suggestion.Entry) QueryShardContext(org.opensearch.index.query.QueryShardContext) DirectSpellChecker(org.apache.lucene.search.spell.DirectSpellChecker) BytesRef(org.apache.lucene.util.BytesRef) BytesRefBuilder(org.apache.lucene.util.BytesRefBuilder) Terms(org.apache.lucene.index.Terms) MultiTerms(org.apache.lucene.index.MultiTerms) Text(org.opensearch.common.text.Text) IndexReader(org.apache.lucene.index.IndexReader) TemplateScript(org.opensearch.script.TemplateScript) XContentParser(org.opensearch.common.xcontent.XContentParser)

Example 5 with Entry

use of org.opensearch.search.suggest.Suggest.Suggestion.Entry in project OpenSearch by opensearch-project.

the class SuggestPhase method execute.

public void execute(SearchContext context) {
    final SuggestionSearchContext suggest = context.suggest();
    if (suggest == null) {
        return;
    }
    try {
        CharsRefBuilder spare = new CharsRefBuilder();
        final List<Suggestion<? extends Entry<? extends Option>>> suggestions = new ArrayList<>(suggest.suggestions().size());
        for (Map.Entry<String, SuggestionSearchContext.SuggestionContext> entry : suggest.suggestions().entrySet()) {
            SuggestionSearchContext.SuggestionContext suggestion = entry.getValue();
            Suggester<SuggestionContext> suggester = suggestion.getSuggester();
            Suggestion<? extends Entry<? extends Option>> result = suggester.execute(entry.getKey(), suggestion, context.searcher(), spare);
            if (result != null) {
                assert entry.getKey().equals(result.name);
                suggestions.add(result);
            }
        }
        context.queryResult().suggest(new Suggest(suggestions));
    } catch (IOException e) {
        throw new OpenSearchException("I/O exception during suggest phase", e);
    }
}
Also used : ArrayList(java.util.ArrayList) IOException(java.io.IOException) SuggestionContext(org.opensearch.search.suggest.SuggestionSearchContext.SuggestionContext) Suggestion(org.opensearch.search.suggest.Suggest.Suggestion) Entry(org.opensearch.search.suggest.Suggest.Suggestion.Entry) SuggestionContext(org.opensearch.search.suggest.SuggestionSearchContext.SuggestionContext) Option(org.opensearch.search.suggest.Suggest.Suggestion.Entry.Option) OpenSearchException(org.opensearch.OpenSearchException) CharsRefBuilder(org.apache.lucene.util.CharsRefBuilder) Map(java.util.Map)

Aggregations

Entry (org.opensearch.search.suggest.Suggest.Suggestion.Entry)7 Option (org.opensearch.search.suggest.Suggest.Suggestion.Entry.Option)5 Text (org.opensearch.common.text.Text)4 CompletionSuggestion (org.opensearch.search.suggest.completion.CompletionSuggestion)4 PhraseSuggestion (org.opensearch.search.suggest.phrase.PhraseSuggestion)4 TermSuggestion (org.opensearch.search.suggest.term.TermSuggestion)4 ArrayList (java.util.ArrayList)3 Map (java.util.Map)3 Suggestion (org.opensearch.search.suggest.Suggest.Suggestion)3 IOException (java.io.IOException)2 BytesReference (org.opensearch.common.bytes.BytesReference)2 XContentParser (org.opensearch.common.xcontent.XContentParser)2 Collections (java.util.Collections)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Set (java.util.Set)1 Predicate (java.util.function.Predicate)1 Supplier (java.util.function.Supplier)1 TokenStream (org.apache.lucene.analysis.TokenStream)1 IndexReader (org.apache.lucene.index.IndexReader)1