com.yahoo.vespaxmlparser.VespaXMLUpdateReader Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespaxmlparser;
import com.yahoo.document.ArrayDataType;
import com.yahoo.document.DataType;
import com.yahoo.document.DocumentId;
import com.yahoo.document.DocumentType;
import com.yahoo.document.DocumentTypeManager;
import com.yahoo.document.DocumentUpdate;
import com.yahoo.document.Field;
import com.yahoo.document.FieldPath;
import com.yahoo.document.MapDataType;
import com.yahoo.document.NumericDataType;
import com.yahoo.document.WeightedSetDataType;
import com.yahoo.document.datatypes.Array;
import com.yahoo.document.datatypes.FieldValue;
import com.yahoo.document.datatypes.IntegerFieldValue;
import com.yahoo.document.datatypes.WeightedSet;
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.select.parser.ParseException;
import com.yahoo.document.serialization.DocumentUpdateReader;
import com.yahoo.document.update.FieldUpdate;
import com.yahoo.document.update.ValueUpdate;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
public class VespaXMLUpdateReader extends VespaXMLFieldReader implements DocumentUpdateReader {
public VespaXMLUpdateReader(String fileName, DocumentTypeManager docTypeManager) throws Exception {
super(fileName, docTypeManager);
}
public VespaXMLUpdateReader(InputStream stream, DocumentTypeManager docTypeManager) throws Exception {
super(stream, docTypeManager);
}
public VespaXMLUpdateReader(XMLStreamReader reader, DocumentTypeManager docTypeManager) {
super(reader, docTypeManager);
}
private Optional condition = Optional.empty();
public Optional getCondition() {
return condition;
}
public boolean hasFieldPath() {
for (int i = 0; i < reader.getAttributeCount(); i++) {
if (reader.getAttributeName(i).toString().equals("fieldpath")) {
return true;
}
}
return false;
}
public void read(DocumentUpdate update) {
try {
// First fetch attributes.
DocumentType doctype = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
final String attributeName = reader.getAttributeName(i).toString();
final String attributeValue = reader.getAttributeValue(i);
if ("documentid".equals(attributeName) || "id".equals(attributeName)) {
update.setId(new DocumentId(attributeValue));
} else if ("documenttype".equals(attributeName) || "type".equals(attributeName)) {
doctype = docTypeManager.getDocumentType(attributeValue);
update.setDocumentType(doctype);
} else if ("create-if-non-existent".equals(attributeName)) {
if ("true".equals(attributeValue)) {
update.setCreateIfNonExistent(true);
} else if ("false".equals(attributeValue)) {
update.setCreateIfNonExistent(false);
} else {
throw newDeserializeException("'create-if-non-existent' must be either 'true' or 'false', was '" + attributeValue +"'");
}
} else if ("condition".equals(attributeName)) {
condition = Optional.of(attributeValue);
}
}
if (doctype == null) {
throw newDeserializeException("Must specify document type. " + reader.getLocation());
}
// Then fetch fields
while (reader.hasNext()) {
int type = reader.next();
if (type == XMLStreamReader.START_ELEMENT) {
final String currentName = reader.getName().toString();
if (hasFieldPath()) {
if ("assign".equals(currentName)) {
update.addFieldPathUpdate(new AssignFieldPathUpdate(doctype, this));
skipToEnd("assign");
} else if ("add".equals(currentName)) {
update.addFieldPathUpdate(new AddFieldPathUpdate(doctype, this));
skipToEnd("add");
} else if ("remove".equals(currentName)) {
update.addFieldPathUpdate(new RemoveFieldPathUpdate(doctype, this));
skipToEnd("remove");
} else {
throw newDeserializeException("Unknown field path update operation " + reader.getName());
}
} else {
if ("assign".equals(currentName)) {
update.addFieldUpdate(readAssign(update));
skipToEnd("assign");
} else if ("add".equals(currentName)) {
update.addFieldUpdate(readAdd(update));
skipToEnd("add");
} else if ("remove".equals(currentName)) {
update.addFieldUpdate(readRemove(update));
skipToEnd("remove");
} else if ("alter".equals(currentName)) {
update.addFieldUpdate(readAlter(update));
skipToEnd("alter");
} else if ("increment".equals(currentName) ||
"decrement".equals(currentName) ||
"multiply".equals(currentName) ||
"divide".equals(currentName)) {
update.addFieldUpdate(readArithmeticField(update, currentName));
skipToEnd(currentName);
} else {
throw newDeserializeException("Unknown update operation " + reader.getName());
}
}
} else if (type == XMLStreamReader.END_ELEMENT) {
return;
}
}
} catch (XMLStreamException e) {
throw newException(e);
}
}
FieldUpdate readAdd(DocumentUpdate update) throws XMLStreamException {
for (int i = 0; i < reader.getAttributeCount(); i++) {
if ("field".equals(reader.getAttributeName(i).toString())) {
Field f = update.getDocumentType().getField(reader.getAttributeValue(i));
FieldValue value = f.getDataType().createFieldValue();
value.deserialize(f, this);
if (value instanceof Array) {
List l = ((Array)value).getValues();
return FieldUpdate.createAddAll(f, l);
} else if (value instanceof WeightedSet) {
return FieldUpdate.createAddAll(f, ((WeightedSet) value));
} else {
throw newDeserializeException("Add operation only applicable to multivalue lists");
}
}
}
throw newDeserializeException("Add update without field attribute");
}
FieldUpdate readRemove(DocumentUpdate update) throws XMLStreamException {
for (int i = 0; i < reader.getAttributeCount(); i++) {
if ("field".equals(reader.getAttributeName(i).toString())) {
Field f = update.getDocumentType().getField(reader.getAttributeValue(i));
FieldValue value = f.getDataType().createFieldValue();
value.deserialize(f, this);
if (value instanceof Array) {
List l = ((Array)value).getValues();
return FieldUpdate.createRemoveAll(f, l);
} else if (value instanceof WeightedSet) {
return FieldUpdate.createRemoveAll(f, ((WeightedSet)value));
} else {
throw newDeserializeException("Remove operation only applicable to multivalue lists");
}
}
}
throw newDeserializeException("Remove update without field attribute");
}
FieldUpdate readAssign(DocumentUpdate update) throws XMLStreamException {
for (int i = 0; i < reader.getAttributeCount(); i++) {
if ("field".equals(reader.getAttributeName(i).toString())) {
Field f = update.getDocumentType().getField(reader.getAttributeValue(i));
if (f == null) {
throw newDeserializeException("Field " + reader.getAttributeValue(i) + " not found.");
}
FieldValue value = f.getDataType().createFieldValue();
value.deserialize(f, this);
return FieldUpdate.createAssign(f, value);
}
}
throw newDeserializeException("Assignment update without field attribute");
}
FieldUpdate readAlter(DocumentUpdate update) throws XMLStreamException {
Field f = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
if ("field".equals(reader.getAttributeName(i).toString())) {
f = update.getDocumentType().getField(reader.getAttributeValue(i));
}
}
if (f == null) {
throw newDeserializeException("Alter update without \"field\" attribute");
}
FieldUpdate fu = FieldUpdate.create(f);
while (reader.hasNext()) {
int type = reader.next();
if (type == XMLStreamReader.START_ELEMENT) {
if ("increment".equals(reader.getName().toString()) ||
"decrement".equals(reader.getName().toString()) ||
"multiply".equals(reader.getName().toString()) ||
"divide".equals(reader.getName().toString())) {
update.addFieldUpdate(readArithmetic(update, reader.getName().toString(), f, fu));
skipToEnd(reader.getName().toString());
} else {
throw newDeserializeException("Element \"" + reader.getName() + "\" not appropriate within alter element");
}
} else if (type == XMLStreamReader.END_ELEMENT) {
break;
}
}
return fu;
}
FieldUpdate readArithmeticField(DocumentUpdate update, String type) throws XMLStreamException {
Field f = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
if ("field".equals(reader.getAttributeName(i).toString())) {
f = update.getDocumentType().getField(reader.getAttributeValue(i));
}
}
if (f == null) {
throw newDeserializeException("Assignment update without \"field\" attribute");
}
FieldUpdate fu = FieldUpdate.create(f);
readArithmetic(update, type, f, fu);
return fu;
}
FieldUpdate readArithmetic(DocumentUpdate update, String type, Field f, FieldUpdate fu) throws XMLStreamException {
Double by = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
if ("by".equals(reader.getAttributeName(i).toString())) {
by = Double.parseDouble(reader.getAttributeValue(i));
}
}
if (by == null) {
throw newDeserializeException("Assignment update without \"by\" attribute");
}
FieldValue key = null;
do {
reader.next();
if (reader.getEventType() == XMLStreamReader.START_ELEMENT) {
if ("key".equals(reader.getName().toString())) {
if (f.getDataType() instanceof WeightedSetDataType) {
DataType nestedType = ((WeightedSetDataType)f.getDataType()).getNestedType();
key = nestedType.createFieldValue();
key.deserialize(this);
} else if (f.getDataType() instanceof MapDataType) {
key = ((MapDataType)f.getDataType()).getKeyType().createFieldValue();
key.deserialize(this);
} else if (f.getDataType() instanceof ArrayDataType) {
key = new IntegerFieldValue(Integer.parseInt(reader.getElementText()));
} else {
throw newDeserializeException("Key tag only applicable for weighted sets and maps");
}
skipToEnd("key");
} else {
throw newDeserializeException("\"" + reader.getName() + "\" not appropriate within " + type + " element.");
}
}
} while (reader.getEventType() != XMLStreamReader.END_ELEMENT);
if (key != null) {
if ("increment".equals(type)) { fu.addValueUpdate(ValueUpdate.createIncrement(key, by)); }
if ("decrement".equals(type)) { fu.addValueUpdate(ValueUpdate.createDecrement(key, by)); }
if ("multiply".equals(type)) { fu.addValueUpdate(ValueUpdate.createMultiply(key, by)); }
if ("divide".equals(type)) { fu.addValueUpdate(ValueUpdate.createDivide(key, by)); }
} else {
if ("increment".equals(type)) { fu.addValueUpdate(ValueUpdate.createIncrement(by)); }
if ("decrement".equals(type)) { fu.addValueUpdate(ValueUpdate.createDecrement(by)); }
if ("multiply".equals(type)) { fu.addValueUpdate(ValueUpdate.createMultiply(by)); }
if ("divide".equals(type)) { fu.addValueUpdate(ValueUpdate.createDivide(by)); }
}
return fu;
}
public void read(FieldUpdate update) {
}
public void read(FieldPathUpdate update) {
String whereClause = null;
String fieldPath = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
if (reader.getAttributeName(i).toString().equals("where")) {
whereClause = reader.getAttributeValue(i);
} else if (reader.getAttributeName(i).toString().equals("fieldpath")) {
fieldPath = reader.getAttributeValue(i);
}
}
if (fieldPath != null) {
update.setFieldPath(fieldPath);
} else {
throw newDeserializeException("Field path is required for document updates.");
}
if (whereClause != null) {
try {
update.setWhereClause(whereClause);
} catch (ParseException e) {
throw newException(e);
}
}
}
public void read(AssignFieldPathUpdate update) {
try {
for (int i = 0; i < reader.getAttributeCount(); i++) {
if (reader.getAttributeName(i).toString().equals("removeifzero")) {
update.setRemoveIfZero(Boolean.parseBoolean(reader.getAttributeValue(i)));
} else if (reader.getAttributeName(i).toString().equals("createmissingpath")) {
update.setCreateMissingPath(Boolean.parseBoolean(reader.getAttributeValue(i)));
}
}
DataType dt = update.getFieldPath().getResultingDataType();
if (dt instanceof NumericDataType) {
update.setExpression(reader.getElementText());
} else {
FieldValue fv = dt.createFieldValue();
fv.deserialize(resolveField(update), this);
update.setNewValue(fv);
}
} catch (XMLStreamException e) {
throw newException(e);
}
}
public void read(AddFieldPathUpdate update) {
DataType dt = update.getFieldPath().getResultingDataType();
FieldValue fv = dt.createFieldValue();
fv.deserialize(resolveField(update), this);
update.setNewValues((Array)fv);
}
public void read(RemoveFieldPathUpdate update) {
}
private static Field resolveField(FieldPathUpdate update) {
String orig = update.getOriginalFieldPath();
if (orig == null) {
return null;
}
FieldPath path = update.getFieldPath();
if (path == null) {
return null;
}
DataType type = path.getResultingDataType();
if (type == null) {
return null;
}
return new Field(orig, type);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy