Skip to content

Commit 2f45c5a

Browse files
committed
javadoc and code refactor
Signed-off-by: Rishabh Maurya <[email protected]>
1 parent 5f5f54d commit 2f45c5a

14 files changed

+930
-221
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.index.mapper;
10+
11+
import org.apache.logging.log4j.LogManager;
12+
import org.apache.logging.log4j.Logger;
13+
import org.opensearch.common.regex.Regex;
14+
import org.opensearch.index.query.QueryShardContext;
15+
import org.opensearch.script.Script;
16+
17+
import java.io.IOException;
18+
import java.util.HashMap;
19+
import java.util.HashSet;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.Optional;
23+
import java.util.Set;
24+
import java.util.concurrent.ConcurrentHashMap;
25+
26+
/**
27+
* Accepts definition of DerivedField from search request in both forms: map parsed from SearchRequest and {@link DerivedField} defined using client.
28+
* The object is initialized per search request and is responsible to resolve {@link DerivedFieldType} given a field name.
29+
* It uses {@link FieldTypeInference} to infer field type for a nested field within DerivedField of {@link DerivedFieldSupportedTypes#OBJECT} type.
30+
*/
31+
public class DefaultDerivedFieldResolver implements DerivedFieldResolver {
32+
private final QueryShardContext queryShardContext;
33+
private final Map<String, DerivedFieldType> derivedFieldTypeMap = new ConcurrentHashMap<>();
34+
private final FieldTypeInference typeInference;
35+
private static final Logger logger = LogManager.getLogger(DefaultDerivedFieldResolver.class);
36+
37+
public DefaultDerivedFieldResolver(
38+
QueryShardContext queryShardContext,
39+
Map<String, Object> derivedFieldsObject,
40+
List<DerivedField> derivedFields
41+
) {
42+
this(
43+
queryShardContext,
44+
derivedFieldsObject,
45+
derivedFields,
46+
new FieldTypeInference(
47+
queryShardContext.index().getName(),
48+
queryShardContext.getMapperService(),
49+
queryShardContext.getIndexReader()
50+
)
51+
);
52+
}
53+
54+
public DefaultDerivedFieldResolver(
55+
QueryShardContext queryShardContext,
56+
Map<String, Object> derivedFieldsObject,
57+
List<DerivedField> derivedFields,
58+
FieldTypeInference typeInference
59+
) {
60+
this.queryShardContext = queryShardContext;
61+
initDerivedFieldTypes(derivedFieldsObject, derivedFields);
62+
this.typeInference = typeInference;
63+
}
64+
65+
@Override
66+
public Set<String> resolvePattern(String pattern) {
67+
Set<String> derivedFields = new HashSet<>();
68+
for (MappedFieldType fieldType : queryShardContext.getMapperService().fieldTypes()) {
69+
if (Regex.simpleMatch(pattern, fieldType.name()) && fieldType instanceof DerivedFieldType) {
70+
derivedFields.add(fieldType.name());
71+
}
72+
}
73+
for (String fieldName : derivedFieldTypeMap.keySet()) {
74+
if (Regex.simpleMatch(pattern, fieldName)) {
75+
derivedFields.add(fieldName);
76+
}
77+
}
78+
return derivedFields;
79+
}
80+
81+
/**
82+
* Resolves the fieldName. The search request definitions are given precedence over derived fields definitions in the index mapping.
83+
* It caches the response for previously resolved field names
84+
* @param fieldName name of the field. It also accepts nested derived field
85+
* @return DerivedFieldType if resolved successfully, a null otherwise.
86+
*/
87+
@Override
88+
public DerivedFieldType resolve(String fieldName) {
89+
return Optional.ofNullable(resolveUsingSearchDefinitions(fieldName))
90+
.orElseGet(() -> (DerivedFieldType) queryShardContext.getMapperService().fieldType(fieldName));
91+
}
92+
93+
private DerivedFieldType resolveUsingSearchDefinitions(String fieldName) {
94+
if (derivedFieldTypeMap.containsKey(fieldName)) {
95+
return derivedFieldTypeMap.get(fieldName);
96+
}
97+
DerivedFieldType resolvedNestedType = resolveNestedField(fieldName);
98+
if (resolvedNestedType != null) {
99+
derivedFieldTypeMap.put(fieldName, resolvedNestedType);
100+
}
101+
return resolvedNestedType;
102+
}
103+
104+
private DerivedFieldType resolveNestedField(String fieldName) {
105+
DerivedFieldType parentDerivedField = (DerivedFieldType) getParentDerivedField(fieldName);
106+
if (parentDerivedField != null) {
107+
try {
108+
Script script = parentDerivedField.derivedField.getScript();
109+
Mapper inferredFieldMapper = typeInference.infer(getValueFetcher(fieldName, script));
110+
if (inferredFieldMapper != null) {
111+
return getDerivedFieldType(
112+
new DerivedField(
113+
fieldName,
114+
inferredFieldMapper.typeName(),
115+
script,
116+
parentDerivedField.derivedField.getSourceIndexedField()
117+
)
118+
);
119+
} else {
120+
logger.warn("Field type cannot be inferred. Ensure the field {} is not rare across entire index", fieldName);
121+
}
122+
} catch (IOException e) {
123+
logger.warn(e);
124+
}
125+
}
126+
return null;
127+
}
128+
129+
private MappedFieldType getParentDerivedField(String fieldName) {
130+
if (fieldName.contains(".")) {
131+
return resolve(fieldName.split("\\.")[0]);
132+
}
133+
return null;
134+
}
135+
136+
ValueFetcher getValueFetcher(String fieldName, Script script) {
137+
String subFieldName = fieldName.substring(fieldName.indexOf(".") + 1);
138+
return new ObjectDerivedFieldType.ObjectDerivedFieldValueFetcher(
139+
subFieldName,
140+
DerivedFieldType.getDerivedFieldLeafFactory(script, queryShardContext, queryShardContext.lookup()),
141+
o -> o // raw object returned will be used to infer the type without modifying it
142+
);
143+
}
144+
145+
private void initDerivedFieldTypes(Map<String, Object> derivedFieldsObject, List<DerivedField> derivedFields) {
146+
if (derivedFieldsObject != null && !derivedFieldsObject.isEmpty()) {
147+
Map<String, Object> derivedFieldObject = new HashMap<>();
148+
derivedFieldObject.put(DerivedFieldMapper.CONTENT_TYPE, derivedFieldsObject);
149+
derivedFieldTypeMap.putAll(getAllDerivedFieldTypeFromObject(derivedFieldObject));
150+
}
151+
if (derivedFields != null) {
152+
for (DerivedField derivedField : derivedFields) {
153+
derivedFieldTypeMap.put(derivedField.getName(), getDerivedFieldType(derivedField));
154+
}
155+
}
156+
}
157+
158+
private Map<String, DerivedFieldType> getAllDerivedFieldTypeFromObject(Map<String, Object> derivedFieldObject) {
159+
Map<String, DerivedFieldType> derivedFieldTypes = new HashMap<>();
160+
DocumentMapper documentMapper = queryShardContext.getMapperService()
161+
.documentMapperParser()
162+
.parse(DerivedFieldMapper.CONTENT_TYPE, derivedFieldObject);
163+
if (documentMapper != null && documentMapper.mappers() != null) {
164+
for (Mapper mapper : documentMapper.mappers()) {
165+
if (mapper instanceof DerivedFieldMapper) {
166+
DerivedFieldType derivedFieldType = ((DerivedFieldMapper) mapper).fieldType();
167+
derivedFieldTypes.put(derivedFieldType.name(), derivedFieldType);
168+
}
169+
}
170+
}
171+
return derivedFieldTypes;
172+
}
173+
174+
private DerivedFieldType getDerivedFieldType(DerivedField derivedField) {
175+
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(
176+
queryShardContext.getMapperService().getIndexSettings().getSettings(),
177+
new ContentPath(1)
178+
);
179+
DerivedFieldMapper.Builder builder = new DerivedFieldMapper.Builder(
180+
derivedField,
181+
queryShardContext.getMapperService().getIndexAnalyzers()
182+
);
183+
return builder.build(builderContext).fieldType();
184+
}
185+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public DerivedFieldMapper build(BuilderContext context) {
8585
);
8686
DerivedFieldType ft;
8787
if (name.contains(".")) {
88-
ft = new DerivedObjectFieldType(
88+
ft = new ObjectDerivedFieldType(
8989
new DerivedField(buildFullName(context), type.getValue(), script.getValue(), sourceIndexedField.getValue()),
9090
fieldMapper,
9191
fieldFunction,

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

Lines changed: 20 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -8,169 +8,29 @@
88

99
package org.opensearch.index.mapper;
1010

11-
import org.apache.logging.log4j.LogManager;
12-
import org.apache.logging.log4j.Logger;
1311
import org.opensearch.common.annotation.PublicApi;
14-
import org.opensearch.common.regex.Regex;
15-
import org.opensearch.index.query.QueryShardContext;
16-
import org.opensearch.script.Script;
1712

18-
import java.io.IOException;
19-
import java.util.HashMap;
20-
import java.util.HashSet;
21-
import java.util.List;
22-
import java.util.Map;
2313
import java.util.Set;
24-
import java.util.concurrent.ConcurrentHashMap;
2514

15+
/**
16+
* DerivedFieldResolver is used as a lookup to resolve derived fields from their name.
17+
* It is created per search request and needs to be set at {@link org.opensearch.index.query.QueryShardContext#setDerivedFieldResolver(DerivedFieldResolver)}
18+
* for derived fields resolution.
19+
*/
2620
@PublicApi(since = "2.15.0")
27-
public class DerivedFieldResolver {
28-
private final QueryShardContext queryShardContext;
29-
private final Map<String, MappedFieldType> derivedFieldTypeMap = new ConcurrentHashMap<>();
30-
private final FieldTypeInference typeInference;
31-
private static final Logger logger = LogManager.getLogger(DerivedFieldResolver.class);
32-
33-
public DerivedFieldResolver(
34-
QueryShardContext queryShardContext,
35-
Map<String, Object> derivedFieldsObject,
36-
List<DerivedField> derivedFields
37-
) {
38-
this(
39-
queryShardContext,
40-
derivedFieldsObject,
41-
derivedFields,
42-
new FieldTypeInference(
43-
queryShardContext.index().getName(),
44-
queryShardContext.getMapperService(),
45-
queryShardContext.getIndexReader()
46-
)
47-
);
48-
}
49-
50-
public DerivedFieldResolver(
51-
QueryShardContext queryShardContext,
52-
Map<String, Object> derivedFieldsObject,
53-
List<DerivedField> derivedFields,
54-
FieldTypeInference typeInference
55-
) {
56-
this.queryShardContext = queryShardContext;
57-
initializeDerivedFieldTypes(derivedFieldsObject);
58-
initializeDerivedFieldTypesFromList(derivedFields);
59-
this.typeInference = typeInference;
60-
}
61-
62-
private void initializeDerivedFieldTypes(Map<String, Object> derivedFieldsObject) {
63-
if (derivedFieldsObject != null) {
64-
Map<String, Object> derivedFieldObject = new HashMap<>();
65-
derivedFieldObject.put(DerivedFieldMapper.CONTENT_TYPE, derivedFieldsObject);
66-
derivedFieldTypeMap.putAll(getAllDerivedFieldTypeFromObject(derivedFieldObject));
67-
}
68-
}
69-
70-
private void initializeDerivedFieldTypesFromList(List<DerivedField> derivedFields) {
71-
if (derivedFields != null) {
72-
for (DerivedField derivedField : derivedFields) {
73-
derivedFieldTypeMap.put(derivedField.getName(), getDerivedFieldType(derivedField));
74-
}
75-
}
76-
}
77-
78-
public Set<String> resolvePattern(String pattern) {
79-
Set<String> matchingDerivedFields = new HashSet<>();
80-
for (String fieldName : derivedFieldTypeMap.keySet()) {
81-
if (!matchingDerivedFields.contains(fieldName) && Regex.simpleMatch(pattern, fieldName)) {
82-
matchingDerivedFields.add(fieldName);
83-
}
84-
}
85-
return matchingDerivedFields;
86-
}
87-
88-
public MappedFieldType resolve(String fieldName) {
89-
MappedFieldType fieldType = derivedFieldTypeMap.get(fieldName);
90-
if (fieldType != null) {
91-
return fieldType;
92-
}
93-
94-
fieldType = queryShardContext.getMapperService().fieldType(fieldName);
95-
if (fieldType != null) {
96-
return fieldType;
97-
}
98-
99-
if (fieldName.contains(".")) {
100-
return resolveNestedField(fieldName);
101-
}
102-
return null;
103-
}
104-
105-
private MappedFieldType resolveNestedField(String fieldName) {
106-
DerivedFieldType parentDerivedField = getParentDerivedField(fieldName);
107-
if (parentDerivedField == null) {
108-
return null;
109-
}
110-
ValueFetcher valueFetcher = getValueFetcher(fieldName, parentDerivedField.derivedField.getScript());
111-
Mapper inferredFieldMapper;
112-
try {
113-
inferredFieldMapper = typeInference.infer(valueFetcher);
114-
} catch (IOException e) {
115-
logger.warn(e);
116-
return null;
117-
}
118-
if (inferredFieldMapper == null) {
119-
return null;
120-
}
121-
return getDerivedFieldType(
122-
new DerivedField(
123-
fieldName,
124-
inferredFieldMapper.typeName(),
125-
parentDerivedField.derivedField.getScript(),
126-
parentDerivedField.derivedField.getSourceIndexedField()
127-
)
128-
);
129-
}
130-
131-
private DerivedFieldType getParentDerivedField(String fieldName) {
132-
String parentFieldName = fieldName.split("\\.")[0];
133-
DerivedFieldType parentDerivedFieldType = (DerivedFieldType) derivedFieldTypeMap.get(parentFieldName);
134-
if (parentDerivedFieldType == null) {
135-
parentDerivedFieldType = (DerivedFieldType) queryShardContext.getMapperService().fieldType(parentFieldName);
136-
}
137-
return parentDerivedFieldType;
138-
}
139-
140-
private ValueFetcher getValueFetcher(String fieldName, Script script) {
141-
String subFieldName = fieldName.substring(fieldName.indexOf(".") + 1);
142-
return new DerivedObjectFieldType.DerivedObjectFieldValueFetcher(
143-
subFieldName,
144-
DerivedFieldType.getDerivedFieldLeafFactory(script, queryShardContext, queryShardContext.lookup()),
145-
o -> o // raw object returned will be used to infer the type without modifying it
146-
);
147-
}
148-
149-
private Map<String, DerivedFieldType> getAllDerivedFieldTypeFromObject(Map<String, Object> derivedFieldObject) {
150-
Map<String, DerivedFieldType> derivedFieldTypes = new HashMap<>();
151-
DocumentMapper documentMapper = queryShardContext.getMapperService()
152-
.documentMapperParser()
153-
.parse(DerivedFieldMapper.CONTENT_TYPE, derivedFieldObject);
154-
if (documentMapper != null && documentMapper.mappers() != null) {
155-
for (Mapper mapper : documentMapper.mappers()) {
156-
if (mapper instanceof DerivedFieldMapper) {
157-
DerivedFieldType derivedFieldType = ((DerivedFieldMapper) mapper).fieldType();
158-
derivedFieldTypes.put(derivedFieldType.name(), derivedFieldType);
159-
}
160-
}
161-
}
162-
return derivedFieldTypes;
163-
}
164-
165-
private DerivedFieldType getDerivedFieldType(DerivedField derivedField) {
166-
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(
167-
queryShardContext.getMapperService().getIndexSettings().getSettings(),
168-
new ContentPath(1)
169-
);
170-
DerivedFieldMapper.Builder builder = new DerivedFieldMapper.Builder(
171-
derivedField,
172-
queryShardContext.getMapperService().getIndexAnalyzers()
173-
);
174-
return builder.build(builderContext).fieldType();
175-
}
21+
public interface DerivedFieldResolver {
22+
/**
23+
* Resolves all derived fields matching a given pattern. It includes derived fields defined both in search requests
24+
* and index mapping.
25+
* @param pattern regex pattern
26+
* @return all derived fields matching the pattern
27+
*/
28+
Set<String> resolvePattern(String pattern);
29+
30+
/**
31+
* Resolves the MappedFieldType associated with a derived field
32+
* @param fieldName field name to lookup
33+
* @return mapped field type
34+
*/
35+
MappedFieldType resolve(String fieldName);
17636
}

0 commit comments

Comments
 (0)