summaryrefslogtreecommitdiffstats
path: root/document
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2022-10-06 23:55:41 +0200
committerJon Bratseth <bratseth@gmail.com>2022-10-06 23:55:41 +0200
commit8fcec55c82a1035dd17a59eb7bd1b1b65fb16f17 (patch)
tree4ea47e490d61b657601e338d70dbeb8977c2cdb8 /document
parent24c70d22397fad2c2d5d2e8b45d7da664283fd85 (diff)
Return X-Vespa-Ignored-Fields if fields were ignored
Diffstat (limited to 'document')
-rw-r--r--document/src/main/java/com/yahoo/document/Document.java3
-rw-r--r--document/src/main/java/com/yahoo/document/DocumentUpdate.java1
-rw-r--r--document/src/main/java/com/yahoo/document/datatypes/StructuredFieldValue.java16
-rw-r--r--document/src/main/java/com/yahoo/document/json/JsonFeedReader.java4
-rw-r--r--document/src/main/java/com/yahoo/document/json/JsonReader.java23
-rw-r--r--document/src/main/java/com/yahoo/document/json/ParsedDocumentOperation.java16
-rw-r--r--document/src/main/java/com/yahoo/document/json/TokenBuffer.java8
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/CompositeReader.java19
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/SingleValueReader.java36
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/StructReader.java38
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/VespaJsonDocumentReader.java58
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/WeightedSetReader.java3
-rw-r--r--document/src/main/java/com/yahoo/document/update/FieldUpdate.java8
-rw-r--r--document/src/main/java/com/yahoo/document/update/ValueUpdate.java10
-rw-r--r--document/src/main/java/com/yahoo/vespaxmlparser/FeedOperation.java3
-rw-r--r--document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java2
-rw-r--r--document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java74
17 files changed, 204 insertions, 118 deletions
diff --git a/document/src/main/java/com/yahoo/document/Document.java b/document/src/main/java/com/yahoo/document/Document.java
index 5937ba00292..760b9de0199 100644
--- a/document/src/main/java/com/yahoo/document/Document.java
+++ b/document/src/main/java/com/yahoo/document/Document.java
@@ -242,8 +242,7 @@ public class Document extends StructuredFieldValue {
@Override
public boolean equals(Object o) {
if (o == this) return true;
- if (!(o instanceof Document)) return false;
- Document other = (Document) o;
+ if (!(o instanceof Document other)) return false;
return (super.equals(o) && docId.equals(other.docId) &&
header.equals(other.header));
}
diff --git a/document/src/main/java/com/yahoo/document/DocumentUpdate.java b/document/src/main/java/com/yahoo/document/DocumentUpdate.java
index befabfb6c07..0731344cea9 100644
--- a/document/src/main/java/com/yahoo/document/DocumentUpdate.java
+++ b/document/src/main/java/com/yahoo/document/DocumentUpdate.java
@@ -443,4 +443,5 @@ public class DocumentUpdate extends DocumentOperation implements Iterable<FieldP
public Optional<Boolean> getOptionalCreateIfNonExistent() {
return Optional.ofNullable(createIfNonExistent);
}
+
}
diff --git a/document/src/main/java/com/yahoo/document/datatypes/StructuredFieldValue.java b/document/src/main/java/com/yahoo/document/datatypes/StructuredFieldValue.java
index 396a42b1237..6adbcda4772 100644
--- a/document/src/main/java/com/yahoo/document/datatypes/StructuredFieldValue.java
+++ b/document/src/main/java/com/yahoo/document/datatypes/StructuredFieldValue.java
@@ -46,14 +46,12 @@ public abstract class StructuredFieldValue extends CompositeFieldValue {
* and using the returned value to call {@link #getFieldValue(Field)}. If the named field does not exist, this
* method returns null.
*
- * @param fieldName The name of the field whose value to return.
- * @return The value of the field, or null.
+ * @param fieldName the name of the field whose value to return.
+ * @return the value of the field, or null if it is not declared in this, or has no value set
*/
public FieldValue getFieldValue(String fieldName) {
Field field = getField(fieldName);
- if (field == null) {
- return null;
- }
+ if (field == null) return null;
return getFieldValue(field);
}
@@ -61,10 +59,10 @@ public abstract class StructuredFieldValue extends CompositeFieldValue {
* Sets the value of the given field. The type of the value must match the type of this field, i.e.
* <pre>field.getDataType().getValueClass().isAssignableFrom(value.getClass())</pre> must be true.
*
- * @param field The field whose value to set.
- * @param value The value to set.
- * @return The previous value of the field, or null.
- * @throws IllegalArgumentException If the value is not compatible with the field.
+ * @param field the field whose value to set
+ * @param value the value to set
+ * @return the previous value of the field, or null
+ * @throws IllegalArgumentException if the value is not compatible with the field
*/
public FieldValue setFieldValue(Field field, FieldValue value) {
if (value == null) {
diff --git a/document/src/main/java/com/yahoo/document/json/JsonFeedReader.java b/document/src/main/java/com/yahoo/document/json/JsonFeedReader.java
index d0bd41c692c..4618a516f66 100644
--- a/document/src/main/java/com/yahoo/document/json/JsonFeedReader.java
+++ b/document/src/main/java/com/yahoo/document/json/JsonFeedReader.java
@@ -16,11 +16,9 @@ import com.yahoo.vespaxmlparser.RemoveFeedOperation;
import java.io.InputStream;
-
/**
* Facade between JsonReader and the FeedReader API.
*
- * <p>
* The feed reader will take ownership of the input stream and close it when the
* last parseable document has been read.
*
@@ -29,7 +27,7 @@ import java.io.InputStream;
public class JsonFeedReader implements FeedReader {
private final JsonReader reader;
- private InputStream stream;
+ private final InputStream stream;
private static final JsonFactory jsonFactory = new JsonFactoryBuilder().disable(JsonFactory.Feature.CANONICALIZE_FIELD_NAMES).build();
public JsonFeedReader(InputStream stream, DocumentTypeManager docMan) {
diff --git a/document/src/main/java/com/yahoo/document/json/JsonReader.java b/document/src/main/java/com/yahoo/document/json/JsonReader.java
index 94ce986fc81..f8de0fb959e 100644
--- a/document/src/main/java/com/yahoo/document/json/JsonReader.java
+++ b/document/src/main/java/com/yahoo/document/json/JsonReader.java
@@ -29,11 +29,6 @@ import static com.yahoo.document.json.readers.JsonParserHelpers.expectArrayStart
*/
public class JsonReader {
- public Optional<DocumentParseInfo> parseDocument() throws IOException {
- DocumentParser documentParser = new DocumentParser(parser);
- return documentParser.parse(Optional.empty());
- }
-
private final JsonParser parser;
private final DocumentTypeManager typeManager;
private ReaderState state = ReaderState.AT_START;
@@ -53,14 +48,19 @@ public class JsonReader {
}
}
+ public Optional<DocumentParseInfo> parseDocument() throws IOException {
+ DocumentParser documentParser = new DocumentParser(parser);
+ return documentParser.parse(Optional.empty());
+ }
+
/**
* Reads a single operation. The operation is not expected to be part of an array.
*
* @param operationType the type of operation (update or put)
- * @param docIdString document ID.
- * @return the document
+ * @param docIdString document ID
+ * @return the parsed document operation
*/
- public DocumentOperation readSingleDocument(DocumentOperationType operationType, String docIdString) {
+ public ParsedDocumentOperation readSingleDocument(DocumentOperationType operationType, String docIdString) {
DocumentId docId = new DocumentId(docIdString);
DocumentParseInfo documentParseInfo;
try {
@@ -72,9 +72,9 @@ public class JsonReader {
}
documentParseInfo.operationType = operationType;
VespaJsonDocumentReader vespaJsonDocumentReader = new VespaJsonDocumentReader(typeManager.getIgnoreUndefinedFields());
- DocumentOperation operation = vespaJsonDocumentReader.createDocumentOperation(
+ ParsedDocumentOperation operation = vespaJsonDocumentReader.createDocumentOperation(
getDocumentTypeFromString(documentParseInfo.documentId.getDocType(), typeManager), documentParseInfo);
- operation.setCondition(TestAndSetCondition.fromConditionString(documentParseInfo.condition));
+ operation.operation().setCondition(TestAndSetCondition.fromConditionString(documentParseInfo.condition));
return operation;
}
@@ -106,7 +106,7 @@ public class JsonReader {
VespaJsonDocumentReader vespaJsonDocumentReader = new VespaJsonDocumentReader(typeManager.getIgnoreUndefinedFields());
DocumentOperation operation = vespaJsonDocumentReader.createDocumentOperation(
getDocumentTypeFromString(documentParseInfo.get().documentId.getDocType(), typeManager),
- documentParseInfo.get());
+ documentParseInfo.get()).operation();
operation.setCondition(TestAndSetCondition.fromConditionString(documentParseInfo.get().condition));
return operation;
}
@@ -132,4 +132,5 @@ public class JsonReader {
throw new IllegalArgumentException(e);
}
}
+
}
diff --git a/document/src/main/java/com/yahoo/document/json/ParsedDocumentOperation.java b/document/src/main/java/com/yahoo/document/json/ParsedDocumentOperation.java
new file mode 100644
index 00000000000..eb171983cf1
--- /dev/null
+++ b/document/src/main/java/com/yahoo/document/json/ParsedDocumentOperation.java
@@ -0,0 +1,16 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.document.json;
+
+import com.yahoo.document.DocumentOperation;
+
+/**
+ * The result of JSON parsing a single document operation
+ *
+ * @param operation
+ * the parsed operation
+ * @param fullyApplied
+ * true if all the JSON content could be applied,
+ * false if some (or all) of the fields were not poresent in this document and was ignored
+ */
+public record ParsedDocumentOperation(DocumentOperation operation, boolean fullyApplied) {
+}
diff --git a/document/src/main/java/com/yahoo/document/json/TokenBuffer.java b/document/src/main/java/com/yahoo/document/json/TokenBuffer.java
index 4fff9c45ea5..e6d2021f90b 100644
--- a/document/src/main/java/com/yahoo/document/json/TokenBuffer.java
+++ b/document/src/main/java/com/yahoo/document/json/TokenBuffer.java
@@ -213,4 +213,12 @@ public class TokenBuffer {
}
return toReturn;
}
+
+ public void skipToRelativeNesting(int relativeNesting) {
+ int initialNesting = nesting();
+ do {
+ next();
+ } while ( nesting() > initialNesting + relativeNesting);
+ }
+
}
diff --git a/document/src/main/java/com/yahoo/document/json/readers/CompositeReader.java b/document/src/main/java/com/yahoo/document/json/readers/CompositeReader.java
index a2dd91b90a0..6cbc1c1e0b1 100644
--- a/document/src/main/java/com/yahoo/document/json/readers/CompositeReader.java
+++ b/document/src/main/java/com/yahoo/document/json/readers/CompositeReader.java
@@ -19,14 +19,22 @@ import static com.yahoo.document.json.readers.WeightedSetReader.fillWeightedSet;
public class CompositeReader {
- // TODO: reateComposite is extremely similar to add/remove, refactor
+ public static boolean populateComposite(TokenBuffer buffer, FieldValue fieldValue, boolean ignoreUndefinedFields) {
+ boolean fullyApplied = populateComposite(buffer.currentToken(), buffer, fieldValue, ignoreUndefinedFields);
+ expectCompositeEnd(buffer.currentToken());
+ return fullyApplied;
+ }
+
+ // TODO: createComposite is extremely similar to add/remove, refactor
// yes, this suppresswarnings ugliness is by intention, the code relies on the contracts in the builders
@SuppressWarnings({ "cast", "rawtypes" })
- public static void populateComposite(TokenBuffer buffer, FieldValue fieldValue, boolean ignoreUndefinedFields) {
- JsonToken token = buffer.currentToken();
+ private static boolean populateComposite(JsonToken token, TokenBuffer buffer, FieldValue fieldValue,
+ boolean ignoreUndefinedFields) {
if ((token != JsonToken.START_OBJECT) && (token != JsonToken.START_ARRAY)) {
throw new IllegalArgumentException("Expected '[' or '{'. Got '" + token + "'.");
}
+
+ boolean fullyApplied = true;
if (fieldValue instanceof CollectionFieldValue) {
DataType valueType = ((CollectionFieldValue) fieldValue).getDataType().getNestedType();
if (fieldValue instanceof WeightedSet) {
@@ -39,13 +47,14 @@ public class CompositeReader {
} else if (PositionDataType.INSTANCE.equals(fieldValue.getDataType())) {
GeoPositionReader.fillGeoPosition(buffer, fieldValue);
} else if (fieldValue instanceof StructuredFieldValue) {
- StructReader.fillStruct(buffer, (StructuredFieldValue) fieldValue, ignoreUndefinedFields);
+ fullyApplied = StructReader.fillStruct(buffer, (StructuredFieldValue) fieldValue, ignoreUndefinedFields);
} else if (fieldValue instanceof TensorFieldValue) {
TensorReader.fillTensor(buffer, (TensorFieldValue) fieldValue);
} else {
throw new IllegalArgumentException("Expected a " + fieldValue.getClass().getName() + " but got an " +
(token == JsonToken.START_OBJECT ? "object" : "array" ));
}
- expectCompositeEnd(buffer.currentToken());
+ return fullyApplied;
}
+
}
diff --git a/document/src/main/java/com/yahoo/document/json/readers/SingleValueReader.java b/document/src/main/java/com/yahoo/document/json/readers/SingleValueReader.java
index 1747e739bbf..3ae82676fa8 100644
--- a/document/src/main/java/com/yahoo/document/json/readers/SingleValueReader.java
+++ b/document/src/main/java/com/yahoo/document/json/readers/SingleValueReader.java
@@ -53,31 +53,17 @@ public class SingleValueReader {
@SuppressWarnings("rawtypes")
public static ValueUpdate readSingleUpdate(TokenBuffer buffer, DataType expectedType, String action, boolean ignoreUndefinedFields) {
- ValueUpdate update;
-
- switch (action) {
- case UPDATE_ASSIGN:
- update = (buffer.currentToken() == JsonToken.VALUE_NULL)
- ? ValueUpdate.createClear()
- : ValueUpdate.createAssign(readSingleValue(buffer, expectedType, ignoreUndefinedFields));
- break;
+ return switch (action) {
+ case UPDATE_ASSIGN -> (buffer.currentToken() == JsonToken.VALUE_NULL)
+ ? ValueUpdate.createClear()
+ : ValueUpdate.createAssign(readSingleValue(buffer, expectedType, ignoreUndefinedFields));
// double is silly, but it's what is used internally anyway
- case UPDATE_INCREMENT:
- update = ValueUpdate.createIncrement(Double.valueOf(buffer.currentText()));
- break;
- case UPDATE_DECREMENT:
- update = ValueUpdate.createDecrement(Double.valueOf(buffer.currentText()));
- break;
- case UPDATE_MULTIPLY:
- update = ValueUpdate.createMultiply(Double.valueOf(buffer.currentText()));
- break;
- case UPDATE_DIVIDE:
- update = ValueUpdate.createDivide(Double.valueOf(buffer.currentText()));
- break;
- default:
- throw new IllegalArgumentException("Operation '" + buffer.currentName() + "' not implemented.");
- }
- return update;
+ case UPDATE_INCREMENT -> ValueUpdate.createIncrement(Double.valueOf(buffer.currentText()));
+ case UPDATE_DECREMENT -> ValueUpdate.createDecrement(Double.valueOf(buffer.currentText()));
+ case UPDATE_MULTIPLY -> ValueUpdate.createMultiply(Double.valueOf(buffer.currentText()));
+ case UPDATE_DIVIDE -> ValueUpdate.createDivide(Double.valueOf(buffer.currentText()));
+ default -> throw new IllegalArgumentException("Operation '" + buffer.currentName() + "' not implemented.");
+ };
}
public static Matcher matchArithmeticOperation(String expression) {
@@ -94,7 +80,7 @@ public class SingleValueReader {
}
}
- private static FieldValue readReferenceFieldValue(final String refText, DataType expectedType) {
+ private static FieldValue readReferenceFieldValue(String refText, DataType expectedType) {
final FieldValue value = expectedType.createFieldValue();
if (!refText.isEmpty()) {
value.assign(new DocumentId(refText));
diff --git a/document/src/main/java/com/yahoo/document/json/readers/StructReader.java b/document/src/main/java/com/yahoo/document/json/readers/StructReader.java
index b9eaf0d8ec6..b944d273a72 100644
--- a/document/src/main/java/com/yahoo/document/json/readers/StructReader.java
+++ b/document/src/main/java/com/yahoo/document/json/readers/StructReader.java
@@ -12,15 +12,32 @@ import static com.yahoo.document.json.readers.SingleValueReader.readSingleValue;
public class StructReader {
- public static void fillStruct(TokenBuffer buffer, StructuredFieldValue parent, boolean ignoreUndefinedFields) {
+ /**
+ * Fills this struct.
+ *
+ * @return true if all this was applied and false if it was ignored because the field does not exist
+ */
+ public static boolean fillStruct(TokenBuffer buffer, StructuredFieldValue parent, boolean ignoreUndefinedFields) {
// do note the order of initializing initNesting and token is relevant for empty docs
- int initNesting = buffer.nesting();
+ int initialNesting = buffer.nesting();
buffer.next();
- while (buffer.nesting() >= initNesting) {
- Field field = getField(buffer, parent, ignoreUndefinedFields);
+ boolean fullyApplied = true;
+ while (buffer.nesting() >= initialNesting) {
+ Field field = parent.getField(buffer.currentName());
+ if (field == null) {
+ if (! ignoreUndefinedFields)
+ throw new IllegalArgumentException("No field '" + buffer.currentName() + "' in the structure of type '" +
+ parent.getDataType().getDataTypeName() +
+ "', which has the fields: " + parent.getDataType().getFields());
+
+ buffer.skipToRelativeNesting(0);
+ fullyApplied = false;
+ continue;
+ }
+
try {
- if (field != null && buffer.currentToken() != JsonToken.VALUE_NULL) {
+ if (buffer.currentToken() != JsonToken.VALUE_NULL) {
FieldValue v = readSingleValue(buffer, field.getDataType(), ignoreUndefinedFields);
parent.setFieldValue(field, v);
}
@@ -29,16 +46,7 @@ public class StructReader {
throw new JsonReaderException(field, e);
}
}
- }
-
- private static Field getField(TokenBuffer buffer, StructuredFieldValue parent, boolean ignoreUndefinedFields) {
- Field field = parent.getField(buffer.currentName());
- if (field == null && ! ignoreUndefinedFields) {
- throw new IllegalArgumentException("No field '" + buffer.currentName() + "' in the structure of type '" +
- parent.getDataType().getDataTypeName() +
- "', which has the fields: " + parent.getDataType().getFields());
- }
- return field;
+ return fullyApplied;
}
}
diff --git a/document/src/main/java/com/yahoo/document/json/readers/VespaJsonDocumentReader.java b/document/src/main/java/com/yahoo/document/json/readers/VespaJsonDocumentReader.java
index 7bc462ec73a..22a9e7a1119 100644
--- a/document/src/main/java/com/yahoo/document/json/readers/VespaJsonDocumentReader.java
+++ b/document/src/main/java/com/yahoo/document/json/readers/VespaJsonDocumentReader.java
@@ -17,6 +17,7 @@ import com.yahoo.document.fieldpathupdate.AssignFieldPathUpdate;
import com.yahoo.document.fieldpathupdate.FieldPathUpdate;
import com.yahoo.document.fieldpathupdate.RemoveFieldPathUpdate;
import com.yahoo.document.json.JsonReaderException;
+import com.yahoo.document.json.ParsedDocumentOperation;
import com.yahoo.document.json.TokenBuffer;
import com.yahoo.document.update.FieldUpdate;
@@ -50,67 +51,65 @@ public class VespaJsonDocumentReader {
this.ignoreUndefinedFields = ignoreUndefinedFields;
}
- public DocumentOperation createDocumentOperation(DocumentType documentType, DocumentParseInfo documentParseInfo) {
+ public ParsedDocumentOperation createDocumentOperation(DocumentType documentType, DocumentParseInfo documentParseInfo) {
final DocumentOperation documentOperation;
+ boolean fullyApplied = true;
try {
switch (documentParseInfo.operationType) {
- case PUT:
+ case PUT -> {
documentOperation = new DocumentPut(new Document(documentType, documentParseInfo.documentId));
- readPut(documentParseInfo.fieldsBuffer, (DocumentPut) documentOperation);
+ fullyApplied = readPut(documentParseInfo.fieldsBuffer, (DocumentPut) documentOperation);
verifyEndState(documentParseInfo.fieldsBuffer, JsonToken.END_OBJECT);
- break;
- case REMOVE:
- documentOperation = new DocumentRemove(documentParseInfo.documentId);
- break;
- case UPDATE:
+ }
+ case REMOVE -> documentOperation = new DocumentRemove(documentParseInfo.documentId);
+ case UPDATE -> {
documentOperation = new DocumentUpdate(documentType, documentParseInfo.documentId);
- readUpdate(documentParseInfo.fieldsBuffer, (DocumentUpdate) documentOperation);
+ fullyApplied = readUpdate(documentParseInfo.fieldsBuffer, (DocumentUpdate) documentOperation);
verifyEndState(documentParseInfo.fieldsBuffer, JsonToken.END_OBJECT);
- break;
- default:
- throw new IllegalStateException("Implementation out of sync with itself. This is a bug.");
+ }
+ default -> throw new IllegalStateException("Implementation out of sync with itself. This is a bug.");
}
} catch (JsonReaderException e) {
throw JsonReaderException.addDocId(e, documentParseInfo.documentId);
}
if (documentParseInfo.create.isPresent()) {
- if (! ( documentOperation instanceof DocumentUpdate)) {
+ if (! (documentOperation instanceof DocumentUpdate update)) {
throw new IllegalArgumentException("Could not set create flag on non update operation.");
}
- DocumentUpdate update = (DocumentUpdate) documentOperation;
update.setCreateIfNonExistent(documentParseInfo.create.get());
}
- return documentOperation;
+ return new ParsedDocumentOperation(documentOperation, fullyApplied);
}
// Exposed for unit testing...
- public void readPut(TokenBuffer buffer, DocumentPut put) {
+ public boolean readPut(TokenBuffer buffer, DocumentPut put) {
try {
if (buffer.isEmpty()) // no "fields" map
throw new IllegalArgumentException(put + " is missing a 'fields' map");
- populateComposite(buffer, put.getDocument(), ignoreUndefinedFields);
+ return populateComposite(buffer, put.getDocument(), ignoreUndefinedFields);
} catch (JsonReaderException e) {
throw JsonReaderException.addDocId(e, put.getId());
}
}
// Exposed for unit testing...
- public void readUpdate(TokenBuffer buffer, DocumentUpdate update) {
+ public boolean readUpdate(TokenBuffer buffer, DocumentUpdate update) {
if (buffer.isEmpty())
- throw new IllegalArgumentException("update of document " + update.getId() + " is missing a 'fields' map");
+ throw new IllegalArgumentException("Update of document " + update.getId() + " is missing a 'fields' map");
expectObjectStart(buffer.currentToken());
int localNesting = buffer.nesting();
buffer.next();
+ boolean fullyApplied = true;
while (localNesting <= buffer.nesting()) {
expectObjectStart(buffer.currentToken());
String fieldName = buffer.currentName();
try {
if (isFieldPath(fieldName)) {
- addFieldPathUpdates(update, buffer, fieldName);
+ fullyApplied &= addFieldPathUpdates(update, buffer, fieldName);
} else {
- addFieldUpdates(update, buffer, fieldName);
+ fullyApplied &= addFieldUpdates(update, buffer, fieldName);
}
expectObjectEnd(buffer.currentToken());
}
@@ -119,12 +118,18 @@ public class VespaJsonDocumentReader {
}
buffer.next();
}
+ return fullyApplied;
}
- private void addFieldUpdates(DocumentUpdate update, TokenBuffer buffer, String fieldName) {
+ private boolean addFieldUpdates(DocumentUpdate update, TokenBuffer buffer, String fieldName) {
Field field = update.getType().getField(fieldName);
- if (field == null)
- throw new IllegalArgumentException("No field named '" + fieldName + "' in " + update.getType());
+ if (field == null) {
+ if (! ignoreUndefinedFields)
+ throw new IllegalArgumentException("No field named '" + fieldName + "' in " + update.getType());
+ buffer.skipToRelativeNesting(-1);
+ return false;
+ }
+
int localNesting = buffer.nesting();
FieldUpdate fieldUpdate = FieldUpdate.create(field);
@@ -158,9 +163,10 @@ public class VespaJsonDocumentReader {
buffer.next();
}
update.addFieldUpdate(fieldUpdate);
+ return true;
}
- private void addFieldPathUpdates(DocumentUpdate update, TokenBuffer buffer, String fieldPath) {
+ private boolean addFieldPathUpdates(DocumentUpdate update, TokenBuffer buffer, String fieldPath) {
int localNesting = buffer.nesting();
buffer.next();
@@ -185,6 +191,7 @@ public class VespaJsonDocumentReader {
update.addFieldPathUpdate(fieldPathUpdate);
buffer.next();
}
+ return true; // TODO: Track fullyApplied for fieldPath updates
}
private AssignFieldPathUpdate readAssignFieldPathUpdate(DocumentType documentType, String fieldPath, TokenBuffer buffer) {
@@ -230,4 +237,5 @@ public class VespaJsonDocumentReader {
Preconditions.checkState(buffer.next() == null, "Dangling data at end of operation");
Preconditions.checkState(buffer.size() == 0, "Dangling data at end of operation");
}
+
}
diff --git a/document/src/main/java/com/yahoo/document/json/readers/WeightedSetReader.java b/document/src/main/java/com/yahoo/document/json/readers/WeightedSetReader.java
index c9af929735e..7a9921498ef 100644
--- a/document/src/main/java/com/yahoo/document/json/readers/WeightedSetReader.java
+++ b/document/src/main/java/com/yahoo/document/json/readers/WeightedSetReader.java
@@ -10,12 +10,14 @@ import static com.yahoo.document.json.readers.JsonParserHelpers.expectObjectStar
public class WeightedSetReader {
+
public static void fillWeightedSet(TokenBuffer buffer, DataType valueType, @SuppressWarnings("rawtypes") WeightedSet weightedSet) {
int initNesting = buffer.nesting();
expectObjectStart(buffer.currentToken());
buffer.next();
iterateThroughWeightedSet(buffer, initNesting, valueType, weightedSet);
}
+
public static void fillWeightedSetUpdate(TokenBuffer buffer, int initNesting, DataType valueType, @SuppressWarnings("rawtypes") WeightedSet weightedSet) {
iterateThroughWeightedSet(buffer, initNesting, valueType, weightedSet);
}
@@ -29,4 +31,5 @@ public class WeightedSetReader {
buffer.next();
}
}
+
}
diff --git a/document/src/main/java/com/yahoo/document/update/FieldUpdate.java b/document/src/main/java/com/yahoo/document/update/FieldUpdate.java
index b2992bf4988..fc4a293f0fb 100644
--- a/document/src/main/java/com/yahoo/document/update/FieldUpdate.java
+++ b/document/src/main/java/com/yahoo/document/update/FieldUpdate.java
@@ -46,10 +46,10 @@ import java.util.List;
* type - any name/value pair which existing in an updatable structure can be addressed by creating the Fields as
* needed. For example:
* <pre>
- * FieldUpdate field=FieldUpdate.createIncrement(new Field("myattribute",DataType.INT),130);
+ * FieldUpdate field = FieldUpdate.createIncrement(new Field("myattribute",DataType.INT),130);
* </pre>
*
- * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @author Einar M R Rosenvinge
* @see com.yahoo.document.update.ValueUpdate
* @see com.yahoo.document.DocumentUpdate
*/
@@ -135,7 +135,7 @@ public class FieldUpdate {
* @throws IllegalArgumentException if the data type of the value update is not equal to the data type of this field
*/
public FieldUpdate addValueUpdate(ValueUpdate valueUpdate) {
- valueUpdate.checkCompatibility(field.getDataType()); //will throw exception
+ valueUpdate.checkCompatibility(field.getDataType()); // will throw exception
valueUpdates.add(valueUpdate);
return this;
}
@@ -149,7 +149,7 @@ public class FieldUpdate {
* @throws IllegalArgumentException if the data type of the value update is not equal to the data type of this field
*/
public FieldUpdate addValueUpdate(int index, ValueUpdate valueUpdate) {
- valueUpdate.checkCompatibility(field.getDataType()); //will throw exception
+ valueUpdate.checkCompatibility(field.getDataType()); // will throw exception
valueUpdates.add(index, valueUpdate);
return this;
}
diff --git a/document/src/main/java/com/yahoo/document/update/ValueUpdate.java b/document/src/main/java/com/yahoo/document/update/ValueUpdate.java
index 3aa728ca5b2..74f5ba9d30a 100644
--- a/document/src/main/java/com/yahoo/document/update/ValueUpdate.java
+++ b/document/src/main/java/com/yahoo/document/update/ValueUpdate.java
@@ -13,7 +13,7 @@ import java.util.List;
/**
* A value update represents some action to perform to a value.
*
- * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @author Einar M R Rosenvinge
* @see com.yahoo.document.update.FieldUpdate
* @see com.yahoo.document.DocumentUpdate
* @see AddValueUpdate
@@ -31,11 +31,7 @@ public abstract class ValueUpdate<T extends FieldValue> {
this.valueUpdateClassID = valueUpdateClassID;
}
- /**
- * Returns the valueUpdateClassID of this value update.
- *
- * @return the valueUpdateClassID of this ValueUpdate
- */
+ /** Returns the valueUpdateClassID of this value update. */
public ValueUpdateClassID getValueUpdateClassID() {
return valueUpdateClassID;
}
@@ -46,7 +42,7 @@ public abstract class ValueUpdate<T extends FieldValue> {
@Override
public boolean equals(Object o) {
- return o instanceof ValueUpdate && valueUpdateClassID == ((ValueUpdate) o).valueUpdateClassID;
+ return o instanceof ValueUpdate && valueUpdateClassID == ((ValueUpdate<?>) o).valueUpdateClassID;
}
@Override
diff --git a/document/src/main/java/com/yahoo/vespaxmlparser/FeedOperation.java b/document/src/main/java/com/yahoo/vespaxmlparser/FeedOperation.java
index 6b084a55309..69d851b09bc 100644
--- a/document/src/main/java/com/yahoo/vespaxmlparser/FeedOperation.java
+++ b/document/src/main/java/com/yahoo/vespaxmlparser/FeedOperation.java
@@ -7,7 +7,9 @@ import com.yahoo.document.DocumentUpdate;
import com.yahoo.document.TestAndSetCondition;
public class FeedOperation {
+
public enum Type {DOCUMENT, REMOVE, UPDATE, INVALID}
+
public static final FeedOperation INVALID = new FeedOperation(Type.INVALID);
private Type type;
@@ -36,4 +38,5 @@ public class FeedOperation {
" testandset=" + getCondition() +
'}';
}
+
} \ No newline at end of file
diff --git a/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java b/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
index e396fe8912b..7bdb526bb1c 100644
--- a/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
+++ b/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
@@ -96,7 +96,7 @@ public class DocumentUpdateJsonSerializerTest {
private static DocumentUpdate jsonToDocumentUpdate(String jsonDoc, String docId) {
final InputStream rawDoc = new ByteArrayInputStream(Utf8.toBytes(jsonDoc));
JsonReader reader = new JsonReader(types, rawDoc, parserFactory);
- return (DocumentUpdate) reader.readSingleDocument(DocumentOperationType.UPDATE, docId);
+ return (DocumentUpdate) reader.readSingleDocument(DocumentOperationType.UPDATE, docId).operation();
}
private static String documentUpdateToJson(DocumentUpdate update) {
diff --git a/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java b/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
index 441f1fd28ea..b86d05f2d3d 100644
--- a/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
+++ b/document/src/test/java/com/yahoo/document/json/JsonReaderTestCase.java
@@ -78,6 +78,7 @@ import static com.yahoo.document.json.readers.SingleValueReader.UPDATE_INCREMENT
import static com.yahoo.document.json.readers.SingleValueReader.UPDATE_MULTIPLY;
import static com.yahoo.test.json.JsonTestHelper.inputJson;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -103,6 +104,8 @@ public class JsonReaderTestCase {
DocumentType x = new DocumentType("smoke");
x.addField(new Field("something", DataType.STRING));
x.addField(new Field("nalle", DataType.STRING));
+ x.addField(new Field("field1", DataType.STRING));
+ x.addField(new Field("field2", DataType.STRING));
x.addField(new Field("int1", DataType.INT));
x.addField(new Field("flag", DataType.BOOL));
types.registerDocumentType(x);
@@ -216,7 +219,7 @@ public class JsonReaderTestCase {
" }",
"}"));
DocumentPut put = (DocumentPut) r.readSingleDocument(DocumentOperationType.PUT,
- "id:unittest:smoke::doc1");
+ "id:unittest:smoke::doc1").operation();
smokeTestDoc(put.getDocument());
}
@@ -226,7 +229,7 @@ public class JsonReaderTestCase {
" 'fields': {",
" 'something': {",
" 'assign': 'orOther' }}}"));
- DocumentUpdate doc = (DocumentUpdate) r.readSingleDocument(DocumentOperationType.UPDATE, "id:unittest:smoke::whee");
+ DocumentUpdate doc = (DocumentUpdate) r.readSingleDocument(DocumentOperationType.UPDATE, "id:unittest:smoke::whee").operation();
FieldUpdate f = doc.getFieldUpdate("something");
assertEquals(1, f.size());
assertTrue(f.getValueUpdate(0) instanceof AssignValueUpdate);
@@ -238,7 +241,7 @@ public class JsonReaderTestCase {
" 'fields': {",
" 'int1': {",
" 'assign': null }}}"));
- DocumentUpdate doc = (DocumentUpdate) r.readSingleDocument(DocumentOperationType.UPDATE, "id:unittest:smoke::whee");
+ DocumentUpdate doc = (DocumentUpdate) r.readSingleDocument(DocumentOperationType.UPDATE, "id:unittest:smoke::whee").operation();
FieldUpdate f = doc.getFieldUpdate("int1");
assertEquals(1, f.size());
assertTrue(f.getValueUpdate(0) instanceof ClearValueUpdate);
@@ -1006,17 +1009,65 @@ public class JsonReaderTestCase {
}
@Test
- public void nonExistingFieldCanBeIgnored() throws IOException{
+ public void nonExistingFieldsCanBeIgnoredInPut() throws IOException{
JsonReader r = createReader(inputJson(
- "{ 'put': 'id:unittest:smoke::whee',",
+ "{ ",
+ " 'put': 'id:unittest:smoke::doc1',",
" 'fields': {",
- " 'smething': 'smoketest',",
- " 'nalle': 'bamse' }}"));
+ " 'nonexisting1': 'ignored value',",
+ " 'field1': 'value1',",
+ " 'nonexisting2': {",
+ " 'blocks':{",
+ " 'a':[2.0,3.0],",
+ " 'b':[4.0,5.0]",
+ " }",
+ " },",
+ " 'field2': 'value2',",
+ " 'nonexisting3': 'ignored value'",
+ " }",
+ "}"));
DocumentParseInfo parseInfo = r.parseDocument().get();
DocumentType docType = r.readDocumentType(parseInfo.documentId);
DocumentPut put = new DocumentPut(new Document(docType, parseInfo.documentId));
+ boolean fullyApplied = new VespaJsonDocumentReader(true).readPut(parseInfo.fieldsBuffer, put);
+ assertFalse(fullyApplied);
+ assertNull(put.getDocument().getField("nonexisting1"));
+ assertEquals("value1", put.getDocument().getFieldValue("field1").toString());
+ assertNull(put.getDocument().getField("nonexisting2"));
+ assertEquals("value2", put.getDocument().getFieldValue("field2").toString());
+ assertNull(put.getDocument().getField("nonexisting3"));
+ }
- new VespaJsonDocumentReader(true).readPut(parseInfo.fieldsBuffer, put);
+ @Test
+ public void nonExistingFieldsCanBeIgnoredInUpdate() throws IOException{
+ JsonReader r = createReader(inputJson(
+ "{ ",
+ " 'update': 'id:unittest:smoke::doc1',",
+ " 'fields': {",
+ " 'nonexisting1': { 'assign': 'ignored value' },",
+ " 'field1': { 'assign': 'value1' },",
+// " 'nonexisting2': { " +
+// " 'assign': {",
+// " 'blocks': {",
+// " 'a':[2.0,3.0],",
+// " 'b':[4.0,5.0]",
+// " }",
+// " }",
+// " },",
+ " 'field2': { 'assign': 'value2' }",
+// " 'nonexisting3': { 'assign': 'ignored value' }",
+ " }",
+ "}"));
+ DocumentParseInfo parseInfo = r.parseDocument().get();
+ DocumentType docType = r.readDocumentType(parseInfo.documentId);
+ DocumentUpdate update = new DocumentUpdate(docType, parseInfo.documentId);
+ boolean fullyApplied = new VespaJsonDocumentReader(true).readUpdate(parseInfo.fieldsBuffer, update);
+ assertFalse(fullyApplied);
+ assertNull(update.getFieldUpdate("nonexisting1"));
+ assertEquals("value1", update.getFieldUpdate("field1").getValueUpdates().get(0).getValue().getWrappedValue().toString());
+ assertNull(update.getFieldUpdate("nonexisting2"));
+ assertEquals("value2", update.getFieldUpdate("field2").getValueUpdates().get(0).getValue().getWrappedValue().toString());
+ assertNull(update.getFieldUpdate("nonexisting3"));
}
@Test
@@ -1044,7 +1095,8 @@ public class JsonReaderTestCase {
DocumentParseInfo parseInfo = r.parseDocument().get();
DocumentType docType = r.readDocumentType(parseInfo.documentId);
DocumentPut put = new DocumentPut(new Document(docType, parseInfo.documentId));
- new VespaJsonDocumentReader(false).readPut(parseInfo.fieldsBuffer, put);
+ boolean fullyApplied = new VespaJsonDocumentReader(false).readPut(parseInfo.fieldsBuffer, put);
+ assertTrue(fullyApplied);
smokeTestDoc(put.getDocument());
}
@@ -1271,7 +1323,7 @@ public class JsonReaderTestCase {
fail("Expected exception");
}
catch (IllegalArgumentException e) {
- assertEquals("update of document id:unittest:smoke::whee is missing a 'fields' map", e.getMessage());
+ assertEquals("Update of document id:unittest:smoke::whee is missing a 'fields' map", e.getMessage());
}
}
@@ -1288,7 +1340,7 @@ public class JsonReaderTestCase {
" }",
"}"));
DocumentPut put = (DocumentPut) r.readSingleDocument(DocumentOperationType.PUT,
- "id:unittest:testnull::doc1");
+ "id:unittest:testnull::doc1").operation();
Document doc = put.getDocument();
assertFieldValueNull(doc, "intfield");
assertFieldValueNull(doc, "stringfield");