Skip to content

Commit c8af0f4

Browse files
authored
Use mappings to format doc-value fields by default. (#30831)
Doc-value fields now return a value that is based on the mappings rather than the script implementation by default. This deprecates the special `use_field_mapping` docvalue format which was added in #29639 only to ease the transition to 7.x and it is not necessary anymore in 7.0.
1 parent b63b50b commit c8af0f4

File tree

29 files changed

+105
-152
lines changed

29 files changed

+105
-152
lines changed

docs/reference/migration/migrate_7_0/search.asciidoc

+10
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ using the "all fields" mode ("default_field": "*") or other fieldname expansions
122122
Search requests with extra content after the main object will no longer be accepted
123123
by the `_search` endpoint. A parsing exception will be thrown instead.
124124

125+
[float]
126+
==== Doc-value fields default format
127+
128+
The format of doc-value fields is changing to be the same as what could be
129+
obtained in 6.x with the special `use_field_mapping` format. This is mostly a
130+
change for date fields, which are now formatted based on the format that is
131+
configured in the mappings by default. This behavior can be changed by
132+
specifying a <<search-request-docvalue-fields,`format`>> within the doc-value
133+
field.
134+
125135
[float]
126136
==== Context Completion Suggester
127137

docs/reference/search/request/docvalue-fields.asciidoc

+11-12
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ GET /_search
1212
"match_all": {}
1313
},
1414
"docvalue_fields" : [
15+
"my_ip_field", <1>
1516
{
16-
"field": "my_ip_field", <1>
17-
"format": "use_field_mapping" <2>
17+
"field": "my_keyword_field" <2>
1818
},
1919
{
2020
"field": "my_date_field",
@@ -25,10 +25,10 @@ GET /_search
2525
--------------------------------------------------
2626
// CONSOLE
2727
<1> the name of the field
28-
<2> the special `use_field_mapping` format tells Elasticsearch to use the format from the mapping
29-
<3> date fields may use a custom format
28+
<2> an object notation is supported as well
29+
<3> the object notation allows to specify a custom format
3030

31-
Doc value fields can work on fields that are not stored.
31+
Doc value fields can work on fields that have doc-values enabled, regardless of whether they are stored
3232

3333
`*` can be used as a wild card, for example:
3434

@@ -41,8 +41,8 @@ GET /_search
4141
},
4242
"docvalue_fields" : [
4343
{
44-
"field": "*field", <1>
45-
"format": "use_field_mapping" <2>
44+
"field": "*_date_field", <1>
45+
"format": "epoch_millis" <2>
4646
}
4747
]
4848
}
@@ -62,9 +62,8 @@ While most fields do not support custom formats, some of them do:
6262
- <<date,Date>> fields can take any <<mapping-date-format,date format>>.
6363
- <<number,Numeric>> fields accept a https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html[DecimalFormat pattern].
6464

65-
All fields support the special `use_field_mapping` format, which tells
66-
Elasticsearch to use the mappings to figure out a default format.
65+
By default fields are formatted based on a sensible configuration that depends
66+
on their mappings: `long`, `double` and other numeric fields are formatted as
67+
numbers, `keyword` fields are formatted as strings, `date` fields are formatted
68+
with the configured `date` format, etc.
6769

68-
NOTE: The default is currently to return the same output as
69-
<<search-request-script-fields,script fields>>. However it will change in 7.0
70-
to behave as if the `use_field_mapping` format was provided.

docs/reference/search/request/inner-hits.asciidoc

+1-4
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,7 @@ POST test/_search
246246
"inner_hits": {
247247
"_source" : false,
248248
"docvalue_fields" : [
249-
{
250-
"field": "comments.text.keyword",
251-
"format": "use_field_mapping"
252-
}
249+
"comments.text.keyword"
253250
]
254251
}
255252
}

docs/reference/sql/endpoints/translate.asciidoc

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ Which returns:
2727
"size" : 10,
2828
"docvalue_fields" : [
2929
{
30-
"field": "page_count",
31-
"format": "use_field_mapping"
30+
"field": "page_count"
3231
},
3332
{
3433
"field": "release_date",

qa/full-cluster-restart/src/test/java/org/elasticsearch/upgrades/QueryBuilderBWCIT.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public void testQueryBuilderBWC() throws Exception {
201201
QueryBuilder expectedQueryBuilder = (QueryBuilder) CANDIDATES.get(i)[1];
202202
Request request = new Request("GET", "/" + index + "/_search");
203203
request.setJsonEntity("{\"query\": {\"ids\": {\"values\": [\"" + Integer.toString(i) + "\"]}}, " +
204-
"\"docvalue_fields\": [{\"field\":\"query.query_builder_field\", \"format\":\"use_field_mapping\"}]}");
204+
"\"docvalue_fields\": [{\"field\":\"query.query_builder_field\"}]}");
205205
Response rsp = client().performRequest(request);
206206
assertEquals(200, rsp.getStatusLine().getStatusCode());
207207
Map<?, ?> hitRsp = (Map<?, ?>) ((List<?>) ((Map<?, ?>)toMap(rsp).get("hits")).get("hits")).get(0);

rest-api-spec/src/main/resources/rest-api-spec/test/search.inner_hits/10_basic.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ setup:
4646
"Nested doc version and seqIDs":
4747

4848
- skip:
49-
version: " - 6.3.99"
50-
reason: "object notation for docvalue_fields was introduced in 6.4"
49+
version: " - 6.99.99"
50+
reason: "Triggers warnings before 7.0"
5151

5252
- do:
5353
index:
@@ -62,7 +62,7 @@ setup:
6262
- do:
6363
search:
6464
rest_total_hits_as_int: true
65-
body: { "query" : { "nested" : { "path" : "nested_field", "query" : { "match_all" : {} }, "inner_hits" : { version: true, "docvalue_fields": [ { "field": "_seq_no", "format": "use_field_mapping" } ]} }}, "version": true, "docvalue_fields" : [ { "field": "_seq_no", "format": "use_field_mapping" } ] }
65+
body: { "query" : { "nested" : { "path" : "nested_field", "query" : { "match_all" : {} }, "inner_hits" : { version: true, "docvalue_fields": [ "_seq_no" ]} }}, "version": true, "docvalue_fields" : [ "_seq_no" ] }
6666

6767
- match: { hits.total: 1 }
6868
- match: { hits.hits.0._index: "test" }
@@ -86,7 +86,7 @@ setup:
8686
- do:
8787
search:
8888
rest_total_hits_as_int: true
89-
body: { "query" : { "nested" : { "path" : "nested_field", "query" : { "match_all" : {} }, "inner_hits" : { version: true, "docvalue_fields": [ { "field": "_seq_no", "format": "use_field_mapping" } ]} }}, "version": true, "docvalue_fields" : [ { "field": "_seq_no", "format": "use_field_mapping" } ] }
89+
body: { "query" : { "nested" : { "path" : "nested_field", "query" : { "match_all" : {} }, "inner_hits" : { version: true, "docvalue_fields": [ "_seq_no" ]} }}, "version": true, "docvalue_fields" : [ "_seq_no" ] }
9090

9191
- match: { hits.total: 1 }
9292
- match: { hits.hits.0._index: "test" }

rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yml

+11-17
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,9 @@ setup:
144144
---
145145
"docvalue_fields":
146146
- skip:
147-
version: " - 6.4.0"
148-
reason: format option was added in 6.4 and the deprecation message changed in 6.4.1
149-
features: warnings
147+
version: " - 6.9.99"
148+
reason: Triggers a deprecation warning before 7.0
150149
- do:
151-
warnings:
152-
- 'There are doc-value fields which are not using a format. The output will change in 7.0 when doc value fields get formatted based on mappings by default. It is recommended to pass [format=use_field_mapping] with a doc value field in order to opt in for the future behaviour and ease the migration to 7.0: [count]'
153150
search:
154151
body:
155152
docvalue_fields: [ "count" ]
@@ -158,12 +155,9 @@ setup:
158155
---
159156
"multiple docvalue_fields":
160157
- skip:
161-
version: " - 6.4.0"
162-
reason: format option was added in 6.4 and the deprecation message changed in 6.4.1
163-
features: warnings
158+
version: " - 6.9.99"
159+
reason: Triggered a deprecation warning before 7.0
164160
- do:
165-
warnings:
166-
- 'There are doc-value fields which are not using a format. The output will change in 7.0 when doc value fields get formatted based on mappings by default. It is recommended to pass [format=use_field_mapping] with a doc value field in order to opt in for the future behaviour and ease the migration to 7.0: [count, include.field1.keyword]'
167161
search:
168162
body:
169163
docvalue_fields: [ "count", "include.field1.keyword" ]
@@ -172,22 +166,22 @@ setup:
172166
---
173167
"docvalue_fields as url param":
174168
- skip:
175-
version: " - 6.4.0"
176-
reason: format option was added in 6.4 and the deprecation message changed in 6.4.1
177-
features: warnings
169+
version: " - 6.99.99"
170+
reason: Triggered a deprecation warning before 7.0
178171
- do:
179-
warnings:
180-
- 'There are doc-value fields which are not using a format. The output will change in 7.0 when doc value fields get formatted based on mappings by default. It is recommended to pass [format=use_field_mapping] with a doc value field in order to opt in for the future behaviour and ease the migration to 7.0: [count]'
181172
search:
182173
docvalue_fields: [ "count" ]
183174
- match: { hits.hits.0.fields.count: [1] }
184175

185176
---
186177
"docvalue_fields with default format":
187178
- skip:
188-
version: " - 6.3.99"
189-
reason: format option was added in 6.4
179+
version: " - 6.99.99"
180+
reason: Only triggers warnings on 7.0+
181+
features: warnings
190182
- do:
183+
warnings:
184+
- "[use_field_mapping] is a special format that was only used to ease the transition to 7.x. It has become the default and shouldn't be set explicitly anymore."
191185
search:
192186
body:
193187
docvalue_fields:

rest-api-spec/src/main/resources/rest-api-spec/test/search/30_limits.yml

+5-8
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ setup:
6767
"Docvalues_fields size limit":
6868

6969
- skip:
70-
version: " - 6.3.99"
71-
reason: "The object notation for docvalue_fields is only supported on 6.4+"
70+
version: " - 6.99.99"
71+
reason: "Triggers warnings before 7.0"
7272
- do:
7373
catch: /Trying to retrieve too many docvalue_fields\. Must be less than or equal to[:] \[2\] but was \[3\]\. This limit can be set by changing the \[index.max_docvalue_fields_search\] index level setting\./
7474
search:
@@ -78,12 +78,9 @@ setup:
7878
query:
7979
match_all: {}
8080
docvalue_fields:
81-
- field: "one"
82-
format: "use_field_mapping"
83-
- field: "two"
84-
format: "use_field_mapping"
85-
- field: "three"
86-
format: "use_field_mapping"
81+
- "one"
82+
- "two"
83+
- "three"
8784

8885
---
8986
"Script_fields size limit":

server/src/main/java/org/elasticsearch/search/fetch/subphase/DocValueFieldsContext.java

-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@
3838
*/
3939
public class DocValueFieldsContext {
4040

41-
public static final String USE_DEFAULT_FORMAT = "use_field_mapping";
42-
4341
/**
4442
* Wrapper around a field name and the format that should be used to
4543
* display values of this field.

server/src/main/java/org/elasticsearch/search/fetch/subphase/DocValueFieldsFetchSubPhase.java

+18-29
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
2929
import org.elasticsearch.index.fielddata.IndexFieldData;
3030
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
31-
import org.elasticsearch.index.fielddata.ScriptDocValues;
3231
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
3332
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
3433
import org.elasticsearch.index.mapper.MappedFieldType;
@@ -46,7 +45,6 @@
4645
import java.util.HashMap;
4746
import java.util.List;
4847
import java.util.Objects;
49-
import java.util.stream.Collectors;
5048

5149
/**
5250
* Query sub phase which pulls data from doc values
@@ -55,7 +53,8 @@
5553
*/
5654
public final class DocValueFieldsFetchSubPhase implements FetchSubPhase {
5755

58-
private static final DeprecationLogger deprecationLogger = new DeprecationLogger(
56+
private static final String USE_DEFAULT_FORMAT = "use_field_mapping";
57+
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(
5958
LogManager.getLogger(DocValueFieldsFetchSubPhase.class));
6059

6160
@Override
@@ -66,9 +65,9 @@ public void hitsExecute(SearchContext context, SearchHit[] hits) throws IOExcept
6665
String name = context.collapse().getFieldName();
6766
if (context.docValueFieldsContext() == null) {
6867
context.docValueFieldsContext(new DocValueFieldsContext(
69-
Collections.singletonList(new FieldAndFormat(name, DocValueFieldsContext.USE_DEFAULT_FORMAT))));
68+
Collections.singletonList(new FieldAndFormat(name, null))));
7069
} else if (context.docValueFieldsContext().fields().stream().map(ff -> ff.field).anyMatch(name::equals) == false) {
71-
context.docValueFieldsContext().fields().add(new FieldAndFormat(name, DocValueFieldsContext.USE_DEFAULT_FORMAT));
70+
context.docValueFieldsContext().fields().add(new FieldAndFormat(name, null));
7271
}
7372
}
7473

@@ -79,33 +78,28 @@ public void hitsExecute(SearchContext context, SearchHit[] hits) throws IOExcept
7978
hits = hits.clone(); // don't modify the incoming hits
8079
Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId));
8180

82-
List<String> noFormatFields = context.docValueFieldsContext().fields().stream().filter(f -> f.format == null).map(f -> f.field)
83-
.collect(Collectors.toList());
84-
if (noFormatFields.isEmpty() == false) {
85-
deprecationLogger.deprecated("There are doc-value fields which are not using a format. The output will "
86-
+ "change in 7.0 when doc value fields get formatted based on mappings by default. It is recommended to pass "
87-
+ "[format={}] with a doc value field in order to opt in for the future behaviour and ease the migration to "
88-
+ "7.0: {}", DocValueFieldsContext.USE_DEFAULT_FORMAT, noFormatFields);
81+
if (context.docValueFieldsContext().fields().stream()
82+
.map(f -> f.format)
83+
.filter(USE_DEFAULT_FORMAT::equals)
84+
.findAny()
85+
.isPresent()) {
86+
DEPRECATION_LOGGER.deprecated("[" + USE_DEFAULT_FORMAT + "] is a special format that was only used to " +
87+
"ease the transition to 7.x. It has become the default and shouldn't be set explicitly anymore.");
8988
}
9089

9190
for (FieldAndFormat fieldAndFormat : context.docValueFieldsContext().fields()) {
9291
String field = fieldAndFormat.field;
9392
MappedFieldType fieldType = context.mapperService().fullName(field);
9493
if (fieldType != null) {
9594
final IndexFieldData<?> indexFieldData = context.getForField(fieldType);
96-
final DocValueFormat format;
97-
if (fieldAndFormat.format == null) {
98-
format = null;
99-
} else {
100-
String formatDesc = fieldAndFormat.format;
101-
if (Objects.equals(formatDesc, DocValueFieldsContext.USE_DEFAULT_FORMAT)) {
102-
formatDesc = null;
103-
}
104-
format = fieldType.docValueFormat(formatDesc, null);
95+
String formatDesc = fieldAndFormat.format;
96+
if (Objects.equals(formatDesc, USE_DEFAULT_FORMAT)) {
97+
// TODO: Remove in 8.x
98+
formatDesc = null;
10599
}
100+
final DocValueFormat format = fieldType.docValueFormat(formatDesc, null);
106101
LeafReaderContext subReaderContext = null;
107102
AtomicFieldData data = null;
108-
ScriptDocValues<?> scriptValues = null; // legacy
109103
SortedBinaryDocValues binaryValues = null; // binary / string / ip fields
110104
SortedNumericDocValues longValues = null; // int / date fields
111105
SortedNumericDoubleValues doubleValues = null; // floating-point fields
@@ -115,9 +109,7 @@ public void hitsExecute(SearchContext context, SearchHit[] hits) throws IOExcept
115109
int readerIndex = ReaderUtil.subIndex(hit.docId(), context.searcher().getIndexReader().leaves());
116110
subReaderContext = context.searcher().getIndexReader().leaves().get(readerIndex);
117111
data = indexFieldData.load(subReaderContext);
118-
if (format == null) {
119-
scriptValues = data.getLegacyFieldValues();
120-
} else if (indexFieldData instanceof IndexNumericFieldData) {
112+
if (indexFieldData instanceof IndexNumericFieldData) {
121113
if (((IndexNumericFieldData) indexFieldData).getNumericType().isFloatingPoint()) {
122114
doubleValues = ((AtomicNumericFieldData) data).getDoubleValues();
123115
} else {
@@ -138,10 +130,7 @@ public void hitsExecute(SearchContext context, SearchHit[] hits) throws IOExcept
138130
final List<Object> values = hitField.getValues();
139131

140132
int subDocId = hit.docId() - subReaderContext.docBase;
141-
if (scriptValues != null) {
142-
scriptValues.setNextDocId(subDocId);
143-
values.addAll(scriptValues);
144-
} else if (binaryValues != null) {
133+
if (binaryValues != null) {
145134
if (binaryValues.advanceExact(subDocId)) {
146135
for (int i = 0, count = binaryValues.docValueCount(); i < count; ++i) {
147136
values.add(format.format(binaryValues.nextValue()));

server/src/test/java/org/elasticsearch/index/query/InnerHitBuilderTests.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.elasticsearch.script.ScriptType;
3333
import org.elasticsearch.search.SearchModule;
3434
import org.elasticsearch.search.builder.SearchSourceBuilder;
35-
import org.elasticsearch.search.fetch.subphase.DocValueFieldsContext;
3635
import org.elasticsearch.search.fetch.subphase.DocValueFieldsContext.FieldAndFormat;
3736
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
3837
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilderTests;
@@ -158,8 +157,7 @@ public static InnerHitBuilder randomInnerHits() {
158157
innerHits.setStoredFieldNames(randomListStuff(16, () -> randomAlphaOfLengthBetween(1, 16)));
159158
}
160159
innerHits.setDocValueFields(randomListStuff(16,
161-
() -> new FieldAndFormat(randomAlphaOfLengthBetween(1, 16),
162-
randomBoolean() ? null : DocValueFieldsContext.USE_DEFAULT_FORMAT)));
160+
() -> new FieldAndFormat(randomAlphaOfLengthBetween(1, 16), null)));
163161
// Random script fields deduped on their field name.
164162
Map<String, SearchSourceBuilder.ScriptField> scriptFields = new HashMap<>();
165163
for (SearchSourceBuilder.ScriptField field: randomListStuff(16, InnerHitBuilderTests::randomScript)) {
@@ -201,8 +199,7 @@ static InnerHitBuilder mutate(InnerHitBuilder original) throws IOException {
201199
modifiers.add(() -> {
202200
if (randomBoolean()) {
203201
copy.setDocValueFields(randomValueOtherThan(copy.getDocValueFields(),
204-
() -> randomListStuff(16, () -> new FieldAndFormat(randomAlphaOfLengthBetween(1, 16),
205-
randomBoolean() ? null : DocValueFieldsContext.USE_DEFAULT_FORMAT))));
202+
() -> randomListStuff(16, () -> new FieldAndFormat(randomAlphaOfLengthBetween(1, 16), null))));
206203
} else {
207204
copy.addDocValueField(randomAlphaOfLengthBetween(1, 16));
208205
}

0 commit comments

Comments
 (0)