Skip to content
This repository has been archived by the owner on Jun 1, 2021. It is now read-only.

Commit

Permalink
Merge pull request #206 from KWatzal/#205-sorted-suggestions
Browse files Browse the repository at this point in the history
#205 sorted suggestions
  • Loading branch information
tkurz authored Apr 1, 2021
2 parents 14928a4 + 2d1eeac commit def17ee
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 4 deletions.
57 changes: 57 additions & 0 deletions api/src/main/java/com/rbmhtechnology/vind/api/query/sort/Sort.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ public static ScoredDate scoredDate(SingleValueFieldDescriptor descriptor) {
return new ScoredDate(descriptor);
}

/**
* Static method to instantiate a {@link Sort.SpecialSort.NumberOfMatchingTermsSort} sorting object.
* @param descriptor {@link SingleValueFieldDescriptor} indicating the field to perform the sorting on.
* @return {@link Sort.SpecialSort.NumberOfMatchingTermsSort} sort query object.
*/
public static NumberOfMatchingTermsSort numberOfMatchingTermsSort(FieldDescriptor descriptor) {
return new NumberOfMatchingTermsSort(descriptor);
}

/**
* Static method to instantiate a {@link com.rbmhtechnology.vind.api.query.distance.Distance} object.
* Be sure that geoDistance is set in search!
Expand Down Expand Up @@ -207,6 +216,54 @@ public Sort clone() {
}
}

public static class NumberOfMatchingTermsSort extends SpecialSort {

private FieldDescriptor descriptor;

/**
* Creates a new instance of {@link Sort.SpecialSort.NumberOfMatchingTermsSort} for a given field.
* @param descriptor {@link SingleValueFieldDescriptor} indicating the field to perform the sorting on.
*/
protected NumberOfMatchingTermsSort(FieldDescriptor descriptor) {
this.descriptor = descriptor;
}

/**
* Gets the {@link FieldDescriptor}.
* @return {@link FieldDescriptor}
*/
public FieldDescriptor getDescriptor() {
return descriptor;
}

/**
* Gets the {@link String} name of the field descriptor.
* @return {@link String} name of the field descriptor.
*/
public String getField() {return descriptor.getName();}

@Override
public void setDirection(Direction direction) {
this.direction = direction;
}

@Override
public String toString(){
final String scoreString = "{" +
"\"direction\":\"%s\"," +
"\"field\":\"%s\"" +
"}";
return String.format(scoreString,this.direction,this.descriptor.getName());
}

@Override
public Sort clone() {
final NumberOfMatchingTermsSort copy = new NumberOfMatchingTermsSort(this.descriptor);
copy.direction = this.direction;
return copy;
}
}

public static class DistanceSort extends SpecialSort {

public DistanceSort() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.rbmhtechnology.vind.api.query.suggestion;

import com.rbmhtechnology.vind.api.query.filter.Filter;
import com.rbmhtechnology.vind.api.query.sort.Sort;
import com.rbmhtechnology.vind.model.FieldDescriptor;

import java.util.Arrays;
Expand All @@ -21,6 +22,8 @@ public class DescriptorSuggestionSearch implements ExecutableSuggestionSearch {
private Filter filter = null;
private Set<FieldDescriptor> suggestionFields = new HashSet<>();
private String searchContext = null;
private Sort sort = null;

/**
* Creates a new instance of {@link DescriptorSuggestionSearch}.
* @param input String text to find suggestion for.
Expand Down Expand Up @@ -162,4 +165,13 @@ public boolean hasFilter() {
public boolean isStringSuggestion() {
return false;
}

public Sort getSort() {
return sort;
}

public DescriptorSuggestionSearch setSort(Sort sort) {
this.sort = sort;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.rbmhtechnology.vind.api.query.suggestion;

import com.rbmhtechnology.vind.api.query.filter.Filter;
import com.rbmhtechnology.vind.api.query.sort.Sort;

/**
* @author Thomas Kurz (tkurz@apache.org)
Expand All @@ -15,6 +16,8 @@ public interface ExecutableSuggestionSearch {

public String getInput();

public Sort getSort();

public ExecutableSuggestionSearch text(String text);

public ExecutableSuggestionSearch filter(Filter filter);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.rbmhtechnology.vind.api.query.suggestion;

import com.rbmhtechnology.vind.api.query.filter.Filter;
import com.rbmhtechnology.vind.api.query.sort.Sort;

import java.util.Arrays;
import java.util.HashSet;
Expand All @@ -20,6 +21,7 @@ public class StringSuggestionSearch implements ExecutableSuggestionSearch {
private Filter filter = null;
private Set<String> suggestionFields = new HashSet<>();
private String searchContext = null;
private Sort sort = null;

/**
* Creates a new instance of {@link StringSuggestionSearch}.
Expand Down Expand Up @@ -163,4 +165,12 @@ public boolean isStringSuggestion() {
return true;
}

public Sort getSort() {
return sort;
}

public StringSuggestionSearch setSort(Sort sort) {
this.sort = sort;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.rbmhtechnology.vind.api.query.suggestion;

import com.rbmhtechnology.vind.api.query.filter.Filter;
import com.rbmhtechnology.vind.api.query.sort.Sort;
import com.rbmhtechnology.vind.model.FieldDescriptor;

import java.util.HashSet;
Expand All @@ -19,6 +20,7 @@ public class SuggestionSearch {
private Set<FieldDescriptor> suggestionFields = new HashSet<>();
private Set<String> suggestionStringFields = new HashSet<>();
private String searchContext = null;
private Sort sort = null;

/**
* Creates a new instance of {@link SuggestionSearch}.
Expand All @@ -42,6 +44,7 @@ public SuggestionSearch copy() {
copy.suggestionFields = this.suggestionFields;
copy.suggestionStringFields = this.suggestionStringFields;
copy.searchContext = this.searchContext;
copy.sort = this.sort;
return copy;
}

Expand Down Expand Up @@ -177,4 +180,12 @@ public String getSearchContext() {
return this.searchContext;
}

public Sort getSort() {
return sort;
}

public SuggestionSearch setSort(final Sort sort) {
this.sort = sort;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import java.util.stream.Stream;

import static com.rbmhtechnology.vind.elasticsearch.backend.util.CursorUtils.fromSearchAfterCursor;
import static com.rbmhtechnology.vind.elasticsearch.backend.util.SortUtils.NUMBER_OF_MATCHING_TERMS_SORT;
import static com.rbmhtechnology.vind.model.FieldDescriptor.UseCase;

public class ElasticQueryBuilder {
Expand Down Expand Up @@ -109,6 +110,7 @@ public class ElasticQueryBuilder {
put("<","");
put(">","");
}};
private static final String RELEVANCE = "relevance";

public static SearchSourceBuilder buildQuery(FulltextSearch search, DocumentFactory factory,
List<String> indexFootPrint, ElasticVindClient client) {
Expand Down Expand Up @@ -1187,6 +1189,11 @@ public static SearchSourceBuilder buildSuggestionQuery(ExecutableSuggestionSearc
.includeExclude(
new IncludeExclude(Suggester.getSuggestionRegex(search.getInput()), null))
)
.map(aggregation ->
search.getSort() != null && NUMBER_OF_MATCHING_TERMS_SORT.equals(search.getSort().getType())
? addSubAggregation(aggregation, search, searchContext, indexFootPrint)
: aggregation
)
.forEach(searchSource::aggregation);

final SuggestBuilder suggestBuilder = new SuggestBuilder();
Expand All @@ -1201,6 +1208,15 @@ public static SearchSourceBuilder buildSuggestionQuery(ExecutableSuggestionSearc
return searchSource;
}

private static AggregationBuilder addSubAggregation(TermsAggregationBuilder aggregation,
ExecutableSuggestionSearch search,
String searchContext,
List<String> indexFootPrint) {
return aggregation
.subAggregation(SortUtils.buildSuggestionSort(RELEVANCE, search.getSort(), searchContext, indexFootPrint, search.getInput()))
.order(BucketOrder.aggregation(RELEVANCE, false));
}

public static List<String> getSpellCheckedQuery(String q, SearchResponse response) {
final Suggest suggestions = response.getSuggest();
if(suggestions!= null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,23 @@
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
import org.elasticsearch.search.sort.ScriptSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;

import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class SortUtils {
public static final String NUMBER_OF_MATCHING_TERMS_SORT = "NumberOfMatchingTermsSort";

protected static SortBuilder buildSort(Sort sort, FulltextSearch search, DocumentFactory factory,
String searchContext, List<String> indexFootPrint) {
switch (sort.getType()) {
Expand Down Expand Up @@ -147,4 +151,48 @@ protected static AggregationBuilder buildFacetSort(String name, Sort sort, Strin
sort.getType()));
}
}

protected static AggregationBuilder buildSuggestionSort(String name,
Sort sort,
String searchContext,
List<String> indexFootPrint,
String input) {
switch (sort.getType()) {
case NUMBER_OF_MATCHING_TERMS_SORT:
return setNumberOfMatchingTermsSort(
name,
(Sort.SpecialSort.NumberOfMatchingTermsSort) sort,
searchContext,
indexFootPrint,
input);
default:
throw new SearchServerException(String
.format("Unable to parse Vind sort '%s' to ElasticSearch sorting: sort type not supported.",
sort.getType()));
}
}

private static MaxAggregationBuilder setNumberOfMatchingTermsSort(String name,
Sort.SpecialSort.NumberOfMatchingTermsSort sort,
String searchContext,
List<String> indexFootPrint,
String input) {
final FieldDescriptor descriptor = sort.getDescriptor();
final String matchingField = Optional.ofNullable(descriptor)
.filter(FieldDescriptor::isSort)
.map(field -> FieldUtil.getFieldName(descriptor, FieldDescriptor.UseCase.Sort, searchContext, indexFootPrint)
.orElseThrow(() ->
new SearchServerException("The field '" + descriptor.getName() + "' is not set for context ["+ searchContext +"]")))
.orElse(sort.getField());
final Map<String, Object> parameters = new HashMap<>();
parameters.put("field",matchingField);
parameters.put("input", Arrays.asList(input.split(" ")));
final Script painlessMatchingSort = new Script(
ScriptType.INLINE,
"painless",
"long sum = 0;for(String value: doc[params.field]){long subSum = 0;for(String searchTerm: params.input){subSum+=value.toLowerCase().contains(searchTerm.toLowerCase()) ? 1 : 0;}if(subSum>sum){sum=subSum;}}return sum;",
parameters
);
return AggregationBuilders.max(name).script(painlessMatchingSort);
}
}
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -580,13 +580,13 @@
<profiles>
<profile>
<id>elastic</id>
</profile>
<profile>
<id>solr</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>solr</id>
</profile>
<profile>
<id>quick</id>
<activation>
Expand Down
Loading

0 comments on commit def17ee

Please sign in to comment.