|
@@ -71,7 +71,6 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constru
|
|
|
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
|
|
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
|
|
|
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureFieldName;
|
|
|
-import static org.elasticsearch.common.xcontent.XContentParserUtils.parseFieldsValue;
|
|
|
|
|
|
/**
|
|
|
* A single search hit.
|
|
@@ -96,7 +95,7 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
private BytesReference source;
|
|
|
|
|
|
private Map<String, DocumentField> documentFields;
|
|
|
- private Map<String, DocumentField> metaFields;
|
|
|
+ private final Map<String, DocumentField> metaFields;
|
|
|
|
|
|
private Map<String, HighlightField> highlightFields = null;
|
|
|
|
|
@@ -137,15 +136,8 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
this.id = null;
|
|
|
}
|
|
|
this.nestedIdentity = nestedIdentity;
|
|
|
- this.documentFields = documentFields;
|
|
|
- if (this.documentFields == null) {
|
|
|
- this.documentFields = new HashMap<>();
|
|
|
- }
|
|
|
-
|
|
|
- this.metaFields = metaFields;
|
|
|
- if (this.metaFields == null) {
|
|
|
- this.metaFields = new HashMap<>();
|
|
|
- }
|
|
|
+ this.documentFields = documentFields == null ? emptyMap() : documentFields;
|
|
|
+ this.metaFields = metaFields == null ? emptyMap() : metaFields;
|
|
|
}
|
|
|
|
|
|
public SearchHit(StreamInput in) throws IOException {
|
|
@@ -173,7 +165,8 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
Map<String, DocumentField> fields = readFields(in);
|
|
|
documentFields = new HashMap<>();
|
|
|
metaFields = new HashMap<>();
|
|
|
- SearchHit.splitFieldsByMetadata(fields, documentFields, metaFields);
|
|
|
+ fields.forEach((fieldName, docField) ->
|
|
|
+ (MapperService.isMetadataFieldStatic(fieldName) ? metaFields : documentFields).put(fieldName, docField));
|
|
|
}
|
|
|
|
|
|
int size = in.readVInt();
|
|
@@ -219,7 +212,7 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
|
|
|
|
|
|
private Map<String, DocumentField> readFields(StreamInput in) throws IOException {
|
|
|
- Map<String, DocumentField> fields = null;
|
|
|
+ Map<String, DocumentField> fields;
|
|
|
int size = in.readVInt();
|
|
|
if (size == 0) {
|
|
|
fields = emptyMap();
|
|
@@ -449,19 +442,21 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
* The hit field matching the given field name.
|
|
|
*/
|
|
|
public DocumentField field(String fieldName) {
|
|
|
- return getFields().get(fieldName);
|
|
|
+ DocumentField result = documentFields.get(fieldName);
|
|
|
+ if (result != null) {
|
|
|
+ return result;
|
|
|
+ } else {
|
|
|
+ return metaFields.get(fieldName);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Adds a new DocumentField to the map in case both parameters are not null.
|
|
|
* */
|
|
|
- public void setField(String fieldName, DocumentField field) {
|
|
|
+ public void setDocumentField(String fieldName, DocumentField field) {
|
|
|
if (fieldName == null || field == null) return;
|
|
|
- if (field.isMetadataField()) {
|
|
|
- this.metaFields.put(fieldName, field);
|
|
|
- } else {
|
|
|
- this.documentFields.put(fieldName, field);
|
|
|
- }
|
|
|
+ if (documentFields.size() == 0) this.documentFields = new HashMap<>();
|
|
|
+ this.documentFields.put(fieldName, field);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -469,27 +464,13 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
* were required to be loaded.
|
|
|
*/
|
|
|
public Map<String, DocumentField> getFields() {
|
|
|
- Map<String, DocumentField> fields = new HashMap<>();
|
|
|
- fields.putAll(metaFields);
|
|
|
- fields.putAll(documentFields);
|
|
|
- return fields;
|
|
|
- }
|
|
|
-
|
|
|
- // returns the fields without handling null cases
|
|
|
- public Map<String, DocumentField> fieldsOrNull() {
|
|
|
- return getFields();
|
|
|
- }
|
|
|
-
|
|
|
- public void fields(Map<String, DocumentField> fields) {
|
|
|
- Objects.requireNonNull(fields);
|
|
|
- this.metaFields = new HashMap<String, DocumentField>();
|
|
|
- this.documentFields = new HashMap<String, DocumentField>();
|
|
|
- for (Map.Entry<String, DocumentField> fieldEntry: fields.entrySet()) {
|
|
|
- if (fieldEntry.getValue().isMetadataField()) {
|
|
|
- this.metaFields.put(fieldEntry.getKey(), fieldEntry.getValue());
|
|
|
- } else {
|
|
|
- this.documentFields.put(fieldEntry.getKey(), fieldEntry.getValue());
|
|
|
- }
|
|
|
+ if (metaFields.size() > 0 || documentFields.size() > 0) {
|
|
|
+ final Map<String, DocumentField> fields = new HashMap<>();
|
|
|
+ fields.putAll(metaFields);
|
|
|
+ fields.putAll(documentFields);
|
|
|
+ return fields;
|
|
|
+ } else {
|
|
|
+ return emptyMap();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -589,22 +570,6 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
this.innerHits = innerHits;
|
|
|
}
|
|
|
|
|
|
- public static void splitFieldsByMetadata(Map<String, DocumentField> fields,
|
|
|
- Map<String, DocumentField> documentFields,
|
|
|
- Map<String, DocumentField> metaFields) {
|
|
|
- // documentFields and metaFields must be non-empty maps
|
|
|
- if (fields == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- for (Map.Entry<String, DocumentField> fieldEntry: fields.entrySet()) {
|
|
|
- if (fieldEntry.getValue().isMetadataField()) {
|
|
|
- metaFields.put(fieldEntry.getKey(), fieldEntry.getValue());
|
|
|
- } else {
|
|
|
- documentFields.put(fieldEntry.getKey(), fieldEntry.getValue());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
public static class Fields {
|
|
|
static final String _INDEX = "_index";
|
|
|
static final String _ID = "_id";
|
|
@@ -720,6 +685,18 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
return builder;
|
|
|
}
|
|
|
|
|
|
+ // All fields on the root level of the parsed SearhHit are interpreted as metadata fields
|
|
|
+ // public because we use it in a completion suggestion option
|
|
|
+ public static final ObjectParser.UnknownFieldConsumer<Map<String, Object>> unknownMetaFieldConsumer = (map, fieldName, fieldValue) -> {
|
|
|
+ Map<String, DocumentField> fieldMap = (Map<String, DocumentField>) map.computeIfAbsent(
|
|
|
+ METADATA_FIELDS, v -> new HashMap<String, DocumentField>());
|
|
|
+ if (fieldName.equals(IgnoredFieldMapper.NAME)) {
|
|
|
+ fieldMap.put(fieldName, new DocumentField(fieldName, (List<Object>) fieldValue));
|
|
|
+ } else {
|
|
|
+ fieldMap.put(fieldName, new DocumentField(fieldName, Collections.singletonList(fieldValue)));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
/**
|
|
|
* This parser outputs a temporary map of the objects needed to create the
|
|
|
* SearchHit instead of directly creating the SearchHit. The reason for this
|
|
@@ -730,7 +707,8 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
* of the included search hit. The output of the map is used to create the
|
|
|
* actual SearchHit instance via {@link #createFromMap(Map)}
|
|
|
*/
|
|
|
- private static final ObjectParser<Map<String, Object>, Void> MAP_PARSER = new ObjectParser<>("innerHitParser", true, HashMap::new);
|
|
|
+ private static final ObjectParser<Map<String, Object>, Void> MAP_PARSER = new ObjectParser<>("innerHitParser",
|
|
|
+ unknownMetaFieldConsumer, HashMap::new);
|
|
|
|
|
|
static {
|
|
|
declareInnerHitsParseFields(MAP_PARSER);
|
|
@@ -741,7 +719,6 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
}
|
|
|
|
|
|
public static void declareInnerHitsParseFields(ObjectParser<Map<String, Object>, Void> parser) {
|
|
|
- declareMetadataFields(parser);
|
|
|
parser.declareString((map, value) -> map.put(Fields._INDEX, value), new ParseField(Fields._INDEX));
|
|
|
parser.declareString((map, value) -> map.put(Fields._ID, value), new ParseField(Fields._ID));
|
|
|
parser.declareString((map, value) -> map.put(Fields._NODE, value), new ParseField(Fields._NODE));
|
|
@@ -838,39 +815,6 @@ public final class SearchHit implements Writeable, ToXContentObject, Iterable<Do
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * we need to declare parse fields for each metadata field, except for _ID, _INDEX and _TYPE which are
|
|
|
- * handled individually. All other fields are parsed to an entry in the fields map
|
|
|
- */
|
|
|
- private static void declareMetadataFields(ObjectParser<Map<String, Object>, Void> parser) {
|
|
|
- /* TODO: This method and its usage in declareInnerHitsParseFields() must be replaced by
|
|
|
- calling an UnknownFieldConsumer. All fields on the root level of the parsed SearhHit
|
|
|
- should be interpreted as metadata fields.
|
|
|
- */
|
|
|
- for (String metadatafield : MapperService.getAllMetaFields()) {
|
|
|
- if (metadatafield.equals(Fields._ID) == false && metadatafield.equals(Fields._INDEX) == false) {
|
|
|
- if (metadatafield.equals(IgnoredFieldMapper.NAME)) {
|
|
|
- parser.declareObjectArray((map, list) -> {
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Map<String, DocumentField> fieldMap = (Map<String, DocumentField>) map.computeIfAbsent(METADATA_FIELDS,
|
|
|
- v -> new HashMap<String, DocumentField>());
|
|
|
- DocumentField field = new DocumentField(metadatafield, list);
|
|
|
- fieldMap.put(field.getName(), field);
|
|
|
- }, (p, c) -> parseFieldsValue(p),
|
|
|
- new ParseField(metadatafield));
|
|
|
- } else {
|
|
|
- parser.declareField((map, field) -> {
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- Map<String, DocumentField> fieldMap = (Map<String, DocumentField>) map.computeIfAbsent(METADATA_FIELDS,
|
|
|
- v -> new HashMap<String, DocumentField>());
|
|
|
- fieldMap.put(field.getName(), field);
|
|
|
- }, (p, c) -> new DocumentField(metadatafield, Collections.singletonList(parseFieldsValue(p))),
|
|
|
- new ParseField(metadatafield), ValueType.VALUE);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private static Map<String, DocumentField> parseFields(XContentParser parser) throws IOException {
|
|
|
Map<String, DocumentField> fields = new HashMap<>();
|
|
|
while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
|