Skip to content

Commit 852f6a4

Browse files
author
Christoph Büscher
authored
Improve FieldFetcher retrieval of fields (#66160)
Currently FieldFetcher stores all the FieldContexts that are later used to retrieve the fields in a List. This has the disadvantage that the same field path can be retrieved several times (e.g. if multiple patterns match the same path or if similar paths are defined several times e.g. with different formats). Currently the last value to be retrieved "wins" and gets returned. We might as well de-duplicate the FieldContexts by using a map internally, keyed by the field path that is going to be retrieved, to avoid more work later.
1 parent e8332e1 commit 852f6a4

File tree

2 files changed

+39
-15
lines changed

2 files changed

+39
-15
lines changed

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

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import java.util.ArrayList;
3535
import java.util.Collection;
3636
import java.util.HashMap;
37-
import java.util.HashSet;
37+
import java.util.LinkedHashMap;
3838
import java.util.List;
3939
import java.util.Map;
4040
import java.util.Set;
@@ -48,9 +48,10 @@ public static FieldFetcher create(QueryShardContext context,
4848
SearchLookup searchLookup,
4949
Collection<FieldAndFormat> fieldAndFormats) {
5050

51-
List<FieldContext> fieldContexts = new ArrayList<>();
51+
// Using a LinkedHashMap so fields are returned in the order requested.
52+
// We won't formally guarantee this but but its good for readability of the response
53+
Map<String, FieldContext> fieldContexts = new LinkedHashMap<>();
5254
List<String> unmappedFetchPattern = new ArrayList<>();
53-
Set<String> mappedToExclude = new HashSet<>();
5455
boolean includeUnmapped = false;
5556

5657
for (FieldAndFormat fieldAndFormat : fieldAndFormats) {
@@ -68,8 +69,7 @@ public static FieldFetcher create(QueryShardContext context,
6869
continue;
6970
}
7071
ValueFetcher valueFetcher = ft.valueFetcher(context, format);
71-
mappedToExclude.add(field);
72-
fieldContexts.add(new FieldContext(field, valueFetcher));
72+
fieldContexts.put(field, new FieldContext(field, valueFetcher));
7373
}
7474
}
7575
CharacterRunAutomaton unmappedFetchAutomaton = new CharacterRunAutomaton(Automata.makeEmpty());
@@ -78,29 +78,26 @@ public static FieldFetcher create(QueryShardContext context,
7878
Regex.simpleMatchToAutomaton(unmappedFetchPattern.toArray(new String[unmappedFetchPattern.size()]))
7979
);
8080
}
81-
return new FieldFetcher(fieldContexts, unmappedFetchAutomaton, mappedToExclude, includeUnmapped);
81+
return new FieldFetcher(fieldContexts, unmappedFetchAutomaton, includeUnmapped);
8282
}
8383

84-
private final List<FieldContext> fieldContexts;
84+
private final Map<String, FieldContext> fieldContexts;
8585
private final CharacterRunAutomaton unmappedFetchAutomaton;
86-
private final Set<String> mappedToExclude;
8786
private final boolean includeUnmapped;
8887

8988
private FieldFetcher(
90-
List<FieldContext> fieldContexts,
89+
Map<String, FieldContext> fieldContexts,
9190
CharacterRunAutomaton unmappedFetchAutomaton,
92-
Set<String> mappedToExclude,
9391
boolean includeUnmapped
9492
) {
9593
this.fieldContexts = fieldContexts;
9694
this.unmappedFetchAutomaton = unmappedFetchAutomaton;
97-
this.mappedToExclude = mappedToExclude;
9895
this.includeUnmapped = includeUnmapped;
9996
}
10097

10198
public Map<String, DocumentField> fetch(SourceLookup sourceLookup, Set<String> ignoredFields) throws IOException {
10299
Map<String, DocumentField> documentFields = new HashMap<>();
103-
for (FieldContext context : fieldContexts) {
100+
for (FieldContext context : fieldContexts.values()) {
104101
String field = context.fieldName;
105102
if (ignoredFields.contains(field)) {
106103
continue;
@@ -141,7 +138,7 @@ private void collectUnmapped(Map<String, DocumentField> documentFields, Map<Stri
141138
collectUnmappedList(documentFields, (List<?>) value, currentPath, currentState);
142139
} else {
143140
// we have a leaf value
144-
if (this.unmappedFetchAutomaton.isAccept(currentState) && this.mappedToExclude.contains(currentPath) == false) {
141+
if (this.unmappedFetchAutomaton.isAccept(currentState) && this.fieldContexts.containsKey(currentPath) == false) {
145142
if (value != null) {
146143
DocumentField currentEntry = documentFields.get(currentPath);
147144
if (currentEntry == null) {
@@ -170,7 +167,7 @@ private void collectUnmappedList(Map<String, DocumentField> documentFields, Iter
170167
} else if (value instanceof List) {
171168
// weird case, but can happen for objects with "enabled" : "false"
172169
collectUnmappedList(documentFields, (List<?>) value, parentPath, lastState);
173-
} else if (this.unmappedFetchAutomaton.isAccept(lastState) && this.mappedToExclude.contains(parentPath) == false) {
170+
} else if (this.unmappedFetchAutomaton.isAccept(lastState) && this.fieldContexts.containsKey(parentPath) == false) {
174171
list.add(value);
175172
}
176173
}
@@ -192,7 +189,7 @@ private static int step(CharacterRunAutomaton automaton, String key, int state)
192189
}
193190

194191
public void setNextReader(LeafReaderContext readerContext) {
195-
for (FieldContext field : fieldContexts) {
192+
for (FieldContext field : fieldContexts.values()) {
196193
field.valueFetcher.setNextReader(readerContext);
197194
}
198195
}

server/src/test/java/org/elasticsearch/search/fetch/subphase/FieldFetcherTests.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.elasticsearch.search.lookup.SourceLookup;
3737

3838
import java.io.IOException;
39+
import java.util.ArrayList;
3940
import java.util.Collections;
4041
import java.util.List;
4142
import java.util.Map;
@@ -658,6 +659,32 @@ public void testUnmappedFieldsWildcard() throws IOException {
658659
assertThat(fields.get("unmapped_object.b").getValue(), equalTo("bar"));
659660
}
660661

662+
public void testLastFormatWins() throws IOException {
663+
MapperService mapperService = createMapperService();
664+
665+
XContentBuilder source = XContentFactory.jsonBuilder().startObject()
666+
.startArray("date_field")
667+
.value("2011-11-11T11:11:11")
668+
.value("2012-12-12T12:12:12")
669+
.endArray()
670+
.endObject();
671+
672+
List<FieldAndFormat> ff = new ArrayList<>();
673+
ff.add(new FieldAndFormat("date_field", "year", false));
674+
Map<String, DocumentField> fields = fetchFields(mapperService, source, ff, null);
675+
assertThat(fields.size(), equalTo(1));
676+
assertThat(fields.get("date_field").getValues().size(), equalTo(2));
677+
assertThat(fields.get("date_field").getValues().get(0), equalTo("2011"));
678+
assertThat(fields.get("date_field").getValues().get(1), equalTo("2012"));
679+
680+
ff.add(new FieldAndFormat("date_field", "hour", false));
681+
fields = fetchFields(mapperService, source, ff, null);
682+
assertThat(fields.size(), equalTo(1));
683+
assertThat(fields.get("date_field").getValues().size(), equalTo(2));
684+
assertThat(fields.get("date_field").getValues().get(0), equalTo("11"));
685+
assertThat(fields.get("date_field").getValues().get(1), equalTo("12"));
686+
}
687+
661688
private List<FieldAndFormat> fieldAndFormatList(String name, String format, boolean includeUnmapped) {
662689
return Collections.singletonList(new FieldAndFormat(name, format, includeUnmapped));
663690
}

0 commit comments

Comments
 (0)