Skip to content

Commit 16ec113

Browse files
authored
Merge pull request #32673 from vespa-engine/bratseth/struct-field-type-checking
Correct struct field type checking
2 parents 1f17dec + eb0cf47 commit 16ec113

File tree

6 files changed

+71
-21
lines changed

6 files changed

+71
-21
lines changed

config-model/src/main/java/com/yahoo/schema/processing/IndexingValidation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ public void tryOutputType(Expression expression, String fieldName, DataType valu
155155

156156
private static DataType createCompatType(DataType origType) {
157157
if (origType instanceof ArrayDataType) {
158-
return DataType.getArray(createCompatType(((ArrayDataType)origType).getNestedType()));
158+
return DataType.getArray(createCompatType(origType.getNestedType()));
159159
} else if (origType instanceof MapDataType) {
160160
MapDataType mapType = (MapDataType)origType;
161161
return DataType.getMap(createCompatType(mapType.getKeyType()),

indexinglanguage/src/main/java/com/yahoo/vespa/indexinglanguage/expressions/GetFieldExpression.java

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,53 @@
33

44
import com.yahoo.document.DataType;
55
import com.yahoo.document.Field;
6+
import com.yahoo.document.StructDataType;
67
import com.yahoo.document.StructuredDataType;
78
import com.yahoo.document.datatypes.FieldValue;
89
import com.yahoo.document.datatypes.StructuredFieldValue;
910

1011
/**
12+
* Returns the value of a struct field.
13+
*
1114
* @author Simon Thoresen Hult
1215
*/
1316
public final class GetFieldExpression extends Expression {
1417

15-
private final String fieldName;
18+
private final String structFieldName;
1619

17-
public GetFieldExpression(String fieldName) {
20+
public GetFieldExpression(String structFieldName) {
1821
super(UnresolvedDataType.INSTANCE);
19-
this.fieldName = fieldName;
22+
this.structFieldName = structFieldName;
2023
}
2124

22-
public String getFieldName() { return fieldName; }
25+
public String getFieldName() { return structFieldName; }
2326

2427
@Override
2528
public DataType setInputType(DataType inputType, VerificationContext context) {
2629
super.setInputType(inputType, context);
27-
return context.getFieldType(fieldName, this);
30+
return getStructFieldType(context);
2831
}
2932

3033
@Override
3134
public DataType setOutputType(DataType outputType, VerificationContext context) {
32-
super.setOutputType(context.getFieldType(fieldName, this), outputType, context);
35+
super.setOutputType(getStructFieldType(context), outputType, context);
3336
return AnyDataType.instance;
3437
}
3538

3639
@Override
3740
protected void doVerify(VerificationContext context) {
41+
context.setCurrentType(getStructFieldType(context));
42+
}
43+
44+
private DataType getStructFieldType(VerificationContext context) {
3845
DataType input = context.getCurrentType();
39-
if (!(input instanceof StructuredDataType)) {
46+
if ( ! (input instanceof StructuredDataType structInput))
4047
throw new VerificationException(this, "Expected structured input, got " + input.getName());
41-
}
42-
Field field = ((StructuredDataType)input).getField(fieldName);
43-
if (field == null) {
44-
throw new VerificationException(this, "Field '" + fieldName + "' not found in struct type '" +
48+
Field field = structInput.getField(structFieldName);
49+
if (field == null)
50+
throw new VerificationException(this, "Field '" + structFieldName + "' not found in struct type '" +
4551
input.getName() + "'");
46-
}
47-
context.setCurrentType(field.getDataType());
52+
return field.getDataType();
4853
}
4954

5055
@Override
@@ -53,9 +58,9 @@ protected void doExecute(ExecutionContext context) {
5358
if (!(input instanceof StructuredFieldValue struct))
5459
throw new IllegalArgumentException("Expected structured input, got " + input.getDataType().getName());
5560

56-
Field field = struct.getField(fieldName);
61+
Field field = struct.getField(structFieldName);
5762
if (field == null)
58-
throw new IllegalArgumentException("Field '" + fieldName + "' not found in struct type '" +
63+
throw new IllegalArgumentException("Field '" + structFieldName + "' not found in struct type '" +
5964
struct.getDataType().getName() + "'");
6065
context.setCurrentValue(struct.getFieldValue(field));
6166
}
@@ -67,19 +72,19 @@ public DataType createdOutputType() {
6772

6873
@Override
6974
public String toString() {
70-
return "get_field " + fieldName;
75+
return "get_field " + structFieldName;
7176
}
7277

7378
@Override
7479
public boolean equals(Object obj) {
7580
if (!(obj instanceof GetFieldExpression rhs)) return false;
76-
if (!fieldName.equals(rhs.fieldName)) return false;
81+
if (!structFieldName.equals(rhs.structFieldName)) return false;
7782
return true;
7883
}
7984

8085
@Override
8186
public int hashCode() {
82-
return getClass().hashCode() + fieldName.hashCode();
87+
return getClass().hashCode() + structFieldName.hashCode();
8388
}
8489

8590
}

indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/SimpleTestAdapter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ public SimpleTestAdapter createField(Field field) {
3333

3434
@Override
3535
public DataType getInputType(Expression exp, String fieldName) {
36+
// Same check as in config-model IndexingValidation:
37+
if ( ! types.containsKey(fieldName))
38+
throw new VerificationException(exp, "Input field '" + fieldName + "' not found.");
3639
return types.get(fieldName);
3740
}
3841

indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/InputTestCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void requireThatExpressionCanBeVerified() {
4040
new InputExpression("bar").verify(adapter);
4141
fail();
4242
} catch (VerificationException e) {
43-
assertEquals("Field 'bar' not found", e.getMessage());
43+
assertEquals("Input field 'bar' not found.", e.getMessage());
4444
}
4545
}
4646

indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/ScriptTestCase.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import com.yahoo.document.ArrayDataType;
55
import com.yahoo.document.DataType;
66
import com.yahoo.document.Field;
7+
import com.yahoo.document.StructDataType;
8+
import com.yahoo.document.datatypes.Array;
79
import com.yahoo.document.datatypes.FieldValue;
810
import com.yahoo.document.datatypes.IntegerFieldValue;
911
import com.yahoo.document.datatypes.StringFieldValue;
12+
import com.yahoo.document.datatypes.Struct;
1013
import com.yahoo.language.simple.SimpleLinguistics;
1114
import com.yahoo.vespa.indexinglanguage.SimpleTestAdapter;
1215
import org.junit.Test;
@@ -146,6 +149,45 @@ public void requireThatVariablesReplaceOthersOutsideScript() {
146149
assertEquals(new IntegerFieldValue(9), adapter.getInputValue("out"));
147150
}
148151

152+
@Test
153+
// indexing: input expressions | for_each {
154+
// get_field myStructField
155+
// } | attribute
156+
@SuppressWarnings("unchecked")
157+
public void testGetStructField() {
158+
var structType = new StructDataType("myStruct");
159+
var stringField = new Field("stringField", DataType.STRING);
160+
var intField = new Field("intField", DataType.INT); // Nopt accesseed
161+
structType.addField(stringField);
162+
structType.addField(intField);
163+
164+
var adapter = new SimpleTestAdapter();
165+
adapter.createField(new Field("myInput", new ArrayDataType(structType)));
166+
adapter.createField(new Field("myOutput", new ArrayDataType(DataType.STRING)));
167+
168+
var array = new Array<Struct>(new ArrayDataType(structType));
169+
var struct1 = new Struct(structType);
170+
struct1.setFieldValue(stringField, "value1");
171+
struct1.setFieldValue(intField, 1);
172+
array.add(struct1);
173+
var struct2 = new Struct(structType);
174+
struct2.setFieldValue(stringField, "value2");
175+
struct2.setFieldValue(intField, 2);
176+
array.add(struct2);
177+
adapter.setValue("myInput", array);
178+
var statement =
179+
newStatement(new InputExpression("myInput"),
180+
new ForEachExpression(new StatementExpression(new GetFieldExpression("stringField"))),
181+
new AttributeExpression("myOutput"));
182+
statement.verify(adapter);
183+
statement.execute(adapter);
184+
185+
var result = (Array<StringFieldValue>)adapter.values.get("myOutput");
186+
assertEquals(2, result.size());
187+
assertEquals("value1", result.get(0).toString());
188+
assertEquals("value2", result.get(1).toString());
189+
}
190+
149191
@Test
150192
// input myString | lowercase | summary | index | split ";" | for_each {
151193
public void testSplitAndForEach() {

indexinglanguage/src/test/java/com/yahoo/vespa/indexinglanguage/expressions/SelectInputTestCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public void requireThatExpressionCanBeVerified() {
6060
assertVerifyThrows(adapter, newSelectInput(new AttributeExpression("my_int"), "my_str"),
6161
"Can not assign string to field 'my_int' which is int.");
6262
assertVerifyThrows(adapter, newSelectInput(new AttributeExpression("my_int"), "my_unknown"),
63-
"Field 'my_unknown' not found");
63+
"Input field 'my_unknown' not found.");
6464
}
6565

6666
@Test

0 commit comments

Comments
 (0)