181 lines
5.4 KiB
Java
181 lines
5.4 KiB
Java
package org.gcube.informationsystem.resourceregistry.queries.operators;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import javax.ws.rs.BadRequestException;
|
|
|
|
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
|
|
import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode;
|
|
import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode;
|
|
import org.gcube.com.fasterxml.jackson.databind.node.TextNode;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
/**
|
|
* @author Luca Frosini (ISTI - CNR)
|
|
*
|
|
* OrientDB supports the eval() function to execute complex operations. Example:
|
|
*
|
|
* SELECT eval( "amount * 120 / 100 - discount" ) as finalPrice from Order
|
|
|
|
*
|
|
* See https://www.orientdb.com/docs/3.0.x/sql/SQL-Where.html#mathematics-operators
|
|
* https://www.orientdb.com/docs/3.0.x/sql/SQL-Syntax.html#math-operators
|
|
* https://www.orientdb.com/docs/3.0.x/sql/SQL-Syntax.html#math-operators-precedence
|
|
* https://www.orientdb.com/docs/3.0.x/sql/SQL-Syntax.html#array-concatenation
|
|
*/
|
|
public enum MatemathicsOperator {
|
|
|
|
SUM("_sum", "+", ""),
|
|
MINUS("_minus", "-", ""),
|
|
MULTIPLY("_multiply", "*", ""),
|
|
DIVIDE("_divide", "/", ""),
|
|
MODULE("_mod", "%", ""),
|
|
BITWISE_RIGHT_SHIFT("_bitrshift", ">>", ""),
|
|
BITWISE_LEFT_SHIFT("_bitlshift", "<<", ""),
|
|
BITWISE_AND("_bitand", "&", ""),
|
|
BITWISE_OR("_bitor", "|", ""),
|
|
BITWISE_XOR("_bitxor", "^", ""),
|
|
ARRAY_CONCATENATION("_arrayconcat", "||", "");
|
|
|
|
protected static Logger logger = LoggerFactory.getLogger(MatemathicsOperator.class);
|
|
|
|
public static final String VALUES_KEY = "values";
|
|
public static final String SEPARATOR_KEY = "separator";
|
|
public static final String AS_KEY = "as";
|
|
|
|
protected final String operatorKey;
|
|
protected final String dbOperator;
|
|
protected final String description;
|
|
|
|
private MatemathicsOperator(String operatorKey, String dbOperator, String description) {
|
|
this.operatorKey = operatorKey;
|
|
this.dbOperator = dbOperator;
|
|
this.description = description;
|
|
}
|
|
|
|
protected String getOperatorKey() {
|
|
return operatorKey;
|
|
}
|
|
|
|
protected String getDbOperator() {
|
|
return dbOperator;
|
|
}
|
|
|
|
public String getDescription() {
|
|
return description;
|
|
}
|
|
|
|
private static Set<String> operators;
|
|
private static Map<String,MatemathicsOperator> operatorByKey;
|
|
|
|
static {
|
|
MatemathicsOperator.operators = new HashSet<>();
|
|
MatemathicsOperator.operatorByKey = new HashMap<>();
|
|
|
|
for(MatemathicsOperator matemathicsOperator : MatemathicsOperator.values()) {
|
|
MatemathicsOperator.operators.add(matemathicsOperator.getOperatorKey());
|
|
MatemathicsOperator.operatorByKey.put(matemathicsOperator.getOperatorKey(), matemathicsOperator);
|
|
}
|
|
}
|
|
|
|
public static Set<String> getOperators() {
|
|
return operators;
|
|
}
|
|
|
|
public static MatemathicsOperator getOperator(String key) {
|
|
return operatorByKey.get(key);
|
|
}
|
|
|
|
protected String getError(String key, Class<?> clazz, boolean mandatory) {
|
|
StringBuffer error = new StringBuffer();
|
|
error.append("Root emitting MatemathicsOperator (i.e. ");
|
|
error.append(this.operatorKey);
|
|
error.append(")");
|
|
error.append(" contains ");
|
|
if(mandatory) {
|
|
error.append("mandatory ");
|
|
}
|
|
error.append(key);
|
|
error.append(" which must be a");
|
|
error.append(clazz.getSimpleName());
|
|
error.append(". This is a client error. Please fix your query");
|
|
logger.error(error.toString());
|
|
throw new BadRequestException(error.toString());
|
|
}
|
|
|
|
public String generateFieldToEmit(JsonNode jsonNode, String fieldPrefix) {
|
|
JsonNode jn = jsonNode.get(VALUES_KEY);
|
|
if(jn.isNull() || !jn.isArray()) {
|
|
getError(VALUES_KEY, ArrayNode.class, true);
|
|
}
|
|
String fieldSeparator = null;
|
|
if(this == MatemathicsOperator.SUM && jsonNode.has(SEPARATOR_KEY)) {
|
|
JsonNode sep = jsonNode.get(SEPARATOR_KEY);
|
|
if(!sep.isTextual()) {
|
|
getError(SEPARATOR_KEY, TextNode.class, false);
|
|
}
|
|
fieldSeparator = sep.asText();
|
|
}
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.append("(");
|
|
sb.append(generateFieldToEmit((ArrayNode) jn, fieldSeparator, fieldPrefix));
|
|
JsonNode jnAs = jsonNode.get(AS_KEY);
|
|
if(jnAs.isNull() || !jnAs.isTextual()) {
|
|
getError(AS_KEY, TextNode.class, true);
|
|
}
|
|
sb.append(") AS `");
|
|
sb.append(jnAs.asText());
|
|
sb.append("`");
|
|
return sb.toString();
|
|
}
|
|
|
|
protected StringBuffer addFieldPrefix(StringBuffer sb, String fieldPrefix) {
|
|
if(fieldPrefix !=null && fieldPrefix.trim().length()>0) {
|
|
sb.append(fieldPrefix.trim());
|
|
sb.append(".");
|
|
}
|
|
return sb;
|
|
}
|
|
|
|
protected StringBuffer generateFieldToEmit(ArrayNode arrayNode, String fieldsSeparator, String fieldPrefix) {
|
|
StringBuffer buffer = new StringBuffer();
|
|
int size = arrayNode.size();
|
|
for(int i=0; i<size; i++) {
|
|
JsonNode jn = arrayNode.get(i);
|
|
if(jn.isObject()) {
|
|
ObjectNode on = (ObjectNode) jn;
|
|
String key = on.fieldNames().next();
|
|
MatemathicsOperator mo = MatemathicsOperator.getOperator(key);
|
|
ArrayNode an = (ArrayNode) on.get(key);
|
|
buffer.append("(");
|
|
buffer.append(mo.generateFieldToEmit(an, null, fieldPrefix));
|
|
buffer.append(")");
|
|
}
|
|
|
|
if(jn.isTextual()) {
|
|
buffer = addFieldPrefix(buffer, fieldPrefix);
|
|
buffer.append(jn.asText());
|
|
}
|
|
|
|
if(i<(size-1)) {
|
|
buffer.append(" ");
|
|
buffer.append(dbOperator);
|
|
buffer.append(" ");
|
|
if(fieldsSeparator!=null) {
|
|
buffer.append("'");
|
|
buffer.append(fieldsSeparator);
|
|
buffer.append("' ");
|
|
buffer.append(dbOperator);
|
|
buffer.append(" ");
|
|
}
|
|
}
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
}
|