Implementing projection

This commit is contained in:
luca.frosini 2023-11-24 14:03:06 +01:00
parent 5fe0e5771b
commit 6a1e0e5838
8 changed files with 275 additions and 131 deletions

View File

@ -22,6 +22,7 @@ import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaE
import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaNotFoundException; import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaNotFoundException;
import org.gcube.informationsystem.resourceregistry.queries.operators.ConditionalOperator; import org.gcube.informationsystem.resourceregistry.queries.operators.ConditionalOperator;
import org.gcube.informationsystem.resourceregistry.queries.operators.LogicalOperator; import org.gcube.informationsystem.resourceregistry.queries.operators.LogicalOperator;
import org.gcube.informationsystem.resourceregistry.queries.operators.MatemathicsOperator;
import org.gcube.informationsystem.resourceregistry.queries.operators.ProjectionOperator; import org.gcube.informationsystem.resourceregistry.queries.operators.ProjectionOperator;
import org.gcube.informationsystem.resourceregistry.types.TypesCache; import org.gcube.informationsystem.resourceregistry.types.TypesCache;
import org.gcube.informationsystem.utils.TypeUtility; import org.gcube.informationsystem.utils.TypeUtility;
@ -187,10 +188,10 @@ public abstract class JsonQueryERElement {
protected StringBuffer generateAlias() { protected StringBuffer generateAlias() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append(type.toLowerCase()); sb.append(type.toLowerCase());
for(JsonQueryERElement elem : breadcrumb) {
// The entry point will have 00 as suffix sb.append(elem.getPosition());
sb.append(breadcrumb.size()); }
sb.append(position); sb.append(this.position);
return sb; return sb;
} }
@ -243,13 +244,11 @@ public abstract class JsonQueryERElement {
JsonNode node = objectNode.get(fieldName); JsonNode node = objectNode.get(fieldName);
StringBuffer evBuffer = evaluateNode(node, fieldName, fieldNamePrefix); StringBuffer evBuffer = evaluateNode(node, fieldName, fieldNamePrefix);
if(!first) {
stringBuffer.append(queryLogicalOperator.getLogicalOperator());
}
if(evBuffer!=null && evBuffer.length()>0) { if(evBuffer!=null && evBuffer.length()>0) {
if(first) { if(first) {
first = false; first = false;
}else {
stringBuffer.append(queryLogicalOperator.getLogicalOperator());
} }
stringBuffer.append(evBuffer); stringBuffer.append(evBuffer);
} }
@ -293,13 +292,23 @@ public abstract class JsonQueryERElement {
b.append(getAlias(true)); b.append(getAlias(true));
b.append("."); b.append(".");
b.append(fieldNameToEmit); b.append(fieldNameToEmit);
b.append(" AS "); b.append(" AS `");
b.append(nameOfFieldToEmit); b.append(nameOfFieldToEmit);
b.append("`");
addFieldToEmit(b.toString()); addFieldToEmit(b.toString());
} }
return null; return null;
} }
if(MatemathicsOperator.getOperators().contains(fieldName)) {
--size;
setProjection(true);
MatemathicsOperator mo = MatemathicsOperator.getMatemathicsOperator(fieldName);
String fieldToEmit = mo.generateFieldToEmit(jsonNode, getAlias(true));
addFieldToEmit(fieldToEmit);
return null;
}
StringBuffer stringBuffer = new StringBuffer(); StringBuffer stringBuffer = new StringBuffer();
@ -347,6 +356,11 @@ public abstract class JsonQueryERElement {
stringBuffer.append(addCondition(ConditionalOperator.EQ, key, value)); stringBuffer.append(addCondition(ConditionalOperator.EQ, key, value));
} }
if(jsonNode.isNull()) {
StringBuffer key = getKey(fieldName, null);
stringBuffer.append(addCondition(ConditionalOperator.IS, key, null));
}
return stringBuffer; return stringBuffer;
} }

View File

@ -5,6 +5,15 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; 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) * @author Luca Frosini (ISTI - CNR)
* *
@ -32,46 +41,137 @@ public enum MatemathicsOperator {
BITWISE_XOR("_bitxor", " ^ ", ""), BITWISE_XOR("_bitxor", " ^ ", ""),
ARRAY_CONCATENATION("_arrayconcat", " || ", ""); ARRAY_CONCATENATION("_arrayconcat", " || ", "");
protected final String operator; protected static Logger logger = LoggerFactory.getLogger(MatemathicsOperator.class);
protected final String matemathicsOperator;
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 dbQueryOperator;
protected final String description; protected final String description;
private MatemathicsOperator(String operator, String matemathicsOperator, String description) { private MatemathicsOperator(String operatorKey, String dbQueryOperator, String description) {
this.operator = operator; this.operatorKey = operatorKey;
this.matemathicsOperator = matemathicsOperator; this.dbQueryOperator = dbQueryOperator;
this.description = description; this.description = description;
} }
public String getOperator() { public String getOperatorKey() {
return operator; return operatorKey;
} }
public String getMatemathicsOperator() { public String getDbQueryOperator() {
return matemathicsOperator; return dbQueryOperator;
} }
public String getDescription() { public String getDescription() {
return description; return description;
} }
private static Set<String> operators;
private static Map<String,MatemathicsOperator> operatorByKey; private static Map<String,MatemathicsOperator> operatorByKey;
static { static {
MatemathicsOperator.operators = new HashSet<>();
MatemathicsOperator.operatorByKey = new HashMap<>(); MatemathicsOperator.operatorByKey = new HashMap<>();
for(MatemathicsOperator queryLogicalOperator : MatemathicsOperator.values()) { for(MatemathicsOperator matemathicsOperator : MatemathicsOperator.values()) {
MatemathicsOperator.operators.add(queryLogicalOperator.getOperator()); MatemathicsOperator.operatorByKey.put(matemathicsOperator.getOperatorKey(), matemathicsOperator);
MatemathicsOperator.operatorByKey.put(queryLogicalOperator.getOperator(), queryLogicalOperator);
} }
} }
public static Set<String> getOperators() { public static Set<String> getOperators() {
return MatemathicsOperator.operators; return new HashSet<>(MatemathicsOperator.operatorByKey.keySet());
} }
public static MatemathicsOperator getQueryLogicalOperator(String key) { public static MatemathicsOperator getMatemathicsOperator(String key) {
return operatorByKey.get(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.getMatemathicsOperator(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(dbQueryOperator);
buffer.append(" ");
if(fieldsSeparator!=null) {
buffer.append("'");
buffer.append(fieldsSeparator);
buffer.append("' ");
buffer.append(dbQueryOperator);
buffer.append(" ");
}
}
}
return buffer;
}
} }

View File

@ -199,7 +199,7 @@ public class JsonQueryTest extends ContextTest {
@Test @Test
public void testCreateMatchQuery() throws Exception { public void testCreateMatchQuery() throws Exception {
File queriesDirectory = getProjectionQueriesDirectory(); File queriesDirectory = getProjectionQueriesDirectory();
File jsonQueryFile = new File(queriesDirectory, "EService-query.json"); File jsonQueryFile = new File(queriesDirectory, "HostingNode-query.json");
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(jsonQueryFile); JsonNode jsonNode = objectMapper.readTree(jsonQueryFile);
logger.info("Going to test the following JSON query {}", jsonNode.toString()); logger.info("Going to test the following JSON query {}", jsonNode.toString());

View File

@ -0,0 +1,38 @@
package org.gcube.informationsystem.resourceregistry.queries.operators;
import org.gcube.com.fasterxml.jackson.databind.JsonNode;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MathematicsOperatorTest {
protected static Logger logger = LoggerFactory.getLogger(MathematicsOperatorTest.class);
public static final String JSON = "{ \n"
+ " \"values\" : [\n"
+ " {\n"
+ " \"_minus\" : [\n"
+ " \"size\", \n"
+ " \"used\" \n"
+ " ]\n"
+ " },\n"
+ " \"unit\" \n"
+ " ],\n"
+ " \"separator\" : \" \",\n"
+ " \"as\" : \"HD Space Left\"\n"
+ "}";
@Test
public void testGenerateFieldToEmit() throws Exception {
ObjectMapper om = new ObjectMapper();
JsonNode jn = om.readTree(JSON);
String s = MatemathicsOperator.SUM.generateFieldToEmit(jn, "haspersistentmemory20");
logger.debug(s);
}
}

View File

@ -21,9 +21,9 @@ MATCH
.outV('EService') { where: ($matched.eservice00 == $currentMatch)} .outV('EService') { where: ($matched.eservice00 == $currentMatch)}
RETURN RETURN
networkingfacet40.hostName AS Host, networkingfacet40.hostName AS `Host`,
softwarefacet20.group AS Group, softwarefacet20.group AS `Group`,
softwarefacet20.name AS Name, softwarefacet20.name AS `Name`,
softwarefacet20.version AS Version, softwarefacet20.version AS `Version`,
statefacet20.value AS Status, statefacet20.value AS `Status`,
eservice00.id AS ID eservice00.id AS `ID`

View File

@ -1,37 +0,0 @@
SELECT id AS ID,
outE("IsIdentifiedBy").inV("SoftwareFacet").name[0] AS `Name`,
outE("IsIdentifiedBy").inV("SoftwareFacet").group[0] AS `Group`,
outE("IsIdentifiedBy").inV("SoftwareFacet").version[0] AS `Version`,
outE("ConsistsOf").inV("StateFacet").value AS `Status`,
inE("Activates").outV("HostingNode").outE("IsIdentifiedBy").inV("NetworkingFacet").hostName[0] AS `Host`
FROM (
TRAVERSE outV("EService") FROM (
TRAVERSE inE("ConsistsOf") FROM (
SELECT FROM (
TRAVERSE inV("StateFacet") FROM (
TRAVERSE outE("ConsistsOf") FROM (
TRAVERSE outV("EService") FROM (
TRAVERSE inE("IsIdentifiedBy") FROM (
SELECT FROM (
TRAVERSE inV("SoftwareFacet") FROM (
TRAVERSE outE("IsIdentifiedBy") FROM (
TRAVERSE inV("EService") FROM (
TRAVERSE outE("Activates") FROM (
TRAVERSE outV("HostingNode") FROM (
TRAVERSE inE("IsIdentifiedBy") FROM (
SELECT FROM NetworkingFacet
)
)
)
)
)
)
)
)
)
)
)
)
)
)
) WHERE @class INSTANCEOF "EService"

View File

@ -1,66 +1,65 @@
{ {
"_projection" : { "type": "HostingNode",
"type": "HostingNode", "_emit" : {
"_emit" : { "id" : "ID"
"id" : "Id", },
}, "metadata" : {
"metadata" : { "_emit" : {
"_emit" : { "lastUpdateTime" : "Last Update Time"
"lastUpdateTime" : "Last Update Time" }
} },
}, "consistsOf": [
"consistsOf": [ {
{ "type": "IsIdentifiedBy",
"type": "IsIdentifiedBy", "target": {
"target": { "type": "NetworkingFacet",
"type": "NetworkingFacet", "_emit" : {
"_emit" : { "hostName" : "HostName"
"hostName" : "HostName"
}
}
},
{
"type": "ConsistsOf",
"target": {
"type": "StateFacet",
"_emit" : {
"value" : "Status"
}
}
},
{
"type": "HasPersistentMemory",
"target": {
"type": "MemoryFacet",
"_concat" : {
"values" : [
{
"_sub" : [
"size",
"used"
]
},
"unit"
],
"separator" : " ",
"as" : "HD Space Left"
}
}
},
{
"type": "HasVolatileMemory",
"target": {
"type": "MemoryFacet",
"_concat" : {
"values" : [
"size",
"unit"
],
"separator" : " ",
"as" : "Mem. Available."
}
} }
} }
] },
} {
"type": "ConsistsOf",
"target": {
"type": "StateFacet",
"_emit" : {
"value" : "Status"
}
}
},
{
"type": "HasPersistentMemory",
"target": {
"type": "MemoryFacet",
"_sum" : {
"values" : [
{
"_minus" : [
"size",
"used"
]
},
"unit"
],
"separator" : " ",
"as" : "HD Space Left"
}
}
},
{
"type": "HasVolatileMemory",
"target": {
"type": "MemoryFacet",
"jvmMaxMemory" : null,
"_sum" : {
"values" : [
"size",
"unit"
],
"separator" : " ",
"as" : "Mem. Available."
}
}
}
]
} }

View File

@ -0,0 +1,30 @@
MATCH
{class: HostingNode, as: hostingnode0, where: ($currentMatch['@class'] INSTANCEOF 'HostingNode')}
.outE('IsIdentifiedBy') { as: isidentifiedby00, where: ($currentMatch['@class'] INSTANCEOF 'IsIdentifiedBy')}
.inV('NetworkingFacet') { as: networkingfacet000, where: ($currentMatch['@class'] INSTANCEOF 'NetworkingFacet')}
.inE('IsIdentifiedBy') { where: ($matched.isidentifiedby00 == $currentMatch)}
.outV('HostingNode') { where: ($matched.hostingnode0 == $currentMatch)}
.outE('ConsistsOf') { as: consistsof01, where: ($currentMatch['@class'] INSTANCEOF 'ConsistsOf')}
.inV('StateFacet') { as: statefacet010, where: ($currentMatch['@class'] INSTANCEOF 'StateFacet')}
.inE('ConsistsOf') { where: ($matched.consistsof01 == $currentMatch)}
.outV('HostingNode') { where: ($matched.hostingnode0 == $currentMatch)}
.outE('HasPersistentMemory') { as: haspersistentmemory02, where: ($currentMatch['@class'] INSTANCEOF 'HasPersistentMemory')}
.inV('MemoryFacet') { as: memoryfacet020, where: ($currentMatch['@class'] INSTANCEOF 'MemoryFacet')}
.inE('HasPersistentMemory') { where: ($matched.haspersistentmemory02 == $currentMatch)}
.outV('HostingNode') { where: ($matched.hostingnode0 == $currentMatch)}
.outE('HasVolatileMemory') { as: hasvolatilememory03, where: ($currentMatch['@class'] INSTANCEOF 'HasVolatileMemory')}
.inV('MemoryFacet') { as: memoryfacet030, where: (($currentMatch['@class'] INSTANCEOF 'MemoryFacet') AND (jvmMaxMemory IS null))}
.inE('HasVolatileMemory') { where: ($matched.hasvolatilememory03 == $currentMatch)}
.outV('HostingNode') { where: ($matched.hostingnode0 == $currentMatch)}
RETURN
networkingfacet000.hostName AS `HostName`,
statefacet010.value AS `Status`,
((memoryfacet020.size - memoryfacet020.used) + ' ' + memoryfacet020.unit) AS `HD Space Left`,
(memoryfacet030.size + ' ' + memoryfacet030.unit) AS `Mem. Available.`,
hostingnode0.id AS `ID`,
hostingnode0.lastUpdateTime AS `Last Update Time`