Skip to content

Commit 2f475af

Browse files
committed
Support for OpenSearch alias type
Signed-off-by: Heng Qian <[email protected]>
1 parent 2f43bb8 commit 2f475af

File tree

11 files changed

+267
-10
lines changed

11 files changed

+267
-10
lines changed

core/src/main/java/org/opensearch/sql/data/type/ExprType.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import java.util.Arrays;
1111
import java.util.List;
12+
import java.util.Optional;
1213
import org.opensearch.sql.data.model.ExprValue;
1314
import org.opensearch.sql.expression.Expression;
1415

@@ -54,4 +55,17 @@ default List<ExprType> getParent() {
5455
default String legacyTypeName() {
5556
return typeName();
5657
}
58+
59+
/** Get the original path. Types like alias type will set the actual path in field property. */
60+
default Optional<String> getOriginalPath() {
61+
return Optional.empty();
62+
}
63+
64+
/**
65+
* Get the original path. Types like alias type should be derived from the type of the original
66+
* field.
67+
*/
68+
default ExprType getOriginalExprType() {
69+
return this;
70+
}
5771
}

core/src/main/java/org/opensearch/sql/expression/ReferenceExpression.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
public class ReferenceExpression implements Expression {
2424
@Getter private final String attr;
2525

26+
@Getter private final String rawPath;
27+
2628
@Getter private final List<String> paths;
2729

2830
private final ExprType type;
@@ -36,8 +38,11 @@ public class ReferenceExpression implements Expression {
3638
public ReferenceExpression(String ref, ExprType type) {
3739
this.attr = ref;
3840
// Todo. the define of paths need to be redefined after adding multiple index/variable support.
39-
this.paths = Arrays.asList(ref.split("\\."));
40-
this.type = type;
41+
// For AliasType, the actual path is set in the property of `path` and the type is derived
42+
// from the type of field on that path; Otherwise, use ref itself as the path
43+
this.rawPath = type.getOriginalPath().orElse(ref);
44+
this.paths = Arrays.asList(rawPath.split("\\."));
45+
this.type = type.getOriginalExprType();
4146
}
4247

4348
@Override

core/src/test/java/org/opensearch/sql/data/type/ExprTypeTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static org.opensearch.sql.data.type.ExprCoreType.UNDEFINED;
2525
import static org.opensearch.sql.data.type.ExprCoreType.UNKNOWN;
2626

27+
import java.util.Optional;
2728
import org.hamcrest.Matchers;
2829
import org.junit.jupiter.api.Test;
2930

@@ -85,4 +86,16 @@ void defaultLegacyTypeName() {
8586
final ExprType exprType = () -> "dummy";
8687
assertEquals("dummy", exprType.legacyTypeName());
8788
}
89+
90+
@Test
91+
void getOriginalPath() {
92+
final ExprType exprType = () -> "dummy";
93+
assertEquals(Optional.empty(), exprType.getOriginalPath());
94+
}
95+
96+
@Test
97+
void getOriginalExprType() {
98+
final ExprType exprType = () -> "dummy";
99+
assertEquals(exprType, exprType.getOriginalExprType());
100+
}
88101
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.opensearch.data.type;
7+
8+
import java.util.List;
9+
import java.util.Optional;
10+
import java.util.Set;
11+
import org.opensearch.sql.data.type.ExprType;
12+
13+
/**
14+
* The type of alias. See <a
15+
* href="https://opensearch.org/docs/latest/opensearch/supported-field-types/alias/">doc</a>
16+
*/
17+
public class OpenSearchAliasType extends OpenSearchDataType {
18+
19+
public static final String typeName = "alias";
20+
public static final String pathPropertyName = "path";
21+
public static final Set<MappingType> objectFieldTypes =
22+
Set.of(MappingType.Object, MappingType.Nested);
23+
private final String path;
24+
private final OpenSearchDataType originalType;
25+
26+
public OpenSearchAliasType(String path, OpenSearchDataType type) {
27+
super(type.getExprCoreType());
28+
if (type instanceof OpenSearchAliasType) {
29+
throw new IllegalStateException(
30+
String.format("Alias field cannot refer to the path [%s] of alias type", path));
31+
} else if (objectFieldTypes.contains(type.getMappingType())) {
32+
throw new IllegalStateException(
33+
String.format("Alias field cannot refer to the path [%s] of object type", path));
34+
}
35+
this.path = path;
36+
this.originalType = type;
37+
}
38+
39+
@Override
40+
public Optional<String> getOriginalPath() {
41+
return Optional.of(this.path);
42+
}
43+
44+
@Override
45+
public ExprType getOriginalExprType() {
46+
return originalType.getExprType();
47+
}
48+
49+
@Override
50+
public ExprType getExprType() {
51+
return this;
52+
}
53+
54+
@Override
55+
public OpenSearchDataType cloneEmpty() {
56+
return new OpenSearchAliasType(this.path, originalType.cloneEmpty());
57+
}
58+
59+
@Override
60+
public boolean isCompatible(ExprType other) {
61+
return originalType.isCompatible(other);
62+
}
63+
64+
@Override
65+
public List<ExprType> getParent() {
66+
return originalType.getParent();
67+
}
68+
69+
@Override
70+
public String typeName() {
71+
return originalType.typeName();
72+
}
73+
74+
@Override
75+
public String legacyTypeName() {
76+
return originalType.legacyTypeName();
77+
}
78+
79+
@Override
80+
public boolean shouldCast(ExprType other) {
81+
return originalType.shouldCast(other);
82+
}
83+
}

opensearch/src/main/java/org/opensearch/sql/opensearch/data/type/OpenSearchDataType.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,19 @@ public static Map<String, OpenSearchDataType> parseMapping(Map<String, Object> i
109109
return result;
110110
}
111111

112+
Map<String, String> aliasMapping = new LinkedHashMap<>();
112113
indexMapping.forEach(
113114
(k, v) -> {
114115
var innerMap = (Map<String, Object>) v;
115116
// by default, the type is treated as an Object if "type" is not provided
116117
var type = ((String) innerMap.getOrDefault("type", "object")).replace("_", "");
117118
if (!EnumUtils.isValidEnumIgnoreCase(OpenSearchDataType.MappingType.class, type)) {
118119
// unknown type, e.g. `alias`
119-
// TODO resolve alias reference
120+
// Record fields of the alias type and resolve them later in case their references have
121+
// not been resolved.
122+
if (OpenSearchAliasType.typeName.equals(type)) {
123+
aliasMapping.put(k, (String) innerMap.get(OpenSearchAliasType.pathPropertyName));
124+
}
120125
return;
121126
}
122127
// create OpenSearchDataType
@@ -126,6 +131,18 @@ public static Map<String, OpenSearchDataType> parseMapping(Map<String, Object> i
126131
EnumUtils.getEnumIgnoreCase(OpenSearchDataType.MappingType.class, type),
127132
innerMap));
128133
});
134+
135+
// Begin to parse alias type fields
136+
aliasMapping.forEach(
137+
(k, v) -> {
138+
if (result.containsKey(v)) {
139+
result.put(k, new OpenSearchAliasType(v, result.get(v)));
140+
} else {
141+
throw new IllegalStateException(
142+
String.format("Cannot find the path [%s] for alias type field [%s]", v, k));
143+
}
144+
});
145+
129146
return result;
130147
}
131148

opensearch/src/main/java/org/opensearch/sql/opensearch/request/OpenSearchRequestBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ public void pushDownHighlight(String field, Map<String, Literal> arguments) {
281281
/** Push down project list to DSL requests. */
282282
public void pushDownProjects(Set<ReferenceExpression> projects) {
283283
sourceBuilder.fetchSource(
284-
projects.stream().map(ReferenceExpression::getAttr).distinct().toArray(String[]::new),
284+
projects.stream().map(ReferenceExpression::getRawPath).distinct().toArray(String[]::new),
285285
new String[0]);
286286
}
287287

opensearch/src/test/java/org/opensearch/sql/opensearch/client/OpenSearchNodeClientTest.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import org.opensearch.sql.data.model.ExprIntegerValue;
7171
import org.opensearch.sql.data.model.ExprTupleValue;
7272
import org.opensearch.sql.data.model.ExprValue;
73+
import org.opensearch.sql.opensearch.data.type.OpenSearchAliasType;
7374
import org.opensearch.sql.opensearch.data.type.OpenSearchDataType;
7475
import org.opensearch.sql.opensearch.data.type.OpenSearchTextType;
7576
import org.opensearch.sql.opensearch.data.value.OpenSearchExprValueFactory;
@@ -159,9 +160,9 @@ void get_index_mappings() throws IOException {
159160
var parsedTypes = OpenSearchDataType.traverseAndFlatten(mapping);
160161
assertAll(
161162
() -> assertEquals(1, indexMappings.size()),
162-
// 10 types extended to 17 after flattening
163-
() -> assertEquals(10, mapping.size()),
164-
() -> assertEquals(17, parsedTypes.size()),
163+
// 11 types extended to 18 after flattening
164+
() -> assertEquals(11, mapping.size()),
165+
() -> assertEquals(18, parsedTypes.size()),
165166
() -> assertEquals("TEXT", mapping.get("address").legacyTypeName()),
166167
() -> assertEquals(OpenSearchTextType.of(MappingType.Text), parsedTypes.get("address")),
167168
() -> assertEquals("INTEGER", mapping.get("age").legacyTypeName()),
@@ -186,6 +187,11 @@ void get_index_mappings() throws IOException {
186187
() -> assertEquals(OpenSearchTextType.of(MappingType.Text), parsedTypes.get("employer")),
187188
// `employer` is a `text` with `fields`
188189
() -> assertTrue(((OpenSearchTextType) parsedTypes.get("employer")).getFields().size() > 0),
190+
() -> assertEquals("TEXT", mapping.get("employer_alias").legacyTypeName()),
191+
() ->
192+
assertEquals(
193+
new OpenSearchAliasType("employer", OpenSearchTextType.of(MappingType.Text)),
194+
parsedTypes.get("employer_alias")),
189195
() -> assertEquals("NESTED", mapping.get("projects").legacyTypeName()),
190196
() -> assertEquals(OpenSearchTextType.of(MappingType.Nested), parsedTypes.get("projects")),
191197
() ->

opensearch/src/test/java/org/opensearch/sql/opensearch/client/OpenSearchRestClientTest.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import org.opensearch.sql.data.model.ExprIntegerValue;
6969
import org.opensearch.sql.data.model.ExprTupleValue;
7070
import org.opensearch.sql.data.model.ExprValue;
71+
import org.opensearch.sql.opensearch.data.type.OpenSearchAliasType;
7172
import org.opensearch.sql.opensearch.data.type.OpenSearchDataType;
7273
import org.opensearch.sql.opensearch.data.type.OpenSearchTextType;
7374
import org.opensearch.sql.opensearch.data.value.OpenSearchExprValueFactory;
@@ -162,9 +163,9 @@ void get_index_mappings() throws IOException {
162163
var parsedTypes = OpenSearchDataType.traverseAndFlatten(mapping);
163164
assertAll(
164165
() -> assertEquals(1, indexMappings.size()),
165-
// 10 types extended to 17 after flattening
166-
() -> assertEquals(10, mapping.size()),
167-
() -> assertEquals(17, parsedTypes.size()),
166+
// 11 types extended to 18 after flattening
167+
() -> assertEquals(11, mapping.size()),
168+
() -> assertEquals(18, parsedTypes.size()),
168169
() -> assertEquals("TEXT", mapping.get("address").legacyTypeName()),
169170
() -> assertEquals(OpenSearchTextType.of(MappingType.Text), parsedTypes.get("address")),
170171
() -> assertEquals("INTEGER", mapping.get("age").legacyTypeName()),
@@ -189,6 +190,11 @@ void get_index_mappings() throws IOException {
189190
() -> assertEquals(OpenSearchTextType.of(MappingType.Text), parsedTypes.get("employer")),
190191
// `employer` is a `text` with `fields`
191192
() -> assertTrue(((OpenSearchTextType) parsedTypes.get("employer")).getFields().size() > 0),
193+
() -> assertEquals("TEXT", mapping.get("employer_alias").legacyTypeName()),
194+
() ->
195+
assertEquals(
196+
new OpenSearchAliasType("employer", OpenSearchTextType.of(MappingType.Text)),
197+
parsedTypes.get("employer_alias")),
192198
() -> assertEquals("NESTED", mapping.get("projects").legacyTypeName()),
193199
() -> assertEquals(OpenSearchTextType.of(MappingType.Nested), parsedTypes.get("projects")),
194200
() ->

0 commit comments

Comments
 (0)