Skip to content

Commit 6805fff

Browse files
authored
Option to add docvalue_fields to a search query.
Original Pull Request #2446 #Closes 2316
1 parent 0971acf commit 6805fff

File tree

6 files changed

+181
-13
lines changed

6 files changed

+181
-13
lines changed

src/main/java/org/springframework/data/elasticsearch/client/elc/RequestConverter.java

+15-9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import co.elastic.clients.elasticsearch._types.mapping.RuntimeField;
3232
import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
3333
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
34+
import co.elastic.clients.elasticsearch._types.query_dsl.FieldAndFormat;
3435
import co.elastic.clients.elasticsearch._types.query_dsl.Like;
3536
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
3637
import co.elastic.clients.elasticsearch.core.*;
@@ -1298,6 +1299,12 @@ private <T> void prepareSearchRequest(Query query, @Nullable Class<T> clazz, Ind
12981299
// noinspection unchecked
12991300
builder.indicesBoost(boosts);
13001301
}
1302+
1303+
if (!isEmpty(query.getDocValueFields())) {
1304+
builder.docvalueFields(query.getDocValueFields().stream() //
1305+
.map(docValueField -> FieldAndFormat.of(b -> b.field(docValueField.field()).format(docValueField.format())))
1306+
.toList());
1307+
}
13011308
}
13021309

13031310
private Rescore getRescore(RescorerQuery rescorerQuery) {
@@ -1554,14 +1561,13 @@ public SearchTemplateRequest searchTemplate(SearchTemplateQuery query, IndexCoor
15541561

15551562
return SearchTemplateRequest.of(builder -> {
15561563
builder //
1557-
.allowNoIndices(query.getAllowNoIndices()) //
1558-
.explain(query.getExplain()) //
1559-
.id(query.getId()) //
1560-
.index(Arrays.asList(index.getIndexNames())) //
1561-
.preference(query.getPreference()) //
1562-
.routing(query.getRoute()) //
1563-
.searchType(searchType(query.getSearchType()))
1564-
.source(query.getSource()) //
1564+
.allowNoIndices(query.getAllowNoIndices()) //
1565+
.explain(query.getExplain()) //
1566+
.id(query.getId()) //
1567+
.index(Arrays.asList(index.getIndexNames())) //
1568+
.preference(query.getPreference()) //
1569+
.routing(query.getRoute()) //
1570+
.searchType(searchType(query.getSearchType())).source(query.getSource()) //
15651571
;
15661572

15671573
var expandWildcards = query.getExpandWildcards();
@@ -1577,7 +1583,7 @@ public SearchTemplateRequest searchTemplate(SearchTemplateQuery query, IndexCoor
15771583
Function<Map.Entry<String, Object>, String> keyMapper = Map.Entry::getKey;
15781584
Function<Map.Entry<String, Object>, JsonData> valueMapper = entry -> JsonData.of(entry.getValue(), jsonpMapper);
15791585
Map<String, JsonData> params = query.getParams().entrySet().stream()
1580-
.collect(Collectors.toMap(keyMapper, valueMapper));
1586+
.collect(Collectors.toMap(keyMapper, valueMapper));
15811587
builder.params(params);
15821588
}
15831589

src/main/java/org/springframework/data/elasticsearch/core/query/BaseQuery.java

+21-2
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ public class BaseQuery implements Query {
7878
@Nullable protected PointInTime pointInTime;
7979
private boolean queryIsUpdatedByConverter = false;
8080
@Nullable private Integer reactiveBatchSize = null;
81-
@Nullable private Boolean allowNoIndices = null;
82-
81+
@Nullable private Boolean allowNoIndices = null;
8382
private EnumSet<IndicesOptions.WildcardStates> expandWildcards;
83+
private List<DocValueField> docValueFields = new ArrayList<>();
8484

8585
public BaseQuery() {}
8686

@@ -114,6 +114,7 @@ public <Q extends BaseQuery, B extends BaseQueryBuilder<Q, B>> BaseQuery(BaseQue
114114
this.reactiveBatchSize = builder.getReactiveBatchSize();
115115
this.allowNoIndices = builder.getAllowNoIndices();
116116
this.expandWildcards = builder.getExpandWildcards();
117+
this.docValueFields = builder.getDocValueFields();
117118
}
118119

119120
/**
@@ -524,4 +525,22 @@ public Boolean getAllowNoIndices() {
524525
public EnumSet<IndicesOptions.WildcardStates> getExpandWildcards() {
525526
return expandWildcards;
526527
}
528+
529+
/**
530+
* @since 5.1
531+
*/
532+
@Override
533+
public List<DocValueField> getDocValueFields() {
534+
return docValueFields;
535+
}
536+
537+
/**
538+
* @since 5.1
539+
*/
540+
public void setDocValueFields(List<DocValueField> docValueFields) {
541+
542+
Assert.notNull(docValueFields, "getDocValueFields must not be null");
543+
544+
this.docValueFields = docValueFields;
545+
}
527546
}

src/main/java/org/springframework/data/elasticsearch/core/query/BaseQueryBuilder.java

+20
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
7070
private EnumSet<IndicesOptions.WildcardStates> expandWildcards = EnumSet.noneOf(IndicesOptions.WildcardStates.class);
7171

7272
@Nullable Integer reactiveBatchSize;
73+
private final List<DocValueField> docValueFields = new ArrayList<>();
7374

7475
@Nullable
7576
public Sort getSort() {
@@ -218,6 +219,13 @@ public EnumSet<IndicesOptions.WildcardStates> getExpandWildcards() {
218219
return expandWildcards;
219220
}
220221

222+
/**
223+
* @since 5.1
224+
*/
225+
public List<DocValueField> getDocValueFields() {
226+
return docValueFields;
227+
}
228+
221229
public SELF withPageable(Pageable pageable) {
222230
this.pageable = pageable;
223231
return self();
@@ -423,6 +431,18 @@ public SELF withExpandWildcards(EnumSet<IndicesOptions.WildcardStates> expandWil
423431
return self();
424432
}
425433

434+
/**
435+
* @since 5.1
436+
*/
437+
public SELF withDocValueFields(List<DocValueField> docValueFields) {
438+
439+
Assert.notNull(docValueFields, "docValueFields must not be null");
440+
441+
this.docValueFields.clear();
442+
this.docValueFields.addAll(docValueFields);
443+
return self();
444+
}
445+
426446
public abstract Q build();
427447

428448
private SELF self() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core.query;
17+
18+
import org.springframework.lang.Nullable;
19+
import org.springframework.util.Assert;
20+
21+
/**
22+
* Record defining a docvalue_field to be used in a query.
23+
*
24+
* @author Peter-Josef Meisch
25+
* @since 5.1
26+
*/
27+
public record DocValueField(String field, @Nullable String format) {
28+
public DocValueField {
29+
Assert.notNull(field, "field must not be null");
30+
}
31+
32+
public DocValueField(String field) {
33+
this(field, null);
34+
}
35+
}

src/main/java/org/springframework/data/elasticsearch/core/query/Query.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -472,8 +472,14 @@ default Integer getReactiveBatchSize() {
472472
EnumSet<IndicesOptions.WildcardStates> getExpandWildcards();
473473

474474
/**
475-
* @since 4.3
476-
*/
475+
* @return a possible empty list of docvalue_field values to be set on the query.
476+
* @since 5.1
477+
*/
478+
List<DocValueField> getDocValueFields();
479+
480+
/**
481+
* @since 4.3
482+
*/
477483
enum SearchType {
478484
QUERY_THEN_FETCH, DFS_QUERY_THEN_FETCH
479485
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.client.elc;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
21+
22+
import org.junit.jupiter.api.DisplayName;
23+
import org.junit.jupiter.api.Test;
24+
import org.springframework.data.annotation.Id;
25+
import org.springframework.data.elasticsearch.annotations.Document;
26+
import org.springframework.data.elasticsearch.annotations.Field;
27+
import org.springframework.data.elasticsearch.annotations.FieldType;
28+
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
29+
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
30+
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
31+
import org.springframework.data.elasticsearch.core.query.DocValueField;
32+
import org.springframework.data.elasticsearch.core.query.Query;
33+
import org.springframework.data.elasticsearch.core.query.StringQuery;
34+
import org.springframework.lang.Nullable;
35+
36+
import java.util.List;
37+
38+
/**
39+
* @author Peter-Josef Meisch
40+
*/
41+
class RequestConverterTest {
42+
43+
private static final SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
44+
private static final MappingElasticsearchConverter converter = new MappingElasticsearchConverter(mappingContext);
45+
private JacksonJsonpMapper jsonpMapper = new JacksonJsonpMapper();
46+
private RequestConverter requestConverter = new RequestConverter(converter, jsonpMapper);
47+
48+
@Test // #2316
49+
@DisplayName("should add docvalue_fields")
50+
void shouldAddDocvalueFields() {
51+
52+
var docValueFields = List.of( //
53+
new DocValueField("field1"), //
54+
new DocValueField("field2", "format2") //
55+
);
56+
// doesn't matter what type of query is used, the relevant part for docvalue_fields is in the base builder.
57+
var query = StringQuery.builder("""
58+
{
59+
"match_all":{}
60+
}
61+
""") //
62+
.withDocValueFields(docValueFields) //
63+
.build();
64+
65+
var searchRequest = requestConverter.searchRequest(query, SampleEntity.class, IndexCoordinates.of("foo"), true);
66+
67+
var fieldAndFormats = searchRequest.docvalueFields();
68+
assertThat(fieldAndFormats).hasSize(2);
69+
assertThat(fieldAndFormats.get(0).field()).isEqualTo("field1");
70+
assertThat(fieldAndFormats.get(0).format()).isNull();
71+
assertThat(fieldAndFormats.get(1).field()).isEqualTo("field2");
72+
assertThat(fieldAndFormats.get(1).format()).isEqualTo("format2");
73+
}
74+
75+
@Document(indexName = "does-not-matter")
76+
static class SampleEntity {
77+
@Nullable
78+
@Id private String id;
79+
@Nullable
80+
@Field(type = FieldType.Text) private String text;
81+
}
82+
}

0 commit comments

Comments
 (0)