aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfreva <valerijf@yahoo-inc.com>2017-02-17 15:31:29 +0100
committerfreva <valerijf@yahoo-inc.com>2017-02-17 15:31:29 +0100
commita50b07f39599001772d3f8075425151622b8306a (patch)
tree9bfaf0bd80d7a9932b5c87c441979099fe788826
parent9859b67db5f08cc25d10a6960173d15c1fca4465 (diff)
Added JSON arithmetic fieldpath operations
-rw-r--r--document/src/main/java/com/yahoo/document/fieldpathupdate/AddFieldPathUpdate.java4
-rw-r--r--document/src/main/java/com/yahoo/document/fieldpathupdate/AssignFieldPathUpdate.java4
-rw-r--r--document/src/main/java/com/yahoo/document/fieldpathupdate/FieldPathUpdate.java12
-rw-r--r--document/src/main/java/com/yahoo/document/fieldpathupdate/RemoveFieldPathUpdate.java4
-rw-r--r--document/src/main/java/com/yahoo/document/json/DocumentUpdateJsonSerializer.java23
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/SingleValueReader.java26
-rw-r--r--document/src/main/java/com/yahoo/document/json/readers/VespaJsonDocumentReader.java44
-rw-r--r--document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java25
8 files changed, 98 insertions, 44 deletions
diff --git a/document/src/main/java/com/yahoo/document/fieldpathupdate/AddFieldPathUpdate.java b/document/src/main/java/com/yahoo/document/fieldpathupdate/AddFieldPathUpdate.java
index b1e06bb1278..d66de429b3c 100644
--- a/document/src/main/java/com/yahoo/document/fieldpathupdate/AddFieldPathUpdate.java
+++ b/document/src/main/java/com/yahoo/document/fieldpathupdate/AddFieldPathUpdate.java
@@ -79,8 +79,8 @@ public class AddFieldPathUpdate extends FieldPathUpdate {
reader.read(this);
}
- public AddFieldPathUpdate(DocumentType type) {
- super(FieldPathUpdate.Type.ADD, type);
+ public AddFieldPathUpdate(DocumentType type, String fieldPath) {
+ super(FieldPathUpdate.Type.ADD, type, fieldPath, null);
}
public void setNewValues(Array value) {
diff --git a/document/src/main/java/com/yahoo/document/fieldpathupdate/AssignFieldPathUpdate.java b/document/src/main/java/com/yahoo/document/fieldpathupdate/AssignFieldPathUpdate.java
index 62bc4ac775b..92fc0458d4e 100644
--- a/document/src/main/java/com/yahoo/document/fieldpathupdate/AssignFieldPathUpdate.java
+++ b/document/src/main/java/com/yahoo/document/fieldpathupdate/AssignFieldPathUpdate.java
@@ -160,8 +160,8 @@ public class AssignFieldPathUpdate extends FieldPathUpdate {
reader.read(this);
}
- public AssignFieldPathUpdate(DocumentType type) {
- super(FieldPathUpdate.Type.ASSIGN, type);
+ public AssignFieldPathUpdate(DocumentType type, String fieldPath) {
+ super(FieldPathUpdate.Type.ASSIGN, type, fieldPath, null);
}
/**
diff --git a/document/src/main/java/com/yahoo/document/fieldpathupdate/FieldPathUpdate.java b/document/src/main/java/com/yahoo/document/fieldpathupdate/FieldPathUpdate.java
index 5672f51beab..9ecdb6f27f0 100644
--- a/document/src/main/java/com/yahoo/document/fieldpathupdate/FieldPathUpdate.java
+++ b/document/src/main/java/com/yahoo/document/fieldpathupdate/FieldPathUpdate.java
@@ -144,18 +144,6 @@ public abstract class FieldPathUpdate {
throw new IllegalArgumentException("Field path update type '" + type + "' not supported.");
}
- public static FieldPathUpdate create(Type type, DocumentType docType) {
- switch (type) {
- case ASSIGN:
- return new AssignFieldPathUpdate(docType);
- case ADD:
- return new AddFieldPathUpdate(docType);
- case REMOVE:
- return new RemoveFieldPathUpdate(docType);
- }
- throw new IllegalArgumentException("Field path update type '" + type + "' not supported.");
- }
-
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/document/src/main/java/com/yahoo/document/fieldpathupdate/RemoveFieldPathUpdate.java b/document/src/main/java/com/yahoo/document/fieldpathupdate/RemoveFieldPathUpdate.java
index 403631322de..96a3fadc66c 100644
--- a/document/src/main/java/com/yahoo/document/fieldpathupdate/RemoveFieldPathUpdate.java
+++ b/document/src/main/java/com/yahoo/document/fieldpathupdate/RemoveFieldPathUpdate.java
@@ -44,10 +44,6 @@ public class RemoveFieldPathUpdate extends FieldPathUpdate {
handler = new IteratorHandler();
}
- public RemoveFieldPathUpdate(DocumentType type) {
- super(FieldPathUpdate.Type.REMOVE, type);
- }
-
FieldPathIteratorHandler getIteratorHandler(Document doc) {
return handler;
}
diff --git a/document/src/main/java/com/yahoo/document/json/DocumentUpdateJsonSerializer.java b/document/src/main/java/com/yahoo/document/json/DocumentUpdateJsonSerializer.java
index 7356bd72dda..2fd09181a44 100644
--- a/document/src/main/java/com/yahoo/document/json/DocumentUpdateJsonSerializer.java
+++ b/document/src/main/java/com/yahoo/document/json/DocumentUpdateJsonSerializer.java
@@ -29,6 +29,7 @@ import com.yahoo.document.fieldpathupdate.AddFieldPathUpdate;
import com.yahoo.document.fieldpathupdate.AssignFieldPathUpdate;
import com.yahoo.document.fieldpathupdate.FieldPathUpdate;
import com.yahoo.document.fieldpathupdate.RemoveFieldPathUpdate;
+import com.yahoo.document.json.readers.SingleValueReader;
import com.yahoo.document.serialization.DocumentUpdateWriter;
import com.yahoo.document.serialization.FieldWriter;
import com.yahoo.document.update.AddValueUpdate;
@@ -48,6 +49,7 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.regex.Matcher;
import java.util.stream.Collectors;
import static com.yahoo.document.json.JsonSerializationHelper.*;
@@ -122,12 +124,13 @@ public class DocumentUpdateJsonSerializer
generator.writeObjectFieldStart(fieldPath.toString());
for (FieldPathUpdate update : fieldPathUpdates) {
+ if (writeArithmeticFieldPathUpdate(update, generator)) continue;
generator.writeFieldName(update.getUpdateType().name().toLowerCase());
if (update instanceof AssignFieldPathUpdate) {
AssignFieldPathUpdate assignUp = (AssignFieldPathUpdate) update;
if (assignUp.getExpression() != null) {
- generator.writeString(assignUp.getExpression());
+ throw new RuntimeException("Unable to parse expression: " + assignUp.getExpression());
} else {
assignUp.getNewValue().serialize(null, this);
}
@@ -143,6 +146,24 @@ public class DocumentUpdateJsonSerializer
generator.writeEndObject();
}
+ // Returns true if fieldpath update was an arithmetic operation after writing it to the generator
+ private boolean writeArithmeticFieldPathUpdate(FieldPathUpdate fieldPathUpdate, JsonGenerator generator) throws IOException {
+ if (! (fieldPathUpdate instanceof AssignFieldPathUpdate)) return false;
+ String expression = ((AssignFieldPathUpdate) fieldPathUpdate).getExpression();
+ if (expression == null) return false;
+
+ Matcher matcher = SingleValueReader.matchArithmeticOperation(expression);
+ if (matcher.find()) {
+ String updateOperation = SingleValueReader.ARITHMETIC_SIGN_TO_UPDATE_OPERATION.get(matcher.group(1));
+ double value = Double.valueOf(matcher.group(2));
+
+ generator.writeNumberField(updateOperation, value);
+ return true;
+ }
+
+ return false;
+ }
+
@Override
public void write(FieldUpdate fieldUpdate) {
wrapIOException(() -> {
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 2f8826354f3..067513b24dc 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
@@ -11,6 +11,12 @@ import com.yahoo.document.json.TokenBuffer;
import com.yahoo.document.update.ValueUpdate;
import org.apache.commons.codec.binary.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
public class SingleValueReader {
public static final String UPDATE_ASSIGN = "assign";
public static final String UPDATE_INCREMENT = "increment";
@@ -18,6 +24,22 @@ public class SingleValueReader {
public static final String UPDATE_MULTIPLY = "multiply";
public static final String UPDATE_DIVIDE = "divide";
+ public static final Map<String, String> UPDATE_OPERATION_TO_ARITHMETIC_SIGN = new HashMap<>();
+ public static final Map<String, String> ARITHMETIC_SIGN_TO_UPDATE_OPERATION;
+ private static final Pattern arithmeticExpressionPattern;
+
+ static {
+ UPDATE_OPERATION_TO_ARITHMETIC_SIGN.put(UPDATE_INCREMENT, "+");
+ UPDATE_OPERATION_TO_ARITHMETIC_SIGN.put(UPDATE_DECREMENT, "-");
+ UPDATE_OPERATION_TO_ARITHMETIC_SIGN.put(UPDATE_MULTIPLY, "*");
+ UPDATE_OPERATION_TO_ARITHMETIC_SIGN.put(UPDATE_DIVIDE, "/");
+ ARITHMETIC_SIGN_TO_UPDATE_OPERATION = UPDATE_OPERATION_TO_ARITHMETIC_SIGN.entrySet().stream()
+ .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
+
+ String validSigns = Pattern.quote(String.join("", UPDATE_OPERATION_TO_ARITHMETIC_SIGN.values()));
+ arithmeticExpressionPattern = Pattern.compile("^\\$\\w+\\s*([" + validSigns + "])\\s*(\\d+(.\\d+)?)$");
+ }
+
public static FieldValue readSingleValue(TokenBuffer buffer, DataType expectedType) {
if (buffer.currentToken().isScalarValue()) {
return readAtomic(buffer.currentText(), expectedType);
@@ -57,6 +79,10 @@ public class SingleValueReader {
return update;
}
+ public static Matcher matchArithmeticOperation(String expression) {
+ return arithmeticExpressionPattern.matcher(expression.trim());
+ }
+
public static FieldValue readAtomic(String field, DataType expectedType) {
if (expectedType.equals(DataType.RAW)) {
return expectedType.createFieldValue(new Base64().decode(field));
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 9fde7aeae62..c384e77b6ea 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
@@ -10,7 +10,6 @@ import com.yahoo.document.DocumentRemove;
import com.yahoo.document.DocumentType;
import com.yahoo.document.DocumentUpdate;
import com.yahoo.document.Field;
-import com.yahoo.document.NumericDataType;
import com.yahoo.document.datatypes.Array;
import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.fieldpathupdate.AddFieldPathUpdate;
@@ -19,7 +18,6 @@ import com.yahoo.document.fieldpathupdate.FieldPathUpdate;
import com.yahoo.document.fieldpathupdate.RemoveFieldPathUpdate;
import com.yahoo.document.json.JsonReaderException;
import com.yahoo.document.json.TokenBuffer;
-import com.yahoo.document.select.parser.ParseException;
import com.yahoo.document.update.FieldUpdate;
import static com.yahoo.document.json.readers.AddRemoveCreator.createAdds;
@@ -29,6 +27,7 @@ import static com.yahoo.document.json.readers.JsonParserHelpers.expectObjectEnd;
import static com.yahoo.document.json.readers.JsonParserHelpers.expectObjectStart;
import static com.yahoo.document.json.readers.MapReader.UPDATE_MATCH;
import static com.yahoo.document.json.readers.MapReader.createMapUpdate;
+import static com.yahoo.document.json.readers.SingleValueReader.UPDATE_ASSIGN;
import static com.yahoo.document.json.readers.SingleValueReader.readSingleUpdate;
/**
@@ -132,25 +131,33 @@ public class VespaJsonDocumentReader {
buffer.next();
while (localNesting <= buffer.nesting()) {
- FieldPathUpdate.Type fieldPathUpdateType = FieldPathUpdate.Type.valueOf(buffer.currentName().toUpperCase());
- FieldPathUpdate fieldPathUpdate = FieldPathUpdate.create(fieldPathUpdateType, update.getType());
-
- fieldPathUpdate.setFieldPath(fieldPath);
- DataType dt = fieldPathUpdate.getFieldPath().getResultingDataType();
- if (fieldPathUpdate instanceof AssignFieldPathUpdate) {
- if (dt instanceof NumericDataType) {
- ((AssignFieldPathUpdate) fieldPathUpdate).setExpression(buffer.currentText());
- } else {
- FieldValue fv = SingleValueReader.readSingleValue(buffer, dt);
- ((AssignFieldPathUpdate) fieldPathUpdate).setNewValue(fv);
- }
-
- } else if (fieldPathUpdate instanceof AddFieldPathUpdate) {
- FieldValue fv = SingleValueReader.readSingleValue(buffer, dt);
+ String fieldPathOperation = buffer.currentName().toLowerCase();
+ FieldPathUpdate fieldPathUpdate;
+ if (fieldPathOperation.equals(UPDATE_ASSIGN)) {
+ fieldPathUpdate = new AssignFieldPathUpdate(update.getType(), fieldPath);
+ FieldValue fv = SingleValueReader.readSingleValue(
+ buffer, fieldPathUpdate.getFieldPath().getResultingDataType());
+ ((AssignFieldPathUpdate) fieldPathUpdate).setNewValue(fv);
+
+ } else if (fieldPathOperation.equals(UPDATE_ADD)) {
+ fieldPathUpdate = new AddFieldPathUpdate(update.getType(), fieldPath);
+ FieldValue fv = SingleValueReader.readSingleValue(
+ buffer, fieldPathUpdate.getFieldPath().getResultingDataType());
((AddFieldPathUpdate) fieldPathUpdate).setNewValues((Array) fv);
- } else if (fieldPathUpdate instanceof RemoveFieldPathUpdate) {
+ } else if (fieldPathOperation.equals(UPDATE_REMOVE)) {
+ fieldPathUpdate = new RemoveFieldPathUpdate(update.getType(), fieldPath);
buffer.next();
+
+ } else if (SingleValueReader.UPDATE_OPERATION_TO_ARITHMETIC_SIGN.containsKey(fieldPathOperation)) {
+ fieldPathUpdate = new AssignFieldPathUpdate(update.getType(), fieldPath);
+ double value = Double.valueOf(buffer.currentText());
+ String expression = String.format("$value %s %s",
+ SingleValueReader.UPDATE_OPERATION_TO_ARITHMETIC_SIGN.get(fieldPathOperation), value);
+ ((AssignFieldPathUpdate) fieldPathUpdate).setExpression(expression);
+
+ } else {
+ throw new IllegalArgumentException("Field path update type '" + fieldPathOperation + "' not supported.");
}
update.addFieldPathUpdate(fieldPathUpdate);
buffer.next();
@@ -158,7 +165,6 @@ public class VespaJsonDocumentReader {
}
-
private static boolean isFieldPath(String field) {
return field.matches("^.*?[.\\[\\{].*$");
}
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 da64ad380fb..a2b92ec4887 100644
--- a/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
+++ b/document/src/test/java/com/yahoo/document/json/DocumentUpdateJsonSerializerTest.java
@@ -360,7 +360,7 @@ public class DocumentUpdateJsonSerializerTest {
}
@Test
- public void testAssignFieldPathValue() {
+ public void testAssignFieldPathUpdate() {
deSerializeAndSerializeJsonAndMatch(inputJson(
"{",
" 'update': 'DOCUMENT_ID',",
@@ -378,7 +378,7 @@ public class DocumentUpdateJsonSerializerTest {
" }",
" },",
" 'map_struct{my_key}.my_int_field': {",
- " 'assign': '10'",
+ " 'assign': 10",
" }",
" }",
"}"
@@ -386,7 +386,7 @@ public class DocumentUpdateJsonSerializerTest {
}
@Test
- public void testRemoveFieldPathValue() {
+ public void testRemoveFieldPathUpdate() {
deSerializeAndSerializeJsonAndMatch(inputJson(
"{",
" 'update': 'DOCUMENT_ID',",
@@ -400,7 +400,7 @@ public class DocumentUpdateJsonSerializerTest {
}
@Test
- public void testAddFieldPathValue() {
+ public void testAddFieldPathUpdate() {
deSerializeAndSerializeJsonAndMatch(inputJson(
"{",
" 'update': 'DOCUMENT_ID',",
@@ -414,6 +414,23 @@ public class DocumentUpdateJsonSerializerTest {
}
@Test
+ public void testArithmeticFieldPathUpdate() {
+ deSerializeAndSerializeJsonAndMatch(inputJson(
+ "{",
+ " 'update': 'DOCUMENT_ID',",
+ " 'fields': {",
+ " 'map_struct{my_key}.my_int_field': {",
+ " 'increment': 5.0",
+ " },",
+ " 'int_array[10]': {",
+ " 'divide': 3.0",
+ " }",
+ " }",
+ "}"
+ ));
+ }
+
+ @Test
public void testMultipleOperationsOnSingleFieldPath() {
deSerializeAndSerializeJsonAndMatch(inputJson(
"{",