Neo4jQueryBuilder.java
package de.dlr.shepard.common.search.query;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.dlr.shepard.common.exceptions.ShepardParserException;
import de.dlr.shepard.common.neo4j.entities.ContainerType;
import de.dlr.shepard.common.search.io.SearchScope;
import de.dlr.shepard.common.util.Constants;
import de.dlr.shepard.common.util.CypherQueryHelper;
import de.dlr.shepard.common.util.Neo4jLabels;
import de.dlr.shepard.common.util.SearchConstants;
import de.dlr.shepard.common.util.SortingHelper;
import de.dlr.shepard.common.util.TraversalRules;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public class Neo4jQueryBuilder {
private static final List<String> BOOLEAN_OPERATORS = List.of(
SearchConstants.JSON_AND,
SearchConstants.JSON_OR,
SearchConstants.JSON_NOT,
SearchConstants.JSON_XOR
);
private static final List<String> OP_ATTRIBUTES = List.of(
SearchConstants.OP_PROPERTY,
SearchConstants.OP_VALUE,
SearchConstants.OP_OPERATOR
);
private static final List<String> NOT_ID_PROPERTIES = List.of(
SearchConstants.JSON_CREATED_BY,
SearchConstants.JSON_UPDATED_BY,
SearchConstants.JSON_VALUE_IRI,
SearchConstants.JSON_PROPERTY_IRI,
SearchConstants.JSON_CREATED_AT,
SearchConstants.JSON_UPDATED_AT,
SearchConstants.JSON_HAS_ANNOTATION,
SearchConstants.JSON_HAS_ANNOTATION_IRI,
SearchConstants.JSON_TIMESERIES_REFERENCE_START,
SearchConstants.JSON_TIMESERIES_REFERENCE_END,
SearchConstants.JSON_REFERENCED_TIMESERIES_PROPERTY_IRI,
SearchConstants.JSON_REFERENCED_TIMESERIES_VALUE_IRI,
SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION,
SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI
);
private static final List<String> ID_PROPERTIES = List.of(
SearchConstants.JSON_ID,
SearchConstants.JSON_REFERENCED_COLLECTION_ID,
SearchConstants.JSON_REFERENCED_DATA_OBJECT_ID,
SearchConstants.JSON_REFERENCED_TIMESERIES_ID,
SearchConstants.JSON_FILE_CONTAINER_ID,
SearchConstants.JSON_STRUCTURED_DATA_CONTAINER_ID,
SearchConstants.JSON_TIMESERIES_CONTAINER_ID,
SearchConstants.JSON_SUCCESSOR_IDS,
SearchConstants.JSON_PREDECESSOR_IDS,
SearchConstants.JSON_CHILDREN_IDS,
SearchConstants.JSON_PARENT_IDS
);
private static final List<String> QUINTUPLE_PROPERTIES = List.of(
SearchConstants.JSON_REFERENCED_TIMESERIES_MEASUREMENT,
SearchConstants.JSON_REFERENCED_TIMESERIES_DEVICE,
SearchConstants.JSON_REFERENCED_TIMESERIES_LOCATION,
SearchConstants.JSON_REFERENCED_TIMESERIES_SYMBOLIC_NAME,
SearchConstants.JSON_REFERENCED_TIMESERIES_FIELD
);
private static String getNeo4jWithNeo4jIdString(String jsonQuery, String variable) {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = null;
try {
jsonNode = objectMapper.readValue(jsonQuery, JsonNode.class);
} catch (JsonProcessingException e) {
throw new ShepardParserException("could not parse JSON " + e.getMessage());
}
return getNeo4jStringWithNeo4jId(jsonNode, variable);
}
private static String getNeo4jStringWithNeo4jId(JsonNode rootNode, String variable) {
String op = "";
try {
op = rootNode.fieldNames().next();
} catch (NoSuchElementException e) {
throw new ShepardParserException("error in parsing" + e.getMessage());
}
if (OP_ATTRIBUTES.contains(op)) {
return primitiveClauseWithNeo4jId(rootNode, variable);
}
return complexClauseWithNeo4jId(rootNode, op, variable);
}
private static String complexClauseWithNeo4jId(JsonNode node, String operator, String variable) {
if (!BOOLEAN_OPERATORS.contains(operator)) throw new ShepardParserException(
"unknown boolean operator: " + operator
);
if (operator.equals(SearchConstants.JSON_NOT)) return notClauseWithNeo4jId(node, variable);
else return multaryClauseWithNeo4jId(node, operator, variable);
}
private static String multaryClauseWithNeo4jId(JsonNode node, String operator, String variable) {
Iterator<JsonNode> argumentsArray = node.get(operator).elements();
String firstArgument = getNeo4jStringWithNeo4jId(argumentsArray.next(), variable);
String ret = "(" + firstArgument;
while (argumentsArray.hasNext()) {
ret = ret + " " + operator + " " + getNeo4jStringWithNeo4jId(argumentsArray.next(), variable);
}
ret = ret + ")";
return ret;
}
private static String notClauseWithNeo4jId(JsonNode node, String variable) {
JsonNode body = node.get(SearchConstants.JSON_NOT);
return "(NOT(" + getNeo4jStringWithNeo4jId(body, variable) + "))";
}
private static String primitiveClauseWithNeo4jId(JsonNode node, String variable) {
String property = node.get(SearchConstants.OP_PROPERTY).textValue();
property = changeAttributesDelimiter(property);
if (NOT_ID_PROPERTIES.contains(property)) return simpleNotIdPropertyPart(node, variable);
if (ID_PROPERTIES.contains(property)) return simpleIdPropertyPart(node, variable);
if (QUINTUPLE_PROPERTIES.contains(property)) return simpleQuintuplePropertyPart(node, variable);
String ret = "(";
ret = ret + "toLower(" + variable + ".`" + property + "`) ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + valuePart(node).toLowerCase();
ret = ret + ")";
return ret;
}
/**
* This is a fix described in:
* https://gitlab.com/dlr-shepard/shepard/-/issues/389
* We use new delimiter characters for attributes ('||'), but want to support
* the old search functionality using '.' as a delimiter.
*
* @param property
* @return property string, if it contained 'attributes.', it is going to be
* replaced by 'attributes||'
*/
private static String changeAttributesDelimiter(String property) {
if (property.startsWith("attributes.")) {
return property.replaceFirst("attributes.", "attributes||");
}
return property;
}
private static String valuePart(JsonNode node) {
String ret = "";
if (node.get(SearchConstants.OP_OPERATOR).textValue().equals(SearchConstants.JSON_IN)) {
ret = ret + "[";
Iterator<JsonNode> setArray = node.get(SearchConstants.OP_VALUE).elements();
if (setArray.hasNext()) {
ret = ret + setArray.next();
while (setArray.hasNext()) ret = ret + ", " + setArray.next();
ret = ret + "]";
} else {
ret = ret + "]";
}
} else ret = ret + node.get(SearchConstants.OP_VALUE);
return ret;
}
private static String simpleNotIdPropertyPart(JsonNode node, String variable) {
String property = node.get(SearchConstants.OP_PROPERTY).textValue();
// search for creating user
if (
property.equals(SearchConstants.JSON_CREATED_BY) || property.equals(SearchConstants.JSON_UPDATED_BY)
) return byPart(node, variable);
// search for createdAt/updatedAt
if (
property.equals(SearchConstants.JSON_CREATED_AT) || property.equals(SearchConstants.JSON_UPDATED_AT)
) return atPart(node, variable);
// search for SemanticAnnotationIRIs
if (
property.equals(SearchConstants.JSON_VALUE_IRI) || property.equals(SearchConstants.JSON_PROPERTY_IRI)
) return iRIPart(node, variable);
// search for SemanticAnnotations
if (property.equals(SearchConstants.JSON_HAS_ANNOTATION)) return hasAnnotationPart(node, variable);
if (property.equals(SearchConstants.JSON_HAS_ANNOTATION_IRI)) return hasAnnotationIRIPart(node, variable);
// search for start/end of TimeseriesReferences
if (
property.equals(SearchConstants.JSON_TIMESERIES_REFERENCE_START) ||
property.equals(SearchConstants.JSON_TIMESERIES_REFERENCE_END)
) return timeFramePart(node, variable);
// search for SemanticAnnotations of referenced Timeseries
if (
property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_VALUE_IRI) ||
property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_PROPERTY_IRI)
) return referencedTimeseriesIRIPart(node, variable);
if (
property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION) ||
property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI)
) return referencedTimeseriesAnnotationPart(node, variable);
return null;
}
/**
* method used for searching according to a timestamp
* used for createdAt and updatedAt
*
* @param node the JsonNode containing a search triple with updatedAt or createdAt as property
* @param variable the variable in the resulting neo4j query representing the noe4jNode under consideration
* @return a condition in a neo4j query representing the criterion given in the search triple given by node applied to the neo4jNode represented by variable
*/
private static String atPart(JsonNode node, String variable) {
String ret = "(";
String at =
switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
case SearchConstants.JSON_CREATED_AT -> Neo4jLabels.CREATED_AT;
case SearchConstants.JSON_UPDATED_AT -> Neo4jLabels.UPDATED_AT;
default -> "";
};
ret = ret + variable + "." + at + " ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + valuePart(node).toLowerCase();
ret = ret + ")";
return ret;
}
private static String timeFramePart(JsonNode node, String variable) {
String ret = "(";
String timeFrame =
switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
case SearchConstants.JSON_TIMESERIES_REFERENCE_START -> Neo4jLabels.TIMESERIES_REFERENCE_START;
case SearchConstants.JSON_TIMESERIES_REFERENCE_END -> Neo4jLabels.TIMESERIES_REFERENCE_END;
default -> "";
};
ret = ret + variable + "." + timeFrame + " ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + valuePart(node).toLowerCase();
ret = ret + ")";
return ret;
}
private static String hasAnnotationIRIPart(JsonNode node, String variable) {
String[] pair = CypherQueryHelper.splitByExactlyOneDoubleColon(node.get(SearchConstants.OP_VALUE).textValue());
String propertyIRI = pair[0];
String valueIRI = pair[1];
String ret = "(";
ret =
ret +
"EXISTS {MATCH (" +
variable +
") - [:" +
Neo4jLabels.HAS_ANNOTATION +
"] -> (sem:" +
Neo4jLabels.SEMANTIC_ANNOTATION +
") WHERE (sem." +
Neo4jLabels.PROPERTY_IRI +
" ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " \"" + propertyIRI + "\" AND ";
ret =
ret +
" sem." +
Neo4jLabels.VALUE_IRI +
" " +
operatorString(node.get(SearchConstants.OP_OPERATOR)) +
" \"" +
valueIRI;
ret = ret + "\")})";
return ret;
}
private static String hasAnnotationPart(JsonNode node, String variable) {
String[] pair = CypherQueryHelper.splitByExactlyOneDoubleColon(node.get(SearchConstants.OP_VALUE).textValue());
String propertyName = pair[0];
String valueName = pair[1];
String ret = "(";
ret =
ret +
"EXISTS {MATCH (" +
variable +
") - [:" +
Neo4jLabels.HAS_ANNOTATION +
"] -> (sem:" +
Neo4jLabels.SEMANTIC_ANNOTATION +
") WHERE (sem." +
Neo4jLabels.PROPERTY_NAME +
" ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " \"" + propertyName + "\" AND ";
ret =
ret +
" sem." +
Neo4jLabels.VALUE_NAME +
" " +
operatorString(node.get(SearchConstants.OP_OPERATOR)) +
" \"" +
valueName;
ret = ret + "\")})";
return ret;
}
private static String simpleIdPropertyPart(JsonNode node, String variable) {
String property = node.get(SearchConstants.OP_PROPERTY).textValue();
// for simple id
if (property.equals(SearchConstants.JSON_ID)) return neo4jIdPart(node, variable);
// for CollectionReferences
if (property.equals(SearchConstants.JSON_REFERENCED_COLLECTION_ID)) return referencedCollectionNeo4jIdPart(
node,
variable
);
// for DataObjectReferences
if (property.equals(SearchConstants.JSON_REFERENCED_DATA_OBJECT_ID)) return referencedDataObjectNeo4jIdPart(
node,
variable
);
// for FileReferences
if (property.equals(SearchConstants.JSON_FILE_CONTAINER_ID)) return fileContainerIdPart(node, variable);
// for StructuredDataReferences
if (property.equals(SearchConstants.JSON_STRUCTURED_DATA_CONTAINER_ID)) return structuredDataContainerIdPart(
node,
variable
);
// for TimeseriesReferences
if (property.equals(SearchConstants.JSON_TIMESERIES_CONTAINER_ID)) return timeseriesContainerIdPart(node, variable);
if (property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_ID)) return referencedTimeseriesIdPart(
node,
variable
);
// for neighborhoodIds
if (
property.equals(SearchConstants.JSON_SUCCESSOR_IDS) ||
property.equals(SearchConstants.JSON_PREDECESSOR_IDS) ||
property.equals(SearchConstants.JSON_CHILDREN_IDS) ||
property.equals(SearchConstants.JSON_PARENT_IDS)
) return neighborhoodIdsPart(node, variable);
return null;
}
private static String byPart(JsonNode node, String variable) {
String ret = "(";
String by =
switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
case SearchConstants.JSON_CREATED_BY -> Neo4jLabels.CREATED_BY;
case SearchConstants.JSON_UPDATED_BY -> Neo4jLabels.UPDATED_BY;
default -> "";
};
ret =
ret + "EXISTS {MATCH (" + variable + ") - [:" + by + "] -> (u) WHERE toLower(u." + Neo4jLabels.USERNAME + ") ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE).toString().toLowerCase() + " ";
ret = ret + "})";
return ret;
}
private static String simpleQuintuplePropertyPart(JsonNode node, String variable) {
String ret = "(";
String quintupleProperty =
switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
case SearchConstants.JSON_REFERENCED_TIMESERIES_MEASUREMENT -> Neo4jLabels.MEASUREMENT;
case SearchConstants.JSON_REFERENCED_TIMESERIES_FIELD -> Neo4jLabels.FIELD;
case SearchConstants.JSON_REFERENCED_TIMESERIES_DEVICE -> Neo4jLabels.DEVICE;
case SearchConstants.JSON_REFERENCED_TIMESERIES_LOCATION -> Neo4jLabels.LOCATION;
case SearchConstants.JSON_REFERENCED_TIMESERIES_SYMBOLIC_NAME -> Neo4jLabels.SYMBOLIC_NAME;
default -> "";
};
ret =
ret +
"EXISTS {MATCH (" +
variable +
") - [:" +
Neo4jLabels.HAS_PAYLOAD +
"] -> (tt:" +
Neo4jLabels.TIMESERIES_TUPLE +
") WHERE tt." +
quintupleProperty +
" ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE).toString().toLowerCase() + " ";
ret = ret + "})";
return ret;
}
private static String neighborhoodIdsPart(JsonNode node, String variable) {
String operatorString = node.get(SearchConstants.OP_OPERATOR).textValue();
String neighborhoodProperty = node.get(SearchConstants.OP_PROPERTY).textValue();
String neighborhoodNeo4j =
switch (neighborhoodProperty) {
case SearchConstants.JSON_SUCCESSOR_IDS -> "-[:" + Neo4jLabels.HAS_SUCCESSOR + "]->";
case SearchConstants.JSON_PREDECESSOR_IDS -> "<-[:" + Neo4jLabels.HAS_SUCCESSOR + "]-";
case SearchConstants.JSON_CHILDREN_IDS -> "-[:" + Neo4jLabels.HAS_CHILD + "]->";
case SearchConstants.JSON_PARENT_IDS -> "<-[:" + Neo4jLabels.HAS_CHILD + "]-";
default -> "";
};
if (operatorString.equals(SearchConstants.JSON_CONTAINS)) return neighborhoodIdsContainsPart(
node,
variable,
neighborhoodNeo4j
);
if (operatorString.equals(SearchConstants.JSON_IS_CONTAINED_IN)) return neighborhoodIdsIsContainedInPart(
node,
variable,
neighborhoodNeo4j
);
if (operatorString.equals(SearchConstants.JSON_EQ)) return neighborhoodIdsEqualsPart(
node,
variable,
neighborhoodNeo4j
);
throw new ShepardParserException("illegal comparison operator " + operatorString);
}
private static String neighborhoodIdsEqualsPart(JsonNode node, String variable, String neighborhoodNeo4j) {
return (
"((" +
neighborhoodIdsIsContainedInPart(node, variable, neighborhoodNeo4j) +
") AND (" +
neighborhoodIdsContainsPart(node, variable, neighborhoodNeo4j) +
"))"
);
}
private static String neighborhoodIdsIsContainedInPart(JsonNode node, String variable, String neighborhoodNeo4j) {
String ret = "(";
ArrayList<Long> neighborIds = new ArrayList<Long>();
for (JsonNode jsonNode : node.get(SearchConstants.OP_VALUE)) {
neighborIds.add(jsonNode.longValue());
}
if (neighborIds.size() == 0) return "(NOT EXISTS{MATCH (" + variable + ")" + neighborhoodNeo4j + "(neighborObj)})";
String arrayString = "[";
for (int i = 0; i < neighborIds.size() - 1; i++) arrayString = arrayString + neighborIds.get(i) + ",";
arrayString = arrayString + neighborIds.getLast() + "]";
ret =
ret +
"NOT EXISTS{MATCH (" +
variable +
")" +
neighborhoodNeo4j +
"(neighborObj) WHERE (NOT id(neighborObj) IN " +
arrayString +
")})";
return ret;
}
private static String neighborhoodIdsContainsPart(JsonNode node, String variable, String neighborhoodNeo4j) {
String ret = "(";
ArrayList<Long> neighborIds = new ArrayList<Long>();
for (JsonNode jsonNode : node.get(SearchConstants.OP_VALUE)) {
neighborIds.add(jsonNode.longValue());
}
if (neighborIds.size() == 0) return "(1=1)";
for (int i = 0; i < neighborIds.size() - 1; i++) {
ret =
ret +
"(EXISTS {MATCH (" +
variable +
")" +
neighborhoodNeo4j +
"(neighborObj) WHERE id(neighborObj)=" +
neighborIds.get(i) +
"}) AND ";
}
ret =
ret +
"(EXISTS {MATCH (" +
variable +
")" +
neighborhoodNeo4j +
"(neighborObj) WHERE id(neighborObj)=" +
neighborIds.getLast() +
"}))";
return ret;
}
private static String iRIPart(JsonNode node, String variable) {
String ret = "(";
String iriType = node.get(SearchConstants.OP_PROPERTY).textValue();
ret =
ret +
"EXISTS {MATCH (" +
variable +
") - [] -> (sem:" +
Neo4jLabels.SEMANTIC_ANNOTATION +
") WHERE (sem." +
iriType +
" ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE);
ret = ret + ")})";
return ret;
}
private static String referencedTimeseriesIRIPart(JsonNode node, String variable) {
String ret = "(";
String iriType =
switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
case SearchConstants.JSON_REFERENCED_TIMESERIES_VALUE_IRI -> Neo4jLabels.VALUE_IRI;
case SearchConstants.JSON_REFERENCED_TIMESERIES_PROPERTY_IRI -> Neo4jLabels.PROPERTY_IRI;
default -> "";
};
ret =
ret +
"EXISTS {MATCH (" +
variable +
") - [:" +
Neo4jLabels.HAS_PAYLOAD +
"] -> (tt:" +
Neo4jLabels.TIMESERIES_TUPLE +
") <- [:" +
Neo4jLabels.HAS_TIMESERIES_TUPLE +
"] - (ts:" +
Neo4jLabels.TIMESERIES +
") - [:" +
Neo4jLabels.HAS_ANNOTATION +
"] -> (sem:" +
Neo4jLabels.SEMANTIC_ANNOTATION +
"), (" +
variable +
") - [:" +
Neo4jLabels.IS_IN_CONTAINER +
"] -> (tsc:" +
Neo4jLabels.TIMESERIES_CONTAINER +
") <- [:" +
Neo4jLabels.IS_IN_CONTAINER +
"] - (ts:" +
Neo4jLabels.TIMESERIES +
") WHERE sem." +
iriType +
" ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE);
ret = ret + "})";
return ret;
}
private static String referencedTimeseriesIdPart(JsonNode node, String variable) {
String ret = "(";
ret =
ret +
"EXISTS {MATCH (" +
variable +
")-[:" +
Neo4jLabels.HAS_PAYLOAD +
"]->(tt:" +
Neo4jLabels.TIMESERIES_TUPLE +
")<-[:" +
Neo4jLabels.HAS_TIMESERIES_TUPLE +
"]-(ts:" +
Neo4jLabels.TIMESERIES +
"), (" +
variable +
") - [:" +
Neo4jLabels.IS_IN_CONTAINER +
"] -> (tsc:" +
Neo4jLabels.TIMESERIES_CONTAINER +
") <- [:" +
Neo4jLabels.IS_IN_CONTAINER +
"] - (ts:" +
Neo4jLabels.TIMESERIES +
") WHERE ts." +
Neo4jLabels.TIMESERIES_ID +
" ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
ret = ret + "})";
return ret;
}
private static String referencedTimeseriesAnnotationPart(JsonNode node, String variable) {
String propertyType =
switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION -> Neo4jLabels.PROPERTY_NAME;
case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI -> Neo4jLabels.PROPERTY_IRI;
default -> "";
};
String valueType =
switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION -> Neo4jLabels.VALUE_NAME;
case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI -> Neo4jLabels.VALUE_IRI;
default -> "";
};
String[] pair = CypherQueryHelper.splitByExactlyOneDoubleColon(node.get(SearchConstants.OP_VALUE).textValue());
String property = pair[0];
String value = pair[1];
String ret = "(";
ret =
ret +
"EXISTS {MATCH (" +
variable +
") - [:" +
Neo4jLabels.HAS_PAYLOAD +
"] -> (tt:" +
Neo4jLabels.TIMESERIES_TUPLE +
") <- [:" +
Neo4jLabels.HAS_TIMESERIES_TUPLE +
"] - (ts:" +
Neo4jLabels.TIMESERIES +
") - [:" +
Neo4jLabels.HAS_ANNOTATION +
"] -> (sem:" +
Neo4jLabels.SEMANTIC_ANNOTATION +
"), (" +
variable +
") - [:is_in_container] -> (tsc:TimeseriesContainer) <- [:is_in_container] - (ts:Timeseries) WHERE sem." +
propertyType +
" ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + "\"" + property + "\"" + " AND sem." + valueType + " ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + "\"" + value + "\"";
ret = ret + "})";
return ret;
}
private static String timeseriesContainerIdPart(JsonNode node, String variable) {
return containerIdPart(node, variable, Neo4jLabels.TIMESERIES_CONTAINER);
}
private static String structuredDataContainerIdPart(JsonNode node, String variable) {
return containerIdPart(node, variable, Neo4jLabels.STRUCTURED_DATA_CONTAINER);
}
private static String fileContainerIdPart(JsonNode node, String variable) {
return containerIdPart(node, variable, Neo4jLabels.FILE_CONTAINER);
}
private static String containerIdPart(JsonNode node, String variable, String containerType) {
String ret = "(";
ret =
ret +
"EXISTS {MATCH (" +
variable +
")-[:" +
Neo4jLabels.IS_IN_CONTAINER +
"]->(refCon:" +
containerType +
") WHERE id(refCon) ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
ret = ret + "})";
return ret;
}
private static String referencedDataObjectNeo4jIdPart(JsonNode node, String variable) {
String ret = "(";
ret =
ret +
"EXISTS {MATCH (" +
variable +
")-[:" +
Neo4jLabels.POINTS_TO +
"]->(refDo:" +
Neo4jLabels.DATA_OBJECT +
") WHERE id(refDo) ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
ret = ret + "})";
return ret;
}
private static String referencedCollectionNeo4jIdPart(JsonNode node, String variable) {
String ret = "(";
ret =
ret +
"EXISTS {MATCH (" +
variable +
")-[:" +
Neo4jLabels.POINTS_TO +
"]->(refCol:" +
Neo4jLabels.COLLECTION +
") WHERE id(refCol) ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
ret = ret + "})";
return ret;
}
private static String neo4jIdPart(JsonNode node, String variable) {
String ret = "(";
ret = ret + "id(" + variable + ") ";
ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
ret = ret + valuePart(node);
ret = ret + ")";
return ret;
}
private static String operatorString(JsonNode node) {
String operator = node.textValue();
return switch (operator) {
case SearchConstants.JSON_EQ -> "=";
case SearchConstants.JSON_CONTAINS -> "contains";
case SearchConstants.JSON_GT -> ">";
case SearchConstants.JSON_LT -> "<";
case SearchConstants.JSON_GE -> ">=";
case SearchConstants.JSON_LE -> "<=";
case SearchConstants.JSON_IN -> "IN";
case SearchConstants.JSON_NE -> "<>";
case SearchConstants.JSON_REGMATCH -> "=~";
default -> throw new ShepardParserException("unknown comparison operator " + operator);
};
}
private static String collectionDataObjectMatchPartWithoutVersion() {
String ret =
"MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:has_dataobject]->(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")";
return ret;
}
private static String collectionNeo4jIdWherePart(Long collectionId) {
String ret = "(id(" + SearchConstants.COLLECTION_IN_QUERY + ") = " + collectionId + ")";
return ret;
}
private static String notDeletedPart(String variable) {
String ret = "(" + variable + ".deleted = FALSE)";
return ret;
}
private static String collectionDataObjectNeo4jIdWherePart(Long collectionId, Long dataObjectId) {
String ret =
"(id(" +
SearchConstants.COLLECTION_IN_QUERY +
") = " +
collectionId +
" AND id(" +
SearchConstants.DATAOBJECT_IN_QUERY +
") = " +
dataObjectId +
")";
return ret;
}
private static String collectionDataObjectTraversalNeo4jIdWherePart(Long collectionId, Long dataObjectId) {
String ret =
"(id(" + SearchConstants.COLLECTION_IN_QUERY + ") = " + collectionId + " AND id(d) = " + dataObjectId + ")";
return ret;
}
private static String basicReferenceMatchPartWithoutVersion() {
String ret =
"MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_REFERENCE +
"]->(" +
SearchConstants.REFERENCE_IN_QUERY +
":" +
Neo4jLabels.BASIC_REFERENCE +
")";
return ret;
}
public static String collectionSelectionQueryWithNeo4jId(
String searchBodyQuery,
String userName,
SortingHelper sortOrder
) {
String ret = "MATCH (" + SearchConstants.COLLECTION_IN_QUERY + ":" + Neo4jLabels.COLLECTION + ")";
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.COLLECTION_IN_QUERY);
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.COLLECTION_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(userName);
if (sortOrder.hasOrderByAttribute()) {
ret +=
" " +
CypherQueryHelper.getOrderByPart(
SearchConstants.COLLECTION_IN_QUERY,
sortOrder.getOrderByAttribute(),
sortOrder.getOrderDesc()
);
}
return ret;
}
public static String containerSelectionQueryWithNeo4jId(
String JSONQuery,
ContainerType containerType,
SortingHelper sortOrder,
String userName
) {
String ret = "MATCH (" + containerType.getTypeAlias() + ":" + containerType.getTypeName() + ")";
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(JSONQuery, containerType.getTypeAlias());
ret = ret + " AND ";
ret = ret + notDeletedPart(containerType.getTypeAlias());
ret = ret + " AND ";
ret = ret + CypherQueryHelper.getReadableByQuery(containerType.getTypeAlias(), userName);
if (sortOrder.hasOrderByAttribute()) {
ret +=
" " +
CypherQueryHelper.getOrderByPart(
containerType.getTypeAlias(),
sortOrder.getOrderByAttribute(),
sortOrder.getOrderDesc()
);
}
return ret;
}
public static String collectionDataObjectSelectionQueryWithNeo4jId(
Long collectionId,
String searchBodyQuery,
String username
) {
String ret = "";
ret = ret + collectionDataObjectMatchPartWithoutVersion();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + collectionNeo4jIdWherePart(collectionId);
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
public static String collectionDataObjectDataObjectSelectionQueryWithNeo4jId(
SearchScope scope,
TraversalRules traversalRule,
String searchBodyQuery,
String username
) {
String ret = "";
ret = ret + collectionDataObjectDataObjectMatchPartWithoutVersion(traversalRule);
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + collectionDataObjectTraversalNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
public static String collectionDataObjectDataObjectSelectionQueryWithNeo4jId(
SearchScope scope,
String searchBodyQuery,
String username
) {
String ret = "";
ret = ret + collectionDataObjectMatchPartWithoutVersion();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + collectionDataObjectNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
public static String dataObjectSelectionQueryWithNeo4jId(String searchBodyQuery, String username) {
String ret = "";
ret = ret + collectionDataObjectMatchPartWithoutVersion();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
public static String basicReferenceSelectionQueryWithNeo4jId(String searchBodyQuery, String username) {
String ret = "";
ret = ret + basicReferenceMatchPartWithoutVersion();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
public static String collectionBasicReferenceSelectionQueryWithNeo4jId(
String searchBodyQuery,
Long collectionId,
String username
) {
String ret = "";
ret = ret + basicReferenceMatchPartWithoutVersion();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + collectionNeo4jIdWherePart(collectionId);
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
public static String collectionDataObjectReferenceSelectionQueryWithNeo4jId(
SearchScope scope,
String searchBodyQuery,
String username
) {
String ret = "";
ret = ret + basicReferenceMatchPartWithoutVersion();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + collectionDataObjectNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
public static String collectionDataObjectBasicReferenceSelectionQueryWithNeo4jId(
SearchScope scope,
TraversalRules traversalRule,
String searchBodyQuery,
String username
) {
String ret = "";
ret = ret + collectionDataObjectBasicReferenceMatchPartWithoutVersion(traversalRule);
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + collectionDataObjectTraversalNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
ret = ret + " AND ";
ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
ret = ret + " AND ";
ret = ret + readableByPart(username);
return ret;
}
private static String collectionDataObjectDataObjectMatchPartWithoutVersion(TraversalRules traversalRule) {
String ret =
switch (traversalRule) {
case children -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_CHILD +
"*0..]->(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")";
case parents -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:" +
Neo4jLabels.DATA_OBJECT +
")<-[:" +
Neo4jLabels.HAS_CHILD +
"*0..]-(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")";
case successors -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_SUCCESSOR +
"*0..]->(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")";
case predecessors -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:DataObject)<-[:has_successor*0..]-(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")";
default -> "";
};
return ret;
}
private static String collectionDataObjectBasicReferenceMatchPartWithoutVersion(TraversalRules traversalRule) {
String ret =
switch (traversalRule) {
case children -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_CHILD +
"*0..]->(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_REFERENCE +
"]->(" +
SearchConstants.REFERENCE_IN_QUERY +
":" +
Neo4jLabels.BASIC_REFERENCE +
")";
case parents -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:" +
Neo4jLabels.DATA_OBJECT +
")<-[:" +
Neo4jLabels.HAS_CHILD +
"*0..]-(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_REFERENCE +
"]->(" +
SearchConstants.REFERENCE_IN_QUERY +
":" +
Neo4jLabels.BASIC_REFERENCE +
")";
case successors -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_SUCCESSOR +
"*0..]->(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_REFERENCE +
"]->(" +
SearchConstants.REFERENCE_IN_QUERY +
":" +
Neo4jLabels.BASIC_REFERENCE +
")";
case predecessors -> "MATCH (" +
SearchConstants.COLLECTION_IN_QUERY +
":" +
Neo4jLabels.COLLECTION +
")-[:" +
Neo4jLabels.HAS_DATAOBJECT +
"]->(d:" +
Neo4jLabels.DATA_OBJECT +
")<-[:" +
Neo4jLabels.HAS_SUCCESSOR +
"*0..]-(" +
SearchConstants.DATAOBJECT_IN_QUERY +
":" +
Neo4jLabels.DATA_OBJECT +
")-[:" +
Neo4jLabels.HAS_REFERENCE +
"]->(" +
SearchConstants.REFERENCE_IN_QUERY +
":" +
Neo4jLabels.BASIC_REFERENCE +
")";
default -> "";
};
return ret;
}
private static String readableByPart(String username) {
String variable = SearchConstants.COLLECTION_IN_QUERY;
return CypherQueryHelper.getReadableByQuery(variable, username);
}
public static String userSelectionQuery(String query) {
String ret = "";
ret = ret + userMatchPart();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(query, Constants.USER_IN_QUERY);
return ret;
}
private static String userMatchPart() {
String ret = "";
ret = ret + "MATCH (" + Constants.USER_IN_QUERY + ":" + Neo4jLabels.USER + ")";
return ret;
}
public static String userGroupSelectionQuery(String query) {
String ret = "";
ret = ret + userGroupMatchPart();
ret = ret + " WHERE ";
ret = ret + getNeo4jWithNeo4jIdString(query, Constants.USERGROUP_IN_QUERY);
return ret;
}
private static String userGroupMatchPart() {
String ret = "";
ret = ret + "MATCH (" + Constants.USERGROUP_IN_QUERY + ":" + Neo4jLabels.USER_GROUP + ")";
return ret;
}
}