DATAES-593 - Add support for field collapse function.

Original PR: #290
This commit is contained in:
Martin Choraine 2019-06-24 20:18:38 +02:00 committed by Peter-Josef Meisch
parent f1aa75a94f
commit 0d1c57a874
8 changed files with 105 additions and 4 deletions

View File

@ -137,6 +137,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Lorenzo Spinelli
* @author Dmitriy Yakovlev
* @author Roman Puchkovskiy
* @author Martin Choraine
* @author Farid Azaza
*/
public class ElasticsearchRestTemplate
@ -1101,6 +1102,10 @@ public class ElasticsearchRestTemplate
}
}
if (searchQuery.getCollapseBuilder() != null) {
searchRequest.source().collapse(searchQuery.getCollapseBuilder());
}
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
HighlightBuilder highlightBuilder = searchQuery.getHighlightBuilder();
if (highlightBuilder == null) {

View File

@ -120,6 +120,7 @@ import org.springframework.util.StringUtils;
* @author Christoph Strobl
* @author Dmitriy Yakovlev
* @author Peter-Josef Meisch
* @author Martin Choraine
* @author Farid Azaza
*/
public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<Client>, ApplicationContextAware {
@ -922,6 +923,10 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
}
}
if (searchQuery.getCollapseBuilder() != null) {
searchRequest.setCollapse(searchQuery.getCollapseBuilder());
}
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
HighlightBuilder highlightBuilder = searchQuery.getHighlightBuilder();
if (highlightBuilder == null) {
@ -952,6 +957,7 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, EsClient<
searchRequest.addAggregation(aggregatedFacet.getFacet());
}
}
return searchRequest.setQuery(searchQuery.getQuery());
}

View File

@ -76,6 +76,7 @@ import org.springframework.util.Assert;
* @author Christoph Strobl
* @author Mark Paluch
* @author Farid Azaza
* @author Martin Choraine
* @since 3.2
*/
public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOperations {
@ -263,6 +264,10 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
searchSourceBuilder.fetchSource(query.getSourceFilter().getIncludes(), query.getSourceFilter().getExcludes());
}
if (query instanceof NativeSearchQuery && ((NativeSearchQuery) query).getCollapseBuilder() != null) {
searchSourceBuilder.collapse(((NativeSearchQuery) query).getCollapseBuilder());
}
sort(query, entity).forEach(searchSourceBuilder::sort);
if (query.getMinScore() > 0) {

View File

@ -20,6 +20,7 @@ import java.util.List;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
@ -33,20 +34,21 @@ import java.util.Arrays;
* @author Mohsin Husen
* @author Artur Konczak
* @author Jean-Baptiste Nizet
* @author Martin Choraine
*/
public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
private QueryBuilder query;
private QueryBuilder filter;
private List<SortBuilder> sorts;
private final List<ScriptField> scriptFields = new ArrayList<>();
private final List<ScriptField> scriptFields = new ArrayList<>();
private CollapseBuilder collapseBuilder;
private List<FacetRequest> facets;
private List<AbstractAggregationBuilder> aggregations;
private HighlightBuilder highlightBuilder;
private HighlightBuilder.Field[] highlightFields;
private List<IndexBoost> indicesBoost;
public NativeSearchQuery(QueryBuilder query) {
this.query = query;
}
@ -111,6 +113,15 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
scriptFields.addAll(Arrays.asList(scriptField));
}
@Override
public CollapseBuilder getCollapseBuilder() {
return collapseBuilder;
}
public void setCollapseBuilder(CollapseBuilder collapseBuilder) {
this.collapseBuilder = collapseBuilder;
}
public void addFacet(FacetRequest facetRequest) {
if (facets == null) {
facets = new ArrayList<>();
@ -132,7 +143,6 @@ public class NativeSearchQuery extends AbstractQuery implements SearchQuery {
return aggregations;
}
public void addAggregation(AbstractAggregationBuilder aggregationBuilder) {
if (aggregations == null) {
aggregations = new ArrayList<>();

View File

@ -25,6 +25,7 @@ import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.domain.Pageable;
@ -40,6 +41,7 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
* @author Alen Turkovic
* @author Sascha Woo
* @author Jean-Baptiste Nizet
* @author Martin Choraine
* @author Farid Azaza
*/
public class NativeSearchQueryBuilder {
@ -57,6 +59,7 @@ public class NativeSearchQueryBuilder {
private String[] types;
private String[] fields;
private SourceFilter sourceFilter;
private CollapseBuilder collapseBuilder;
private List<IndexBoost> indicesBoost;
private float minScore;
private boolean trackScores;
@ -86,6 +89,11 @@ public class NativeSearchQueryBuilder {
return this;
}
public NativeSearchQueryBuilder withCollapse(String collapseField) {
this.collapseBuilder = new CollapseBuilder(collapseField);
return this;
}
public NativeSearchQueryBuilder addAggregation(AbstractAggregationBuilder aggregationBuilder) {
this.aggregationBuilders.add(aggregationBuilder);
return this;
@ -207,6 +215,10 @@ public class NativeSearchQueryBuilder {
nativeSearchQuery.setScriptFields(scriptFields);
}
if (collapseBuilder != null) {
nativeSearchQuery.setCollapseBuilder(collapseBuilder);
}
if (!isEmpty(facetRequests)) {
nativeSearchQuery.setFacets(facetRequests);
}

View File

@ -19,6 +19,7 @@ import java.util.List;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.springframework.data.elasticsearch.core.facet.FacetRequest;
@ -30,6 +31,7 @@ import org.springframework.data.elasticsearch.core.facet.FacetRequest;
* @author Mohsin Husen
* @author Artur Konczak
* @author Jean-Baptiste Nizet
* @author Martin Choraine
*/
public interface SearchQuery extends Query {
@ -50,6 +52,8 @@ public interface SearchQuery extends Query {
List<IndexBoost> getIndicesBoost();
List<ScriptField> getScriptFields();
List<ScriptField> getScriptFields();
CollapseBuilder getCollapseBuilder();
}

View File

@ -105,6 +105,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @author Ivan Greene
* @author Dmitriy Yakovlev
* @author Peter-Josef Meisch
* @author Martin Choraine
* @author Farid Azaza
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ -2825,6 +2826,36 @@ public class ElasticsearchTemplateTests {
assertThat(sampleEntities.get(2).getMessage()).isEqualTo(sampleEntity1.getMessage());
}
@Test // DATAES-593
public void shouldReturnDocumentWithCollapsedField() {
// given
SampleEntity sampleEntity = SampleEntity.builder().id(randomNumeric(5)).message("message 1").rate(1)
.version(System.currentTimeMillis()).build();
SampleEntity sampleEntity2 = SampleEntity.builder().id(randomNumeric(5)).message("message 2").rate(2)
.version(System.currentTimeMillis()).build();
SampleEntity sampleEntit3 = SampleEntity.builder().id(randomNumeric(5)).message("message 1").rate(1)
.version(System.currentTimeMillis()).build();
List<IndexQuery> indexQueries = getIndexQueries(Arrays.asList(sampleEntity, sampleEntity2, sampleEntit3));
elasticsearchTemplate.bulkIndex(indexQueries);
elasticsearchTemplate.refresh(SampleEntity.class);
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
.withIndices(INDEX_NAME_SAMPLE_ENTITY).withTypes(TYPE_NAME).withCollapse("rate").build();
// when
Page<SampleEntity> page = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class);
// then
assertThat(page).isNotNull();
assertThat(page.getTotalElements()).isEqualTo(3);
assertThat(page.getContent()).hasSize(2);
assertThat(page.getContent().get(0).getMessage()).isEqualTo("message 1");
assertThat(page.getContent().get(1).getMessage()).isEqualTo("message 2");
}
private IndexQuery getIndexQuery(SampleEntity sampleEntity) {
return new IndexQueryBuilder().withId(sampleEntity.getId()).withObject(sampleEntity)
.withVersion(sampleEntity.getVersion()).build();

View File

@ -24,6 +24,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@ -76,6 +78,7 @@ import org.springframework.util.StringUtils;
* @author Mark Paluch
* @author Peter-Josef Meisch
* @author Farid Azaza
* @author Martin Choraine
* @currentRead Golden Fool - Robin Hobb
*/
@RunWith(SpringRunner.class)
@ -666,6 +669,30 @@ public class ReactiveElasticsearchTemplateTests {
.verifyComplete();
}
@Test // DATAES-593
public void shouldReturnDocumentWithCollapsedField() {
SampleEntity entity1 = randomEntity("test message");
entity1.setRate(1);
SampleEntity entity2 = randomEntity("test another message");
entity2.setRate(2);
SampleEntity entity3 = randomEntity("test message again");
entity3.setRate(1);
index(entity1, entity2, entity3);
SearchQuery query = new NativeSearchQueryBuilder()
.withIndices(DEFAULT_INDEX)
.withQuery(matchAllQuery())
.withCollapse("rate")
.withPageable(PageRequest.of(0, 25))
.build();
template.find(query, SampleEntity.class)
.as(StepVerifier::create)
.expectNextCount(2)
.verifyComplete();
}
@Data
@Document(indexName = "marvel", type = "characters")
static class Person {
@ -733,6 +760,7 @@ public class ReactiveElasticsearchTemplateTests {
@Id private String id;
@Field(type = Text, store = true, fielddata = true) private String message;
private int rate;
@Version private Long version;
@Score private float score;
}