Skip to content

Commit b8a7b12

Browse files
committed
Support derived fields definition in search request
* adds support for fetch phase on derived fields * adds support for highligting on derived fields Signed-off-by: Rishabh Maurya <rishabhmaurya05@gmail.com>
1 parent 5329c66 commit b8a7b12

File tree

7 files changed

+82
-10
lines changed

7 files changed

+82
-10
lines changed

server/src/main/java/org/opensearch/index/mapper/DocumentMapperParser.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public DocumentMapper parse(@Nullable String type, CompressedXContent source) th
133133
}
134134

135135
@SuppressWarnings({ "unchecked" })
136-
private DocumentMapper parse(String type, Map<String, Object> mapping) throws MapperParsingException {
136+
public DocumentMapper parse(String type, Map<String, Object> mapping) throws MapperParsingException {
137137
if (type == null) {
138138
throw new MapperParsingException("Failed to derive type");
139139
}

server/src/main/java/org/opensearch/index/query/QueryShardContext.java

+25-7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import org.opensearch.index.cache.bitset.BitsetFilterCache;
5959
import org.opensearch.index.fielddata.IndexFieldData;
6060
import org.opensearch.index.mapper.ContentPath;
61+
import org.opensearch.index.mapper.DerivedFieldMapper;
6162
import org.opensearch.index.mapper.DocumentMapper;
6263
import org.opensearch.index.mapper.MappedFieldType;
6364
import org.opensearch.index.mapper.Mapper;
@@ -119,6 +120,8 @@ public class QueryShardContext extends QueryRewriteContext {
119120
private final ValuesSourceRegistry valuesSourceRegistry;
120121
private BitSetProducer parentFilter;
121122

123+
private DocumentMapper derivedFieldMappers;
124+
122125
public QueryShardContext(
123126
int shardId,
124127
IndexSettings indexSettings,
@@ -264,6 +267,7 @@ private QueryShardContext(
264267
this.fullyQualifiedIndex = fullyQualifiedIndex;
265268
this.allowExpensiveQueries = allowExpensiveQueries;
266269
this.valuesSourceRegistry = valuesSourceRegistry;
270+
derivedFieldMappers = null;
267271
}
268272

269273
private void reset() {
@@ -395,6 +399,14 @@ public ValuesSourceRegistry getValuesSourceRegistry() {
395399
return valuesSourceRegistry;
396400
}
397401

402+
public void setDerivedFieldMappers(DocumentMapper derivedFieldMappers) {
403+
this.derivedFieldMappers = derivedFieldMappers;
404+
}
405+
406+
public DocumentMapper getDerivedFieldsMapper() {
407+
return derivedFieldMappers;
408+
}
409+
398410
public void setAllowUnmappedFields(boolean allowUnmappedFields) {
399411
this.allowUnmappedFields = allowUnmappedFields;
400412
}
@@ -404,14 +416,20 @@ public void setMapUnmappedFieldAsString(boolean mapUnmappedFieldAsString) {
404416
}
405417

406418
MappedFieldType failIfFieldMappingNotFound(String name, MappedFieldType fieldMapping) {
407-
if (fieldMapping != null || allowUnmappedFields) {
419+
if (fieldMapping != null) {
408420
return fieldMapping;
409-
} else if (mapUnmappedFieldAsString) {
410-
TextFieldMapper.Builder builder = new TextFieldMapper.Builder(name, mapperService.getIndexAnalyzers());
411-
return builder.build(new Mapper.BuilderContext(indexSettings.getSettings(), new ContentPath(1))).fieldType();
412-
} else {
413-
throw new QueryShardException(this, "No field mapping can be found for the field with name [{}]", name);
414-
}
421+
} else if (derivedFieldMappers != null
422+
&& derivedFieldMappers.mappers() != null
423+
&& derivedFieldMappers.mappers().getMapper(name) != null) {
424+
return ((DerivedFieldMapper) derivedFieldMappers.mappers().getMapper(name)).fieldType();
425+
} else if (allowUnmappedFields) {
426+
return fieldMapping;
427+
} else if (mapUnmappedFieldAsString) {
428+
TextFieldMapper.Builder builder = new TextFieldMapper.Builder(name, mapperService.getIndexAnalyzers());
429+
return builder.build(new Mapper.BuilderContext(indexSettings.getSettings(), new ContentPath(1))).fieldType();
430+
} else {
431+
throw new QueryShardException(this, "No field mapping can be found for the field with name [{}]", name);
432+
}
415433
}
416434

417435
private SearchLookup lookup = null;

server/src/main/java/org/opensearch/search/SearchService.java

+11
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
import org.opensearch.index.IndexService;
7878
import org.opensearch.index.IndexSettings;
7979
import org.opensearch.index.engine.Engine;
80+
import org.opensearch.index.mapper.DerivedFieldMapper;
81+
import org.opensearch.index.mapper.DocumentMapper;
8082
import org.opensearch.index.query.InnerHitContextBuilder;
8183
import org.opensearch.index.query.MatchAllQueryBuilder;
8284
import org.opensearch.index.query.MatchNoneQueryBuilder;
@@ -1066,6 +1068,15 @@ private DefaultSearchContext createSearchContext(ReaderContext reader, ShardSear
10661068
// might end up with incorrect state since we are using now() or script services
10671069
// during rewrite and normalized / evaluate templates etc.
10681070
QueryShardContext context = new QueryShardContext(searchContext.getQueryShardContext());
1071+
if (request.source().derivedFields() != null && request.source().size() != 0) {
1072+
Map<String, Object> derivedFieldObject = new HashMap<>();
1073+
derivedFieldObject.put(DerivedFieldMapper.CONTENT_TYPE, request.source().derivedFields());
1074+
DocumentMapper documentMapper = searchContext.mapperService()
1075+
.documentMapperParser()
1076+
.parse(DerivedFieldMapper.CONTENT_TYPE, derivedFieldObject);
1077+
context.setDerivedFieldMappers(documentMapper);
1078+
searchContext.getQueryShardContext().setDerivedFieldMappers(documentMapper);
1079+
}
10691080
Rewriteable.rewrite(request.getRewriteable(), context, true);
10701081
assert searchContext.getQueryShardContext().isCacheable();
10711082
success = true;

server/src/main/java/org/opensearch/search/builder/SearchSourceBuilder.java

+24
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.opensearch.core.xcontent.XContentBuilder;
5252
import org.opensearch.core.xcontent.XContentHelper;
5353
import org.opensearch.core.xcontent.XContentParser;
54+
import org.opensearch.index.mapper.DerivedFieldMapper;
5455
import org.opensearch.index.query.QueryBuilder;
5556
import org.opensearch.index.query.QueryRewriteContext;
5657
import org.opensearch.index.query.Rewriteable;
@@ -113,6 +114,7 @@ public final class SearchSourceBuilder implements Writeable, ToXContentObject, R
113114
public static final ParseField DOCVALUE_FIELDS_FIELD = new ParseField("docvalue_fields");
114115
public static final ParseField FETCH_FIELDS_FIELD = new ParseField("fields");
115116
public static final ParseField SCRIPT_FIELDS_FIELD = new ParseField("script_fields");
117+
public static final ParseField DERIVED_FIELDS_FIELD = new ParseField(DerivedFieldMapper.CONTENT_TYPE);
116118
public static final ParseField SCRIPT_FIELD = new ParseField("script");
117119
public static final ParseField IGNORE_FAILURE_FIELD = new ParseField("ignore_failure");
118120
public static final ParseField SORT_FIELD = new ParseField("sort");
@@ -192,6 +194,7 @@ public static HighlightBuilder highlight() {
192194
private StoredFieldsContext storedFieldsContext;
193195
private List<FieldAndFormat> docValueFields;
194196
private List<ScriptField> scriptFields;
197+
private Map<String, Object> derivedFields;
195198
private FetchSourceContext fetchSourceContext;
196199
private List<FieldAndFormat> fetchFields;
197200

@@ -247,6 +250,9 @@ public SearchSourceBuilder(StreamInput in) throws IOException {
247250
if (in.readBoolean()) {
248251
scriptFields = in.readList(ScriptField::new);
249252
}
253+
if (in.readBoolean()) {
254+
derivedFields = in.readMap();
255+
}
250256
size = in.readVInt();
251257
if (in.readBoolean()) {
252258
int size = in.readVInt();
@@ -310,6 +316,11 @@ public void writeTo(StreamOutput out) throws IOException {
310316
if (hasScriptFields) {
311317
out.writeList(scriptFields);
312318
}
319+
boolean hasDerivedFields = derivedFields != null;
320+
out.writeBoolean(hasDerivedFields);
321+
if (hasDerivedFields) {
322+
out.writeMap(derivedFields);
323+
}
313324
out.writeVInt(size);
314325
boolean hasSorts = sorts != null;
315326
out.writeBoolean(hasSorts);
@@ -955,6 +966,10 @@ public List<ScriptField> scriptFields() {
955966
return scriptFields;
956967
}
957968

969+
public Map<String, Object> derivedFields() {
970+
return derivedFields;
971+
}
972+
958973
/**
959974
* Sets the boost a specific index or alias will receive when the query is executed
960975
* against it.
@@ -1119,6 +1134,7 @@ private SearchSourceBuilder shallowCopy(
11191134
rewrittenBuilder.queryBuilder = queryBuilder;
11201135
rewrittenBuilder.rescoreBuilders = rescoreBuilders;
11211136
rewrittenBuilder.scriptFields = scriptFields;
1137+
rewrittenBuilder.derivedFields = derivedFields;
11221138
rewrittenBuilder.searchAfterBuilder = searchAfterBuilder;
11231139
rewrittenBuilder.sliceBuilder = slice;
11241140
rewrittenBuilder.size = size;
@@ -1220,6 +1236,8 @@ public void parseXContent(XContentParser parser, boolean checkTrailingTokens) th
12201236
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
12211237
scriptFields.add(new ScriptField(parser));
12221238
}
1239+
} else if (DERIVED_FIELDS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
1240+
derivedFields = parser.map();
12231241
} else if (INDICES_BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
12241242
deprecationLogger.deprecate(
12251243
"indices_boost_object_format",
@@ -1434,6 +1452,10 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t
14341452
builder.endObject();
14351453
}
14361454

1455+
if (derivedFields != null) {
1456+
builder.field(DERIVED_FIELDS_FIELD.getPreferredName(), derivedFields);
1457+
}
1458+
14371459
if (sorts != null) {
14381460
builder.startArray(SORT_FIELD.getPreferredName());
14391461
for (SortBuilder<?> sort : sorts) {
@@ -1772,6 +1794,7 @@ public int hashCode() {
17721794
queryBuilder,
17731795
rescoreBuilders,
17741796
scriptFields,
1797+
derivedFields,
17751798
size,
17761799
sorts,
17771800
searchAfterBuilder,
@@ -1815,6 +1838,7 @@ public boolean equals(Object obj) {
18151838
&& Objects.equals(queryBuilder, other.queryBuilder)
18161839
&& Objects.equals(rescoreBuilders, other.rescoreBuilders)
18171840
&& Objects.equals(scriptFields, other.scriptFields)
1841+
&& Objects.equals(derivedFields, other.derivedFields)
18181842
&& Objects.equals(size, other.size)
18191843
&& Objects.equals(sorts, other.sorts)
18201844
&& Objects.equals(searchAfterBuilder, other.searchAfterBuilder)

server/src/main/java/org/opensearch/search/fetch/subphase/highlight/HighlightPhase.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import org.apache.lucene.index.LeafReaderContext;
3636
import org.apache.lucene.search.Query;
3737
import org.opensearch.common.regex.Regex;
38+
import org.opensearch.index.mapper.DerivedFieldMapper;
39+
import org.opensearch.index.mapper.DocumentMapper;
3840
import org.opensearch.index.mapper.KeywordFieldMapper;
3941
import org.opensearch.index.mapper.MappedFieldType;
4042
import org.opensearch.index.mapper.SourceFieldMapper;
@@ -145,6 +147,12 @@ private Map<String, Function<HitContext, FieldHighlightContext>> contextBuilders
145147
boolean fieldNameContainsWildcards = field.field().contains("*");
146148
for (String fieldName : fieldNamesToHighlight) {
147149
MappedFieldType fieldType = context.mapperService().fieldType(fieldName);
150+
if (fieldType == null && context.getQueryShardContext().getDerivedFieldsMapper() != null) {
151+
DocumentMapper derivedFieldsMapper = context.getQueryShardContext().getDerivedFieldsMapper();
152+
if (derivedFieldsMapper.mappers() != null && derivedFieldsMapper.mappers().getMapper(fieldName) != null) {
153+
fieldType = ((DerivedFieldMapper) derivedFieldsMapper.mappers().getMapper(fieldName)).fieldType();
154+
}
155+
}
148156
if (fieldType == null) {
149157
continue;
150158
}
@@ -170,12 +178,13 @@ private Map<String, Function<HitContext, FieldHighlightContext>> contextBuilders
170178
Query highlightQuery = field.fieldOptions().highlightQuery();
171179

172180
boolean forceSource = highlightContext.forceSource(field);
181+
MappedFieldType finalFieldType = fieldType;
173182
builders.put(
174183
fieldName,
175184
hc -> new FieldHighlightContext(
176-
fieldType.name(),
185+
finalFieldType.name(),
177186
field,
178-
fieldType,
187+
finalFieldType,
179188
context,
180189
hc,
181190
highlightQuery == null ? query : highlightQuery,

server/src/main/java/org/opensearch/search/fetch/subphase/highlight/HighlightUtils.java

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.lucene.search.highlight.Encoder;
3636
import org.apache.lucene.search.highlight.SimpleHTMLEncoder;
3737
import org.opensearch.index.fieldvisitor.CustomFieldsVisitor;
38+
import org.opensearch.index.mapper.DerivedFieldValueFetcher;
3839
import org.opensearch.index.mapper.MappedFieldType;
3940
import org.opensearch.index.mapper.ValueFetcher;
4041
import org.opensearch.index.query.QueryShardContext;
@@ -77,6 +78,9 @@ public static List<Object> loadFieldValues(
7778
return textsToHighlight != null ? textsToHighlight : Collections.emptyList();
7879
}
7980
ValueFetcher fetcher = fieldType.valueFetcher(context, null, null);
81+
if (fetcher instanceof DerivedFieldValueFetcher) {
82+
fetcher.setNextReader(hitContext.reader().getContext());
83+
}
8084
return fetcher.fetchValues(hitContext.sourceLookup());
8185
}
8286

server/src/main/java/org/opensearch/search/fetch/subphase/highlight/UnifiedHighlighter.java

+6
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ CustomUnifiedHighlighter buildHighlighter(FieldHighlightContext fieldContext) th
159159
Integer fieldMaxAnalyzedOffset = fieldContext.field.fieldOptions().maxAnalyzerOffset();
160160
int numberOfFragments = fieldContext.field.fieldOptions().numberOfFragments();
161161
Analyzer analyzer = getAnalyzer(fieldContext.context.mapperService().documentMapper());
162+
if (fieldContext.context.getQueryShardContext().getDerivedFieldsMapper() != null) {
163+
DocumentMapper derivedFieldsMapper = fieldContext.context.getQueryShardContext().getDerivedFieldsMapper();
164+
if (derivedFieldsMapper != null) {
165+
analyzer = getAnalyzer(derivedFieldsMapper);
166+
}
167+
}
162168
if (fieldMaxAnalyzedOffset != null) {
163169
analyzer = getLimitedOffsetAnalyzer(analyzer, fieldMaxAnalyzedOffset);
164170
}

0 commit comments

Comments
 (0)