Skip to content

Commit 142e7c1

Browse files
Fix duplicate strings in SearchHit serialization
The map key is always the field name. We exploited this fact in the get results but not in search hits, leading to a lot of duplicate strings in many heap dumps. We could do much better here since the names are generally coming out of a know limited set of names, but as a first step lets at least align the get- and search-responses and non-trivial amount of bytes in a number of use-cases. Plus, having a single string instance is faster on lookup etc. and saves on CPU also.
1 parent a5f935a commit 142e7c1

File tree

17 files changed

+65
-51
lines changed

17 files changed

+65
-51
lines changed

modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorMatchedSlotSubFetchPhase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public void process(HitContext hitContext) throws IOException {
100100
IntStream slots = convertTopDocsToSlots(topDocs, pc.rootDocsBySlot);
101101
// _percolator_document_slot fields are document fields and should be under "fields" section in a hit
102102
List<Object> docSlots = slots.boxed().collect(Collectors.toList());
103-
hitContext.hit().setDocumentField(fieldName, new DocumentField(fieldName, docSlots));
103+
hitContext.hit().setDocumentField(new DocumentField(fieldName, docSlots));
104104

105105
// Add info what sub-queries of percolator query matched this each percolated document
106106
if (fetchContext.getSearchExecutionContext().hasNamedQueries()) {
@@ -120,7 +120,7 @@ public void process(HitContext hitContext) throws IOException {
120120
matchedQueries.add(match.getName());
121121
}
122122
String matchedFieldName = fieldName + "_" + docSlots.get(i) + "_matched_queries";
123-
hitContext.hit().setDocumentField(matchedFieldName, new DocumentField(matchedFieldName, matchedQueries));
123+
hitContext.hit().setDocumentField(new DocumentField(matchedFieldName, matchedQueries));
124124
}
125125
}
126126
}

server/src/internalClusterTest/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,8 +1327,7 @@ public void setNextReader(LeafReaderContext ctx) {
13271327
public void process(FetchSubPhase.HitContext hitContext) {
13281328
leafSearchLookup.setDocument(hitContext.docId());
13291329
FieldLookup fieldLookup = leafSearchLookup.fields().get("text");
1330-
hitContext.hit()
1331-
.setDocumentField("text_stored_lookup", new DocumentField("text_stored_lookup", fieldLookup.getValues()));
1330+
hitContext.hit().setDocumentField(new DocumentField("text_stored_lookup", fieldLookup.getValues()));
13321331
}
13331332

13341333
@Override

server/src/internalClusterTest/java/org/elasticsearch/search/fetch/FetchSubPhasePluginIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ private void hitExecute(FetchContext context, HitContext hitContext) throws IOEx
137137
DocumentField hitField = hitContext.hit().getFields().get(NAME);
138138
if (hitField == null) {
139139
hitField = new DocumentField(NAME, new ArrayList<>(1));
140-
hitContext.hit().setDocumentField(NAME, hitField);
140+
hitContext.hit().setDocumentField(hitField);
141141
}
142142
Terms terms = hitContext.reader().termVectors().get(hitContext.docId(), field);
143143
if (terms != null) {

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ static TransportVersion def(int id) {
233233
public static final TransportVersion SEARCH_INCREMENTAL_TOP_DOCS_NULL = def(9_058_0_00);
234234
public static final TransportVersion COMPRESS_DELAYABLE_WRITEABLE = def(9_059_0_00);
235235
public static final TransportVersion SYNONYMS_REFRESH_PARAM = def(9_060_0_00);
236+
public static final TransportVersion DOC_FIELDS_AS_LIST = def(9_061_0_00);
236237

237238
/*
238239
* STOP! READ THIS FIRST! No, really,

server/src/main/java/org/elasticsearch/common/document/DocumentField.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Collections;
2525
import java.util.Iterator;
2626
import java.util.List;
27+
import java.util.Map;
2728
import java.util.Objects;
2829

2930
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
@@ -70,6 +71,15 @@ public DocumentField(String name, List<Object> values, List<Object> ignoredValue
7071
: "DocumentField can't have both lookup fields and values";
7172
}
7273

74+
/**
75+
* Read map of document fields written via {@link StreamOutput#writeMapValues(Map)}.
76+
* @param in stream input
77+
* @return map of {@link DocumentField} keyed by {@link DocumentField#getName()}
78+
*/
79+
public static Map<String, DocumentField> readFieldsFromMapValues(StreamInput in) throws IOException {
80+
return in.readMapValues(DocumentField::new, DocumentField::getName);
81+
}
82+
7383
/**
7484
* The name of the field.
7585
*/

server/src/main/java/org/elasticsearch/index/get/GetResult.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ public GetResult(StreamInput in) throws IOException {
7474
if (source.length() == 0) {
7575
source = null;
7676
}
77-
documentFields = in.readMapValues(DocumentField::new, DocumentField::getName);
78-
metaFields = in.readMapValues(DocumentField::new, DocumentField::getName);
77+
documentFields = DocumentField.readFieldsFromMapValues(in);
78+
metaFields = DocumentField.readFieldsFromMapValues(in);
7979
} else {
8080
metaFields = Collections.emptyMap();
8181
documentFields = Collections.emptyMap();

server/src/main/java/org/elasticsearch/search/SearchHit.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,15 @@ public static SearchHit readFrom(StreamInput in, boolean pooled) throws IOExcept
216216
if (in.readBoolean()) {
217217
explanation = readExplanation(in);
218218
}
219-
final Map<String, DocumentField> documentFields = in.readMap(DocumentField::new);
220-
final Map<String, DocumentField> metaFields = in.readMap(DocumentField::new);
219+
final Map<String, DocumentField> documentFields;
220+
final Map<String, DocumentField> metaFields;
221+
if (in.getTransportVersion().onOrAfter(TransportVersions.DOC_FIELDS_AS_LIST)) {
222+
documentFields = DocumentField.readFieldsFromMapValues(in);
223+
metaFields = DocumentField.readFieldsFromMapValues(in);
224+
} else {
225+
documentFields = in.readMap(DocumentField::new);
226+
metaFields = in.readMap(DocumentField::new);
227+
}
221228
Map<String, HighlightField> highlightFields = in.readMapValues(HighlightField::new, HighlightField::name);
222229
highlightFields = highlightFields.isEmpty() ? null : unmodifiableMap(highlightFields);
223230

@@ -322,8 +329,13 @@ public void writeTo(StreamOutput out) throws IOException {
322329
out.writeBoolean(true);
323330
writeExplanation(out, explanation);
324331
}
325-
out.writeMap(documentFields, StreamOutput::writeWriteable);
326-
out.writeMap(metaFields, StreamOutput::writeWriteable);
332+
if (out.getTransportVersion().onOrAfter(TransportVersions.DOC_FIELDS_AS_LIST)) {
333+
out.writeMapValues(documentFields);
334+
out.writeMapValues(metaFields);
335+
} else {
336+
out.writeMap(documentFields, StreamOutput::writeWriteable);
337+
out.writeMap(metaFields, StreamOutput::writeWriteable);
338+
}
327339
if (highlightFields == null) {
328340
out.writeVInt(0);
329341
} else {
@@ -502,9 +514,9 @@ public DocumentField field(String fieldName) {
502514
/*
503515
* Adds a new DocumentField to the map in case both parameters are not null.
504516
* */
505-
public void setDocumentField(String fieldName, DocumentField field) {
506-
if (fieldName == null || field == null) return;
507-
this.documentFields.put(fieldName, field);
517+
public void setDocumentField(DocumentField field) {
518+
if (field == null) return;
519+
this.documentFields.put(field.getName(), field);
508520
}
509521

510522
public void addDocumentFields(Map<String, DocumentField> docFields, Map<String, DocumentField> metaFields) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public void process(HitContext hit) throws IOException {
7676
hitField = new DocumentField(f.field, new ArrayList<>(2));
7777
// even if we request a doc values of a meta-field (e.g. _routing),
7878
// docValues fields will still be document fields, and put under "fields" section of a hit.
79-
hit.hit().setDocumentField(f.field, hitField);
79+
hit.hit().setDocumentField(hitField);
8080
}
8181
List<Object> ignoredValues = new ArrayList<>();
8282
hitField.getValues().addAll(f.fetcher.fetchValues(hit.source(), hit.docId(), ignoredValues));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void process(HitContext hitContext) {
7575
}
7676
hitField = new DocumentField(scriptFieldName, values);
7777
// script fields are never meta-fields
78-
hitContext.hit().setDocumentField(scriptFieldName, hitField);
78+
hitContext.hit().setDocumentField(hitField);
7979
}
8080
}
8181
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ public void process(HitContext hitContext) {
9595
Map<String, List<Object>> loadedFields = hitContext.loadedFields();
9696
for (StoredField storedField : storedFields) {
9797
if (storedField.hasValue(loadedFields)) {
98-
hitContext.hit()
99-
.setDocumentField(storedField.name, new DocumentField(storedField.name, storedField.process(loadedFields)));
98+
hitContext.hit().setDocumentField(new DocumentField(storedField.name, storedField.process(loadedFields)));
10099
}
101100
}
102101
}

0 commit comments

Comments
 (0)