diff options
author | Jon Bratseth <bratseth@gmail.com> | 2022-10-06 23:55:41 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@gmail.com> | 2022-10-06 23:55:41 +0200 |
commit | 8fcec55c82a1035dd17a59eb7bd1b1b65fb16f17 (patch) | |
tree | 4ea47e490d61b657601e338d70dbeb8977c2cdb8 /document | |
parent | 24c70d22397fad2c2d5d2e8b45d7da664283fd85 (diff) |
Return X-Vespa-Ignored-Fields if fields were ignored
Diffstat (limited to 'document')
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"); |