Skip to content

Commit 6ddbdcd

Browse files
[DerivedFields] PR2 - Implementation for all supported types and DerivedFieldType (opensearch-project#12808)
Implementation for all supported types and DerivedFieldType. We support the following types so far: boolean date geo_point ip keyword long double --------- Signed-off-by: Rishabh Maurya <rishabhmaurya05@gmail.com>
1 parent 5db84d1 commit 6ddbdcd

File tree

4 files changed

+614
-1
lines changed

4 files changed

+614
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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.lucene.document.DoubleField;
12+
import org.apache.lucene.document.Field;
13+
import org.apache.lucene.document.FieldType;
14+
import org.apache.lucene.document.InetAddressPoint;
15+
import org.apache.lucene.document.KeywordField;
16+
import org.apache.lucene.document.LatLonPoint;
17+
import org.apache.lucene.document.LongField;
18+
import org.apache.lucene.document.LongPoint;
19+
import org.apache.lucene.index.IndexOptions;
20+
import org.apache.lucene.index.IndexableField;
21+
import org.opensearch.Version;
22+
import org.opensearch.common.Booleans;
23+
import org.opensearch.common.lucene.Lucene;
24+
import org.opensearch.common.network.InetAddresses;
25+
26+
import java.net.InetAddress;
27+
import java.util.Arrays;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.function.BiFunction;
31+
import java.util.function.Function;
32+
import java.util.stream.Collectors;
33+
34+
/**
35+
* Contains logic to get the FieldMapper for a given type of derived field. Also, for a given type of derived field,
36+
* it is used to create an IndexableField for the provided type and object. It is useful when indexing into
37+
* lucene MemoryIndex in {@link org.opensearch.index.query.DerivedFieldQuery}.
38+
*/
39+
enum DerivedFieldSupportedTypes {
40+
41+
BOOLEAN("boolean", (name, context) -> {
42+
BooleanFieldMapper.Builder builder = new BooleanFieldMapper.Builder(name);
43+
return builder.build(context);
44+
}, name -> o -> {
45+
// Trying to mimic the logic for parsing source value as used in BooleanFieldMapper valueFetcher
46+
Boolean value;
47+
if (o instanceof Boolean) {
48+
value = (Boolean) o;
49+
} else {
50+
String textValue = o.toString();
51+
value = Booleans.parseBooleanStrict(textValue, false);
52+
}
53+
return new Field(name, value ? "T" : "F", BooleanFieldMapper.Defaults.FIELD_TYPE);
54+
}),
55+
DATE("date", (name, context) -> {
56+
// TODO: should we support mapping settings exposed by a given field type from derived fields too?
57+
// for example, support `format` for date type?
58+
DateFieldMapper.Builder builder = new DateFieldMapper.Builder(
59+
name,
60+
DateFieldMapper.Resolution.MILLISECONDS,
61+
DateFieldMapper.getDefaultDateTimeFormatter(),
62+
false,
63+
Version.CURRENT
64+
);
65+
return builder.build(context);
66+
}, name -> o -> new LongPoint(name, (long) o)),
67+
GEO_POINT("geo_point", (name, context) -> {
68+
GeoPointFieldMapper.Builder builder = new GeoPointFieldMapper.Builder(name);
69+
return builder.build(context);
70+
}, name -> o -> {
71+
// convert o to array of double
72+
if (!(o instanceof List) || ((List<?>) o).size() != 2 || !(((List<?>) o).get(0) instanceof Double)) {
73+
throw new ClassCastException("geo_point should be in format emit(double lat, double lon) for derived fields");
74+
}
75+
return new LatLonPoint(name, (Double) ((List<?>) o).get(0), (Double) ((List<?>) o).get(1));
76+
}),
77+
IP("ip", (name, context) -> {
78+
IpFieldMapper.Builder builder = new IpFieldMapper.Builder(name, false, Version.CURRENT);
79+
return builder.build(context);
80+
}, name -> o -> {
81+
InetAddress address;
82+
if (o instanceof InetAddress) {
83+
address = (InetAddress) o;
84+
} else {
85+
address = InetAddresses.forString(o.toString());
86+
}
87+
return new InetAddressPoint(name, address);
88+
}),
89+
KEYWORD("keyword", (name, context) -> {
90+
FieldType dummyFieldType = new FieldType();
91+
dummyFieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS);
92+
KeywordFieldMapper.Builder keywordBuilder = new KeywordFieldMapper.Builder(name);
93+
KeywordFieldMapper.KeywordFieldType keywordFieldType = keywordBuilder.buildFieldType(context, dummyFieldType);
94+
keywordFieldType.setIndexAnalyzer(Lucene.KEYWORD_ANALYZER);
95+
return new KeywordFieldMapper(
96+
name,
97+
dummyFieldType,
98+
keywordFieldType,
99+
keywordBuilder.multiFieldsBuilder.build(keywordBuilder, context),
100+
keywordBuilder.copyTo.build(),
101+
keywordBuilder
102+
);
103+
}, name -> o -> new KeywordField(name, (String) o, Field.Store.NO)),
104+
LONG("long", (name, context) -> {
105+
NumberFieldMapper.Builder longBuilder = new NumberFieldMapper.Builder(name, NumberFieldMapper.NumberType.LONG, false, false);
106+
return longBuilder.build(context);
107+
}, name -> o -> new LongField(name, Long.parseLong(o.toString()), Field.Store.NO)),
108+
DOUBLE("double", (name, context) -> {
109+
NumberFieldMapper.Builder doubleBuilder = new NumberFieldMapper.Builder(name, NumberFieldMapper.NumberType.DOUBLE, false, false);
110+
return doubleBuilder.build(context);
111+
}, name -> o -> new DoubleField(name, Double.parseDouble(o.toString()), Field.Store.NO));
112+
113+
final String name;
114+
private final BiFunction<String, Mapper.BuilderContext, FieldMapper> builder;
115+
116+
private final Function<String, Function<Object, IndexableField>> indexableFieldBuilder;
117+
118+
DerivedFieldSupportedTypes(
119+
String name,
120+
BiFunction<String, Mapper.BuilderContext, FieldMapper> builder,
121+
Function<String, Function<Object, IndexableField>> indexableFieldBuilder
122+
) {
123+
this.name = name;
124+
this.builder = builder;
125+
this.indexableFieldBuilder = indexableFieldBuilder;
126+
}
127+
128+
public String getName() {
129+
return name;
130+
}
131+
132+
private FieldMapper getFieldMapper(String name, Mapper.BuilderContext context) {
133+
return builder.apply(name, context);
134+
}
135+
136+
private Function<Object, IndexableField> getIndexableFieldGenerator(String name) {
137+
return indexableFieldBuilder.apply(name);
138+
}
139+
140+
private static final Map<String, DerivedFieldSupportedTypes> enumMap = Arrays.stream(DerivedFieldSupportedTypes.values())
141+
.collect(Collectors.toMap(DerivedFieldSupportedTypes::getName, enumValue -> enumValue));
142+
143+
public static FieldMapper getFieldMapperFromType(String type, String name, Mapper.BuilderContext context) {
144+
if (!enumMap.containsKey(type)) {
145+
throw new IllegalArgumentException("Type [" + type + "] isn't supported in Derived field context.");
146+
}
147+
return enumMap.get(type).getFieldMapper(name, context);
148+
}
149+
150+
public static Function<Object, IndexableField> getIndexableFieldGeneratorType(String type, String name) {
151+
if (!enumMap.containsKey(type)) {
152+
throw new IllegalArgumentException("Type [" + type + "] isn't supported in Derived field context.");
153+
}
154+
return enumMap.get(type).getIndexableFieldGenerator(name);
155+
}
156+
}

0 commit comments

Comments
 (0)