diff --git a/src/main/java/org/gcube/informationsystem/resourceregistry/queries/json/base/JsonQueryERElement.java b/src/main/java/org/gcube/informationsystem/resourceregistry/queries/json/base/JsonQueryERElement.java index 5b09b52..0f90726 100644 --- a/src/main/java/org/gcube/informationsystem/resourceregistry/queries/json/base/JsonQueryERElement.java +++ b/src/main/java/org/gcube/informationsystem/resourceregistry/queries/json/base/JsonQueryERElement.java @@ -368,7 +368,7 @@ public abstract class JsonQueryERElement { StringBuffer stringBuffer = new StringBuffer(); if(fieldNamePrefix!=null) { stringBuffer.append(fieldNamePrefix.trim()); - if(fieldName!=null && fieldName.trim().length()==0) { + if(fieldName!=null && fieldName.trim().length()!=0) { stringBuffer.append("."); } } diff --git a/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/ConditionalOperator.java b/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/ConditionalOperator.java index 0711918..0155c3d 100644 --- a/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/ConditionalOperator.java +++ b/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/ConditionalOperator.java @@ -16,39 +16,41 @@ import org.gcube.informationsystem.types.PropertyTypeName.BaseTypeGroup; */ public enum ConditionalOperator { - EQ("_eq", " = ", BaseTypeGroup.ANY, "Matches values that are equal to a specified value. E.g. `name = 'Luke'`"), - GT("_gt", " > ", BaseTypeGroup.ANY, "Matches values that are greater than a specified value. "), - GTE("_gte", " >= ", BaseTypeGroup.ANY, "Matches values that are greater than or equal to a specified value."), - LT("_lt", " < ", BaseTypeGroup.ANY, "Matches values that are less than a specified value."), - LTE("_lte", " <= ", BaseTypeGroup.ANY, "Matches values that are less than or equal to a specified value."), - NE("_ne", " <> ", BaseTypeGroup.ANY, "Matches all values that are not equal to a specified value."), - BETWEEN("_between", " BETWEEN %s AND %s", BaseTypeGroup.ANY, "Returns TRUE is a value is between two values, eg. 5 BETWEEN 1 AND 10. The value is between a range. E.g. `price BETWEEN 10 AND 30`. It's equivalent to `price >= 10 AND price <= 30`."), - IS("_is", " IS ", BaseTypeGroup.ANY, "Used to test if a value is NULL"), + EQ("_eq", " = ", 2, BaseTypeGroup.ANY, "Matches values that are equal to a specified value. E.g. `name = 'Luke'`"), + GT("_gt", " > ", 2, BaseTypeGroup.ANY, "Matches values that are greater than a specified value. "), + GTE("_gte", " >= ", 2, BaseTypeGroup.ANY, "Matches values that are greater than or equal to a specified value."), + LT("_lt", " < ", 2, BaseTypeGroup.ANY, "Matches values that are less than a specified value."), + LTE("_lte", " <= ", 2, BaseTypeGroup.ANY, "Matches values that are less than or equal to a specified value."), + NE("_ne", " <> ", 2, BaseTypeGroup.ANY, "Matches all values that are not equal to a specified value."), + BETWEEN("_between", " BETWEEN %s AND %s", 3, BaseTypeGroup.ANY, "Returns TRUE is a value is between two values, eg. 5 BETWEEN 1 AND 10. The value is between a range. E.g. `price BETWEEN 10 AND 30`. It's equivalent to `price >= 10 AND price <= 30`."), + IS("_is", " IS ", 2, BaseTypeGroup.ANY, "Used to test if a value is NULL"), - LIKE("_like", " LIKE ", BaseTypeGroup.STRING, "For strings, checks if a string contains another string. % is used as a wildcard, eg. 'foobar CONTAINS '%ooba%''. Similar to equals, but allow the wildcard '%' that means 'any'. E.g. `name LIKE 'Luk%'`"), - CONTAINS_TEXT("_containsText", " CONTAINSTEXT ", BaseTypeGroup.STRING, "The string contains such text. E.g. `text CONTAINSTEXT 'jay'`"), - MATCHES("_matches", " MATCHES ", BaseTypeGroup.STRING, "Checks if a string matches a regular expression. Matches the string using a Regular Expression. E.g. `text MATCHES '\b[A-Z0-9.%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}\b'`"), + LIKE("_like", " LIKE ", 2, BaseTypeGroup.STRING, "For strings, checks if a string contains another string. % is used as a wildcard, eg. 'foobar CONTAINS '%ooba%''. Similar to equals, but allow the wildcard '%' that means 'any'. E.g. `name LIKE 'Luk%'`"), + CONTAINS_TEXT("_containsText", " CONTAINSTEXT ", 2, BaseTypeGroup.STRING, "The string contains such text. E.g. `text CONTAINSTEXT 'jay'`"), + MATCHES("_matches", " MATCHES ", 2, BaseTypeGroup.STRING, "Checks if a string matches a regular expression. Matches the string using a Regular Expression. E.g. `text MATCHES '\b[A-Z0-9.%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}\b'`"), - IN("_in", " IN ", BaseTypeGroup.COLLECTION, "The same as CONTAINS, but with inverted operands. Matches any of the values specified in an array. E.g. `name in ['European','Asiatic']`"), - CONTAINS("_contains", " CONTAINS ", BaseTypeGroup.COLLECTION, "Checks if the left collection contains the right element. The left argument has to be a colleciton, otherwise it returns FALSE. It's NOT the check of colleciton intersections, so ['a', 'b', 'c'] CONTAINS ['a', 'b'] will return FALSE, while ['a', 'b', 'c'] CONTAINS 'a' will return TRUE. True if the collection contains at least one element that satisfy the next condition. Condition can be a single item: in this case the behaviour is like the IN operator. E.g. `children contains (name = 'Luke')` - `map.values() contains (name = 'Luke')`"), - CONTAINS_ALL("_containsAll", " CONTAINSALL ", BaseTypeGroup.COLLECTION, "True if all the elements of the collection satisfy the next condition. E.g. `children CONTAINSALL (name = 'Luke')`"), - CONTAINS_ANY("_containsAny", " CONTAINSANY ", BaseTypeGroup.COLLECTION, "True if all the elements of the collection satisfy the next condition. E.g. `children CONTAINSANY (name = 'Luke')`"), + IN("_in", " IN ", 2, BaseTypeGroup.COLLECTION, "The same as CONTAINS, but with inverted operands. Matches any of the values specified in an array. E.g. `name in ['European','Asiatic']`"), + CONTAINS("_contains", " CONTAINS ", 2, BaseTypeGroup.COLLECTION, "Checks if the left collection contains the right element. The left argument has to be a colleciton, otherwise it returns FALSE. It's NOT the check of colleciton intersections, so ['a', 'b', 'c'] CONTAINS ['a', 'b'] will return FALSE, while ['a', 'b', 'c'] CONTAINS 'a' will return TRUE. True if the collection contains at least one element that satisfy the next condition. Condition can be a single item: in this case the behaviour is like the IN operator. E.g. `children contains (name = 'Luke')` - `map.values() contains (name = 'Luke')`"), + CONTAINS_ALL("_containsAll", " CONTAINSALL ", 2, BaseTypeGroup.COLLECTION, "True if all the elements of the collection satisfy the next condition. E.g. `children CONTAINSALL (name = 'Luke')`"), + CONTAINS_ANY("_containsAny", " CONTAINSANY ", 2, BaseTypeGroup.COLLECTION, "True if all the elements of the collection satisfy the next condition. E.g. `children CONTAINSANY (name = 'Luke')`"), - CONTAINS_KEY("_containsKey", " CONTAINSKEY ", BaseTypeGroup.MAP, "For maps, the same as for CONTAINS, but checks on the map keys. True if the map contains at least one key equals to the requested. You can also use map.keys() CONTAINS in place of it. E.g. `connections CONTAINSKEY 'Luke'`"), - CONTAINS_VALUE("_containsValue", " CONTAINSVALUE ", BaseTypeGroup.MAP , "For maps, the same as for CONTAINS, but checks on the map values. True if the map contains at least one value equals to the requested. You can also use map.values() CONTAINS in place of it. E.g. `connections containsValue 10:3`"), + CONTAINS_KEY("_containsKey", " CONTAINSKEY ", 2, BaseTypeGroup.MAP, "For maps, the same as for CONTAINS, but checks on the map keys. True if the map contains at least one key equals to the requested. You can also use map.keys() CONTAINS in place of it. E.g. `connections CONTAINSKEY 'Luke'`"), + CONTAINS_VALUE("_containsValue", " CONTAINSVALUE ", 2, BaseTypeGroup.MAP , "For maps, the same as for CONTAINS, but checks on the map values. True if the map contains at least one value equals to the requested. You can also use map.values() CONTAINS in place of it. E.g. `connections containsValue 10:3`"), - IS_DEFINED("_isDefined", " IS DEFINED ", BaseTypeGroup.ANY, "Returns TRUE is a field is defined in a document"), - IS_NOT_DEFINED("_isNotDefined", " IS NOT DEFINED ", BaseTypeGroup.ANY, "Returns TRUE is a field is not defined in a document"); + IS_DEFINED("_isDefined", " IS DEFINED ", 1, BaseTypeGroup.ANY, "Returns TRUE is a field is defined in a document"), + IS_NOT_DEFINED("_isNotDefined", " IS NOT DEFINED ", 1, BaseTypeGroup.ANY, "Returns TRUE is a field is not defined in a document"); protected final String operatorKey; + protected final int numberOfOperand; protected final String dbConditionalOperator; protected final BaseTypeGroup allowed; protected final String description; - private ConditionalOperator(String operatorKey, String conditionalOperator, BaseTypeGroup allowed, String description) { + private ConditionalOperator(String operatorKey, String conditionalOperator, int numberOfOperand, BaseTypeGroup allowed, String description) { this.operatorKey = operatorKey; this.dbConditionalOperator = conditionalOperator; + this.numberOfOperand = numberOfOperand; this.allowed = allowed; this.description = description; } @@ -86,11 +88,11 @@ public enum ConditionalOperator { return operatorByKey.get(key); } - public StringBuffer addCondition(String key, String value) { + public StringBuffer addCondition(String... operands) { StringBuffer stringBuffer = new StringBuffer(); - stringBuffer.append(key); + stringBuffer.append(operands[0]); stringBuffer.append(getDbConditionalOperator()); - stringBuffer.append(value); + stringBuffer.append(operands[1]); return stringBuffer; } } diff --git a/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/MatemathicsOperator.java b/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/MatemathicsOperator.java index dc41379..367ca2d 100644 --- a/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/MatemathicsOperator.java +++ b/src/main/java/org/gcube/informationsystem/resourceregistry/queries/operators/MatemathicsOperator.java @@ -1,6 +1,7 @@ package org.gcube.informationsystem.resourceregistry.queries.operators; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -28,17 +29,17 @@ import org.slf4j.LoggerFactory; */ 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", " || ", ""); + 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); @@ -72,6 +73,7 @@ public enum MatemathicsOperator { private static Map operatorByKey; static { + MatemathicsOperator.operators = new HashSet<>(); MatemathicsOperator.operatorByKey = new HashMap<>(); for(MatemathicsOperator matemathicsOperator : MatemathicsOperator.values()) { diff --git a/src/test/java/org/gcube/informationsystem/resourceregistry/queries/JsonQueryTest.java b/src/test/java/org/gcube/informationsystem/resourceregistry/queries/JsonQueryTest.java index 7b66978..49b769e 100644 --- a/src/test/java/org/gcube/informationsystem/resourceregistry/queries/JsonQueryTest.java +++ b/src/test/java/org/gcube/informationsystem/resourceregistry/queries/JsonQueryTest.java @@ -56,7 +56,7 @@ public class JsonQueryTest extends ContextTest { }; for(File jsonQueryFile : queriesDirectory.listFiles(filenameFilter)) { - logger.info("Going to read JSON query frtm file {}", jsonQueryFile.getAbsolutePath()); + logger.info("Going to read JSON query from file {}", jsonQueryFile.getAbsolutePath()); ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(jsonQueryFile); @@ -197,7 +197,7 @@ public class JsonQueryTest extends ContextTest { } @Test - public void testCreateMatchQuery() throws Exception { + public void testSingleMatchQuery() throws Exception { File queriesDirectory = getProjectionQueriesDirectory(); File jsonQueryFile = new File(queriesDirectory, "HostingNode-query.json"); ObjectMapper objectMapper = new ObjectMapper(); @@ -210,19 +210,58 @@ public class JsonQueryTest extends ContextTest { logger.info("Created Query from JSON:\n{}", createdStringBuffer.toString()); -// StringBuffer expectedStringBuffer = new StringBuffer(); -// File expectedQueryFile = new File(queriesDirectory, jsonQueryFile.getName().replace("json", "match.oquery")); -// try(BufferedReader br = new BufferedReader(new FileReader(expectedQueryFile))) { -// for(String line; (line = br.readLine()) != null; ) { -// expectedStringBuffer.append(line); -// } -// } -// -// logger.info("Expected Query from JSON: {}", expectedStringBuffer.toString()); -// -// Assert.assertTrue(createdStringBuffer.toString().compareTo(expectedStringBuffer.toString())==0); -// + StringBuffer expectedStringBuffer = new StringBuffer(); + File expectedQueryFile = new File(queriesDirectory, jsonQueryFile.getName().replace("-query.json", ".match.oquery")); + try(BufferedReader br = new BufferedReader(new FileReader(expectedQueryFile))) { + for(String line; (line = br.readLine()) != null; ) { + expectedStringBuffer.append(line); + } + } + + logger.info("Expected Query from JSON: {}", expectedStringBuffer.toString()); + + Assert.assertTrue(createdStringBuffer.toString().replaceAll("\n", "").compareTo(expectedStringBuffer.toString().replaceAll("\n", ""))==0); + // String result = jsonQuery.query(); // logger.info("Result : {}", result); } + + @Test + public void testMatchQueries() throws Exception { + File queriesDirectory = getProjectionQueriesDirectory(); + + FilenameFilter filenameFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith("-query.json"); + } + }; + + for(File jsonQueryFile : queriesDirectory.listFiles(filenameFilter)) { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(jsonQueryFile); + logger.info("Going to test the following JSON query {}", jsonNode.toString()); + + JsonQuery jsonQuery = new JsonQuery(); + jsonQuery.setJsonQuery(jsonNode); + StringBuffer createdStringBuffer = jsonQuery.createMatchQuery(); + + logger.info("Created Query from JSON:\n{}", createdStringBuffer.toString()); + + StringBuffer expectedStringBuffer = new StringBuffer(); + File expectedQueryFile = new File(queriesDirectory, jsonQueryFile.getName().replace("-query.json", ".match.oquery")); + try(BufferedReader br = new BufferedReader(new FileReader(expectedQueryFile))) { + for(String line; (line = br.readLine()) != null; ) { + expectedStringBuffer.append(line); + } + } + + logger.info("Expected Query from JSON: {}", expectedStringBuffer.toString()); + + Assert.assertTrue(createdStringBuffer.toString().replaceAll("\n", "").compareTo(expectedStringBuffer.toString().replaceAll("\n", ""))==0); + + //String result = jsonQuery.query(); + //logger.info("Result : {}", result); + } + } } diff --git a/src/test/resources/projection-queries/EService.match.oquery b/src/test/resources/projection-queries/EService.match.oquery index 3cd8d05..a9499a4 100644 --- a/src/test/resources/projection-queries/EService.match.oquery +++ b/src/test/resources/projection-queries/EService.match.oquery @@ -1,29 +1,29 @@ MATCH - {class: EService, as: eservice00, where: ($currentMatch['@class'] INSTANCEOF 'EService')} + {class: EService, as: eservice0, where: ($currentMatch['@class'] INSTANCEOF 'EService')} - .inE('Activates') { as: activates10, where: ($currentMatch['@class'] INSTANCEOF 'Activates')} - .outV('HostingNode') { as: hostingnode20, where: ($currentMatch['@class'] INSTANCEOF 'HostingNode')} - .outE('IsIdentifiedBy') { as: isidentifiedby30, where: ($currentMatch['@class'] INSTANCEOF 'IsIdentifiedBy')} - .inV('NetworkingFacet') { as: networkingfacet40, where: ($currentMatch['@class'] INSTANCEOF 'NetworkingFacet')} - .inE('IsIdentifiedBy') { where: ($matched.isidentifiedby30 == $currentMatch)} - .outV('HostingNode') { where: ($matched.hostingnode20 == $currentMatch)} - .outE('Activates') { where: ($matched.activates10 == $currentMatch)} - .inV('EService') { where: ($matched.eservice00 == $currentMatch)} + .inE('Activates') { as: activates00, where: ($currentMatch['@class'] INSTANCEOF 'Activates')} + .outV('HostingNode') { as: hostingnode000, where: ($currentMatch['@class'] INSTANCEOF 'HostingNode')} + .outE('IsIdentifiedBy') { as: isidentifiedby0000, where: ($currentMatch['@class'] INSTANCEOF 'IsIdentifiedBy')} + .inV('NetworkingFacet') { as: networkingfacet00000, where: ($currentMatch['@class'] INSTANCEOF 'NetworkingFacet')} + .inE('IsIdentifiedBy') { where: ($matched.isidentifiedby0000 == $currentMatch)} + .outV('HostingNode') { where: ($matched.hostingnode000 == $currentMatch)} + .outE('Activates') { where: ($matched.activates00 == $currentMatch)} + .inV('EService') { where: ($matched.eservice0 == $currentMatch)} - .outE('IsIdentifiedBy') { as: isidentifiedby11, where: ($currentMatch['@class'] INSTANCEOF 'IsIdentifiedBy')} - .inV('SoftwareFacet') { as: softwarefacet20, where: ($currentMatch['@class'] INSTANCEOF 'SoftwareFacet')} - .inE('IsIdentifiedBy') { where: ($matched.isidentifiedby11 == $currentMatch)} - .outV('EService') { where: ($matched.eservice00 == $currentMatch)} + .outE('IsIdentifiedBy') { as: isidentifiedby01, where: ($currentMatch['@class'] INSTANCEOF 'IsIdentifiedBy')} + .inV('SoftwareFacet') { as: softwarefacet010, where: ($currentMatch['@class'] INSTANCEOF 'SoftwareFacet')} + .inE('IsIdentifiedBy') { where: ($matched.isidentifiedby01 == $currentMatch)} + .outV('EService') { where: ($matched.eservice0 == $currentMatch)} - .outE('ConsistsOf') { as: consistsof12, where: ($currentMatch['@class'] INSTANCEOF 'ConsistsOf')} - .inV('StateFacet') { as: statefacet20, where: ($currentMatch['@class'] INSTANCEOF 'StateFacet')} - .inE('ConsistsOf') { where: ($matched.consistsof12 == $currentMatch)} - .outV('EService') { where: ($matched.eservice00 == $currentMatch)} + .outE('ConsistsOf') { as: consistsof02, where: ($currentMatch['@class'] INSTANCEOF 'ConsistsOf')} + .inV('StateFacet') { as: statefacet020, where: ($currentMatch['@class'] INSTANCEOF 'StateFacet')} + .inE('ConsistsOf') { where: ($matched.consistsof02 == $currentMatch)} + .outV('EService') { where: ($matched.eservice0 == $currentMatch)} RETURN - networkingfacet40.hostName AS `Host`, - softwarefacet20.group AS `Group`, - softwarefacet20.name AS `Name`, - softwarefacet20.version AS `Version`, - statefacet20.value AS `Status`, - eservice00.id AS `ID` \ No newline at end of file + networkingfacet00000.hostName AS `Host`, + softwarefacet010.group AS `Group`, + softwarefacet010.name AS `Name`, + softwarefacet010.version AS `Version`, + statefacet020.value AS `Status`, + eservice0.id AS `ID` \ No newline at end of file diff --git a/src/test/resources/projection-queries/HostingNode.match.oquery b/src/test/resources/projection-queries/HostingNode.match.oquery index 6ba206d..eaa9d36 100644 --- a/src/test/resources/projection-queries/HostingNode.match.oquery +++ b/src/test/resources/projection-queries/HostingNode.match.oquery @@ -24,7 +24,7 @@ MATCH 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.`, + ((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` \ No newline at end of file