diff options
-rw-r--r-- | document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java | 32 | ||||
-rw-r--r-- | document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java | 30 |
2 files changed, 59 insertions, 3 deletions
diff --git a/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java b/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java index 0cedda7c4f0..9e2759e1590 100644 --- a/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java +++ b/document/src/main/java/com/yahoo/document/select/rule/AttributeNode.java @@ -7,6 +7,7 @@ import com.yahoo.document.Document; import com.yahoo.document.DocumentGet; import com.yahoo.document.DocumentPut; import com.yahoo.document.DocumentRemove; +import com.yahoo.document.DocumentType; import com.yahoo.document.DocumentUpdate; import com.yahoo.document.FieldPath; import com.yahoo.document.datatypes.FieldPathIteratorHandler; @@ -131,10 +132,37 @@ public class AttributeNode implements ExpressionNode { throw new IllegalStateException("Function '" + function + "' is not supported."); } - private static Object evaluateFieldPath(String fieldPth, Object value) { + private static boolean looksLikeComplexFieldPath(String path) { + for (int i = 0; i < path.length(); ++i) { + switch (path.charAt(i)) { + case '.': + case '{': + case '[': + return true; + } + } + return false; + } + + private static boolean isSimpleImportedField(String path, DocumentType documentType) { + if (looksLikeComplexFieldPath(path)) { + return false; + } + return documentType.hasImportedField(path); + } + + private static Object evaluateFieldPath(String fieldPathStr, Object value) { if (value instanceof DocumentPut) { final Document doc = ((DocumentPut) value).getDocument(); - FieldPath fieldPath = doc.getDataType().buildFieldPath(fieldPth); + if (isSimpleImportedField(fieldPathStr, doc.getDataType())) { + // Imported fields can only be meaningfully evaluated in the backend, so we + // explicitly treat them as if they are valid fields with missing values. This + // will be treated the same as if it's a normal field by the selection operators. + // This avoids any awkward interaction with Invalid values or having to + // augment the FieldPath code with knowledge of imported fields. + return null; + } + FieldPath fieldPath = doc.getDataType().buildFieldPath(fieldPathStr); IteratorHandler handler = new IteratorHandler(); doc.iterateNested(fieldPath, 0, handler); if (handler.values.isEmpty()) { diff --git a/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java b/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java index 0ff0bd81d90..05cfbc301cc 100644 --- a/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java +++ b/document/src/test/java/com/yahoo/document/select/DocumentSelectorTestCase.java @@ -8,10 +8,13 @@ import com.yahoo.document.select.parser.ParseException; import com.yahoo.document.select.parser.TokenMgrException; import com.yahoo.yolean.Exceptions; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; @@ -26,11 +29,15 @@ import static org.junit.Assert.fail; */ public class DocumentSelectorTestCase { + @Rule + public final ExpectedException exceptionRule = ExpectedException.none(); + private static DocumentTypeManager manager = new DocumentTypeManager(); @Before public void setUp() { - DocumentType type = new DocumentType("test"); + var importedFields = new HashSet<>(List.of("my_imported_field")); + DocumentType type = new DocumentType("test", importedFields); type.addHeaderField("hint", DataType.INT); type.addHeaderField("hfloat", DataType.FLOAT); type.addHeaderField("hstring", DataType.STRING); @@ -697,6 +704,27 @@ public class DocumentSelectorTestCase { } @Test + public void imported_field_references_are_treated_as_valid_field_with_missing_value() throws ParseException { + var documents = createDocs(); + assertEquals(Result.TRUE, evaluate("test.my_imported_field == null", documents.get(0))); + assertEquals(Result.FALSE, evaluate("test.my_imported_field != null", documents.get(0))); + assertEquals(Result.FALSE, evaluate("test.my_imported_field", documents.get(0))); + // Only (in)equality operators are well defined for null values; everything else becomes Invalid. + assertEquals(Result.INVALID, evaluate("test.my_imported_field > 0", documents.get(0))); + } + + @Test + public void imported_fields_only_supported_for_simple_expressions() throws ParseException { + exceptionRule.expect(IllegalArgumentException.class); + // TODO we should probably handle this case specially and give a better exception message + exceptionRule.expectMessage("Field 'my_imported_field' not found in type datatype test"); + + var documents = createDocs(); + // Nested field access is NOT considered a simple expression. + evaluate("test.my_imported_field.foo", documents.get(0)); + } + + @Test public void testTicket1769674() { assertParseError("music.uri=\"junk", "Lexical error at line -1, column 17. Encountered: <EOF> after : \"\\\"junk\""); |