Skip to content

suggest handling #2187

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import co.elastic.clients.elasticsearch.core.MgetResponse;
import co.elastic.clients.elasticsearch.core.explain.ExplanationDetail;
import co.elastic.clients.elasticsearch.core.get.GetResult;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.NestedIdentity;
import co.elastic.clients.json.JsonData;
Expand Down Expand Up @@ -142,6 +143,21 @@ public static SearchDocument from(Hit<?> hit, JsonpMapper jsonpMapper) {
highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing());
}

public static SearchDocument from(CompletionSuggestOption<EntityAsMap> completionSuggestOption) {

Document document = completionSuggestOption.source() != null ? Document.from(completionSuggestOption.source())
: Document.create();
document.setIndex(completionSuggestOption.index());

if (completionSuggestOption.id() != null) {
document.setId(completionSuggestOption.id());
}

float score = completionSuggestOption.score() != null ? completionSuggestOption.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, new Object[] {}, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), null, null, null, completionSuggestOption.routing());
}

@Nullable
private static Explanation from(@Nullable co.elastic.clients.elasticsearch.core.explain.Explanation explanation) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,30 @@

import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import co.elastic.clients.elasticsearch.core.search.*;
import co.elastic.clients.json.JsonpMapper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.search.SearchHits;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.PhraseSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.elasticsearch.core.suggest.response.TermSuggestion;
import org.springframework.data.elasticsearch.support.ScoreDoc;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

/**
* Factory class to create {@link SearchDocumentResponse} instances.
Expand All @@ -43,6 +49,9 @@
* @since 4.4
*/
class SearchDocumentResponseBuilder {

private static final Log LOGGER = LogFactory.getLog(SearchDocumentResponseBuilder.class);

/**
* creates a SearchDocumentResponse from the {@link SearchResponse}
*
Expand Down Expand Up @@ -80,7 +89,7 @@ public static <T> SearchDocumentResponse from(ResponseBody<EntityAsMap> response
* @return the {@link SearchDocumentResponse}
*/
public static <T> SearchDocumentResponse from(HitsMetadata<?> hitsMetadata, @Nullable String scrollId,
Map<String, Aggregate> aggregations, Map<String, List<Suggestion<EntityAsMap>>> suggestES,
@Nullable Map<String, Aggregate> aggregations, Map<String, List<Suggestion<EntityAsMap>>> suggestES,
SearchDocumentResponse.EntityCreator<T> entityCreator, JsonpMapper jsonpMapper) {

Assert.notNull(hitsMetadata, "hitsMetadata must not be null");
Expand Down Expand Up @@ -116,10 +125,116 @@ public static <T> SearchDocumentResponse from(HitsMetadata<?> hitsMetadata, @Nul
ElasticsearchAggregations aggregationsContainer = aggregations != null ? new ElasticsearchAggregations(aggregations)
: null;

// todo #2154
Suggest suggest = null;
Suggest suggest = suggestFrom(suggestES, entityCreator);

return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments,
aggregationsContainer, suggest);
}

@Nullable
private static <T> Suggest suggestFrom(Map<String, List<Suggestion<EntityAsMap>>> suggestES,
SearchDocumentResponse.EntityCreator<T> entityCreator) {

if (CollectionUtils.isEmpty(suggestES)) {
return null;
}

List<Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>>> suggestions = new ArrayList<>();

suggestES.forEach((name, suggestionsES) -> {

if (!suggestionsES.isEmpty()) {
// take the type from the first entry
switch (suggestionsES.get(0)._kind()) {
case Term: {
suggestions.add(getTermSuggestion(name, suggestionsES));
break;
}
case Phrase: {
suggestions.add(getPhraseSuggestion(name, suggestionsES));
break;
}
case Completion: {
suggestions.add(getCompletionSuggestion(name, suggestionsES, entityCreator));
break;
}
default:
break;
}
}
});

// todo: hasScoreDocs checks if any one
boolean hasScoreDocs = false;

return new Suggest(suggestions, hasScoreDocs);
}

private static TermSuggestion getTermSuggestion(String name, List<Suggestion<EntityAsMap>> suggestionsES) {

List<TermSuggestion.Entry> entries = new ArrayList<>();
suggestionsES.forEach(suggestionES -> {
TermSuggest termSuggest = suggestionES.term();

TermSuggestOption optionES = termSuggest.options();
List<TermSuggestion.Entry.Option> options = new ArrayList<>();
options.add(new TermSuggestion.Entry.Option(optionES.text(), null, optionES.score(), null,
Math.toIntExact(optionES.freq())));
entries.add(new TermSuggestion.Entry(termSuggest.text(), termSuggest.offset(), termSuggest.length(), options));
});
return new TermSuggestion(name, suggestionsES.size(), entries, null);
}

private static PhraseSuggestion getPhraseSuggestion(String name, List<Suggestion<EntityAsMap>> suggestionsES) {

List<PhraseSuggestion.Entry> entries = new ArrayList<>();
suggestionsES.forEach(suggestionES -> {
PhraseSuggest phraseSuggest = suggestionES.phrase();
PhraseSuggestOption optionES = phraseSuggest.options();
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
options.add(new PhraseSuggestion.Entry.Option(optionES.text(), optionES.highlighted(), null, null));
entries.add(new PhraseSuggestion.Entry(phraseSuggest.text(), phraseSuggest.offset(), phraseSuggest.length(),
options, null));
});
return new PhraseSuggestion(name, suggestionsES.size(), entries);
}

private static <T> CompletionSuggestion<T> getCompletionSuggestion(String name,
List<Suggestion<EntityAsMap>> suggestionsES, SearchDocumentResponse.EntityCreator<T> entityCreator) {
List<CompletionSuggestion.Entry<T>> entries = new ArrayList<>();
suggestionsES.forEach(suggestionES -> {
CompletionSuggest<EntityAsMap> completionSuggest = suggestionES.completion();
List<CompletionSuggestion.Entry.Option<T>> options = new ArrayList<>();
List<CompletionSuggestOption<EntityAsMap>> optionsES = completionSuggest.options();
optionsES.forEach(optionES -> {
SearchDocument searchDocument = (optionES.source() != null) ? DocumentAdapters.from(optionES) : null;
T hitEntity = null;

if (searchDocument != null) {
try {
hitEntity = entityCreator.apply(searchDocument).get();
} catch (Exception e) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Error creating entity from SearchDocument: " + e.getMessage());
}
}
}

Map<String, Set<String>> contexts = new HashMap<>();
optionES.contexts().forEach((key, contextList) -> contexts.put(key,
contextList.stream().map(context -> context._get().toString()).collect(Collectors.toSet())));

// response from the new client does not have a doc and shardindex as the ScoreDoc from the old client responses

options.add(new CompletionSuggestion.Entry.Option<>(optionES.text(), null, optionES.score(),
optionES.collateMatch() != null ? optionES.collateMatch() : false, contexts,
new ScoreDoc(optionES.score() != null ? optionES.score() : Double.NaN, null, null), searchDocument,
hitEntity));
});

entries.add(new CompletionSuggestion.Entry<>(completionSuggest.text(), completionSuggest.offset(),
completionSuggest.length(), options));
});
return new CompletionSuggestion<>(name, suggestionsES.size(), entries);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -745,9 +745,9 @@ public T doWith(@Nullable Document document) {

IndexedObjectInformation indexedObjectInformation = IndexedObjectInformation.of( //
documentAfterLoad.hasId() ? documentAfterLoad.getId() : null, //
documentAfterLoad.getSeqNo(), //
documentAfterLoad.getPrimaryTerm(), //
documentAfterLoad.getVersion()); //
documentAfterLoad.hasSeqNo() ? documentAfterLoad.getSeqNo() : null, //
documentAfterLoad.hasPrimaryTerm() ? documentAfterLoad.getPrimaryTerm() : null, //
documentAfterLoad.hasVersion() ? documentAfterLoad.getVersion() : null); //
entity = updateIndexedObject(entity, indexedObjectInformation);

return maybeCallbackAfterConvert(entity, documentAfterLoad, index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -549,9 +549,9 @@ public Mono<T> toEntity(@Nullable Document document) {

IndexedObjectInformation indexedObjectInformation = IndexedObjectInformation.of( //
documentAfterLoad.hasId() ? documentAfterLoad.getId() : null, //
documentAfterLoad.getSeqNo(), //
documentAfterLoad.getPrimaryTerm(), //
documentAfterLoad.getVersion()); //
documentAfterLoad.hasSeqNo() ? documentAfterLoad.getSeqNo() : null, //
documentAfterLoad.hasPrimaryTerm() ? documentAfterLoad.getPrimaryTerm() : null, //
documentAfterLoad.hasVersion() ? documentAfterLoad.getVersion() : null); //
entity = updateIndexedObject(entity, indexedObjectInformation);

return maybeCallAfterConvert(entity, documentAfterLoad, index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ private static <T> Suggest suggestFrom(@Nullable org.elasticsearch.search.sugges
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
for (org.elasticsearch.search.suggest.phrase.PhraseSuggestion.Entry.Option optionES : entryES) {
options.add(new PhraseSuggestion.Entry.Option(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch()));
textToString(optionES.getHighlighted()), (double) optionES.getScore(), optionES.collateMatch()));
}

entries.add(new PhraseSuggestion.Entry(textToString(entryES.getText()), entryES.getOffset(),
Expand Down Expand Up @@ -188,7 +188,7 @@ private static <T> Suggest suggestFrom(@Nullable org.elasticsearch.search.sugges
}

options.add(new CompletionSuggestion.Entry.Option<>(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch(),
textToString(optionES.getHighlighted()), (double) optionES.getScore(), optionES.collateMatch(),
optionES.getContexts(), scoreDocFrom(optionES.getDoc()), searchDocument, hitEntity));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static class Option<T> extends Suggest.Suggestion.Entry.Option {
@Nullable private final T hitEntity;
@Nullable private SearchHit<T> searchHit;

public Option(String text, String highlighted, float score, Boolean collateMatch,
public Option(String text, @Nullable String highlighted, @Nullable Double score, Boolean collateMatch,
Map<String, Set<String>> contexts, ScoreDoc scoreDoc, @Nullable SearchDocument searchDocument,
@Nullable T hitEntity) {
super(text, highlighted, score, collateMatch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.springframework.data.elasticsearch.core.suggest.response;

import org.springframework.lang.Nullable;

import java.util.List;

/**
Expand All @@ -29,20 +31,21 @@ public PhraseSuggestion(String name, int size, List<Entry> entries) {

public static class Entry extends Suggest.Suggestion.Entry<Entry.Option> {

private final double cutoffScore;
@Nullable private final Double cutoffScore;

public Entry(String text, int offset, int length, List<Option> options, double cutoffScore) {
public Entry(String text, int offset, int length, List<Option> options, @Nullable Double cutoffScore) {
super(text, offset, length, options);
this.cutoffScore = cutoffScore;
}

public double getCutoffScore() {
@Nullable
public Double getCutoffScore() {
return cutoffScore;
}

public static class Option extends Suggest.Suggestion.Entry.Option {

public Option(String text, String highlighted, float score, Boolean collateMatch) {
public Option(String text, String highlighted, @Nullable Double score, @Nullable Boolean collateMatch) {
super(text, highlighted, score, collateMatch);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,12 @@ public List<O> getOptions() {

public abstract static class Option {
private final String text;
private final String highlighted;
private final float score;
@Nullable private final String highlighted;
@Nullable private final Double score;
@Nullable private final Boolean collateMatch;

public Option(String text, String highlighted, float score, @Nullable Boolean collateMatch) {
public Option(String text, @Nullable String highlighted, @Nullable Double score,
@Nullable Boolean collateMatch) {
this.text = text;
this.highlighted = highlighted;
this.score = score;
Expand All @@ -121,11 +122,13 @@ public String getText() {
return text;
}

@Nullable
public String getHighlighted() {
return highlighted;
}

public float getScore() {
@Nullable
public Double getScore() {
return score;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,23 @@
*/
package org.springframework.data.elasticsearch.core.suggest.response;

import org.springframework.lang.Nullable;

import java.util.List;

/**
* @author Peter-Josef Meisch
*/
public class TermSuggestion extends Suggest.Suggestion<TermSuggestion.Entry> {

private final SortBy sort;
@Nullable private final SortBy sort;

public TermSuggestion(String name, int size, List<Entry> entries, SortBy sort) {
public TermSuggestion(String name, int size, List<Entry> entries, @Nullable SortBy sort) {
super(name, size, entries);
this.sort = sort;
}

@Nullable
public SortBy getSort() {
return sort;
}
Expand All @@ -43,7 +46,7 @@ public static class Option extends Suggest.Suggestion.Entry.Option {

private final int freq;

public Option(String text, String highlighted, float score, Boolean collateMatch, int freq) {
public Option(String text, @Nullable String highlighted, double score, @Nullable Boolean collateMatch, int freq) {
super(text, highlighted, score, collateMatch);
this.freq = freq;
}
Expand Down
Loading