1 package de.dlr.shepard.common.search.query;
2
3 import com.fasterxml.jackson.core.JsonProcessingException;
4 import com.fasterxml.jackson.databind.JsonNode;
5 import com.fasterxml.jackson.databind.ObjectMapper;
6 import de.dlr.shepard.common.exceptions.ShepardParserException;
7 import de.dlr.shepard.common.neo4j.entities.ContainerType;
8 import de.dlr.shepard.common.search.io.SearchScope;
9 import de.dlr.shepard.common.util.Constants;
10 import de.dlr.shepard.common.util.CypherQueryHelper;
11 import de.dlr.shepard.common.util.Neo4jLabels;
12 import de.dlr.shepard.common.util.SearchConstants;
13 import de.dlr.shepard.common.util.SortingHelper;
14 import de.dlr.shepard.common.util.TraversalRules;
15 import java.util.ArrayList;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.NoSuchElementException;
19
20 public class Neo4jQueryBuilder {
21
22 private static final List<String> BOOLEAN_OPERATORS = List.of(
23 SearchConstants.JSON_AND,
24 SearchConstants.JSON_OR,
25 SearchConstants.JSON_NOT,
26 SearchConstants.JSON_XOR
27 );
28 private static final List<String> OP_ATTRIBUTES = List.of(
29 SearchConstants.OP_PROPERTY,
30 SearchConstants.OP_VALUE,
31 SearchConstants.OP_OPERATOR
32 );
33 private static final List<String> NOT_ID_PROPERTIES = List.of(
34 SearchConstants.JSON_CREATED_BY,
35 SearchConstants.JSON_UPDATED_BY,
36 SearchConstants.JSON_VALUE_IRI,
37 SearchConstants.JSON_PROPERTY_IRI,
38 SearchConstants.JSON_CREATED_AT,
39 SearchConstants.JSON_UPDATED_AT,
40 SearchConstants.JSON_HAS_ANNOTATION,
41 SearchConstants.JSON_HAS_ANNOTATION_IRI,
42 SearchConstants.JSON_TIMESERIES_REFERENCE_START,
43 SearchConstants.JSON_TIMESERIES_REFERENCE_END,
44 SearchConstants.JSON_REFERENCED_TIMESERIES_PROPERTY_IRI,
45 SearchConstants.JSON_REFERENCED_TIMESERIES_VALUE_IRI,
46 SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION,
47 SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI
48 );
49
50 private static final List<String> ID_PROPERTIES = List.of(
51 SearchConstants.JSON_ID,
52 SearchConstants.JSON_REFERENCED_COLLECTION_ID,
53 SearchConstants.JSON_REFERENCED_DATA_OBJECT_ID,
54 SearchConstants.JSON_REFERENCED_TIMESERIES_ID,
55 SearchConstants.JSON_FILE_CONTAINER_ID,
56 SearchConstants.JSON_STRUCTURED_DATA_CONTAINER_ID,
57 SearchConstants.JSON_TIMESERIES_CONTAINER_ID,
58 SearchConstants.JSON_SUCCESSOR_IDS,
59 SearchConstants.JSON_PREDECESSOR_IDS,
60 SearchConstants.JSON_CHILDREN_IDS,
61 SearchConstants.JSON_PARENT_IDS
62 );
63
64 private static final List<String> QUINTUPLE_PROPERTIES = List.of(
65 SearchConstants.JSON_REFERENCED_TIMESERIES_MEASUREMENT,
66 SearchConstants.JSON_REFERENCED_TIMESERIES_DEVICE,
67 SearchConstants.JSON_REFERENCED_TIMESERIES_LOCATION,
68 SearchConstants.JSON_REFERENCED_TIMESERIES_SYMBOLIC_NAME,
69 SearchConstants.JSON_REFERENCED_TIMESERIES_FIELD
70 );
71
72 private static String getNeo4jWithNeo4jIdString(String jsonQuery, String variable) {
73 ObjectMapper objectMapper = new ObjectMapper();
74 JsonNode jsonNode = null;
75 try {
76 jsonNode = objectMapper.readValue(jsonQuery, JsonNode.class);
77 } catch (JsonProcessingException e) {
78 throw new ShepardParserException("could not parse JSON " + e.getMessage());
79 }
80 return getNeo4jStringWithNeo4jId(jsonNode, variable);
81 }
82
83 private static String getNeo4jStringWithNeo4jId(JsonNode rootNode, String variable) {
84 String op = "";
85 try {
86 op = rootNode.fieldNames().next();
87 } catch (NoSuchElementException e) {
88 throw new ShepardParserException("error in parsing" + e.getMessage());
89 }
90 if (OP_ATTRIBUTES.contains(op)) {
91 return primitiveClauseWithNeo4jId(rootNode, variable);
92 }
93 return complexClauseWithNeo4jId(rootNode, op, variable);
94 }
95
96 private static String complexClauseWithNeo4jId(JsonNode node, String operator, String variable) {
97 if (!BOOLEAN_OPERATORS.contains(operator)) throw new ShepardParserException(
98 "unknown boolean operator: " + operator
99 );
100 if (operator.equals(SearchConstants.JSON_NOT)) return notClauseWithNeo4jId(node, variable);
101 else return multaryClauseWithNeo4jId(node, operator, variable);
102 }
103
104 private static String multaryClauseWithNeo4jId(JsonNode node, String operator, String variable) {
105 Iterator<JsonNode> argumentsArray = node.get(operator).elements();
106 String firstArgument = getNeo4jStringWithNeo4jId(argumentsArray.next(), variable);
107 String ret = "(" + firstArgument;
108 while (argumentsArray.hasNext()) {
109 ret = ret + " " + operator + " " + getNeo4jStringWithNeo4jId(argumentsArray.next(), variable);
110 }
111 ret = ret + ")";
112 return ret;
113 }
114
115 private static String notClauseWithNeo4jId(JsonNode node, String variable) {
116 JsonNode body = node.get(SearchConstants.JSON_NOT);
117 return "(NOT(" + getNeo4jStringWithNeo4jId(body, variable) + "))";
118 }
119
120 private static String primitiveClauseWithNeo4jId(JsonNode node, String variable) {
121 String property = node.get(SearchConstants.OP_PROPERTY).textValue();
122 property = changeAttributesDelimiter(property);
123 if (NOT_ID_PROPERTIES.contains(property)) return simpleNotIdPropertyPart(node, variable);
124 if (ID_PROPERTIES.contains(property)) return simpleIdPropertyPart(node, variable);
125 if (QUINTUPLE_PROPERTIES.contains(property)) return simpleQuintuplePropertyPart(node, variable);
126 String ret = "(";
127 ret = ret + "toLower(" + variable + ".`" + property + "`) ";
128 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
129 ret = ret + valuePart(node).toLowerCase();
130 ret = ret + ")";
131 return ret;
132 }
133
134
135
136
137
138
139
140
141
142
143
144 private static String changeAttributesDelimiter(String property) {
145 if (property.startsWith("attributes.")) {
146 return property.replaceFirst("attributes.", "attributes||");
147 }
148 return property;
149 }
150
151 private static String valuePart(JsonNode node) {
152 String ret = "";
153 if (node.get(SearchConstants.OP_OPERATOR).textValue().equals(SearchConstants.JSON_IN)) {
154 ret = ret + "[";
155 Iterator<JsonNode> setArray = node.get(SearchConstants.OP_VALUE).elements();
156 if (setArray.hasNext()) {
157 ret = ret + setArray.next();
158 while (setArray.hasNext()) ret = ret + ", " + setArray.next();
159 ret = ret + "]";
160 } else {
161 ret = ret + "]";
162 }
163 } else ret = ret + node.get(SearchConstants.OP_VALUE);
164 return ret;
165 }
166
167 private static String simpleNotIdPropertyPart(JsonNode node, String variable) {
168 String property = node.get(SearchConstants.OP_PROPERTY).textValue();
169
170 if (
171 property.equals(SearchConstants.JSON_CREATED_BY) || property.equals(SearchConstants.JSON_UPDATED_BY)
172 ) return byPart(node, variable);
173
174 if (
175 property.equals(SearchConstants.JSON_CREATED_AT) || property.equals(SearchConstants.JSON_UPDATED_AT)
176 ) return atPart(node, variable);
177
178 if (
179 property.equals(SearchConstants.JSON_VALUE_IRI) || property.equals(SearchConstants.JSON_PROPERTY_IRI)
180 ) return iRIPart(node, variable);
181
182 if (property.equals(SearchConstants.JSON_HAS_ANNOTATION)) return hasAnnotationPart(node, variable);
183 if (property.equals(SearchConstants.JSON_HAS_ANNOTATION_IRI)) return hasAnnotationIRIPart(node, variable);
184
185 if (
186 property.equals(SearchConstants.JSON_TIMESERIES_REFERENCE_START) ||
187 property.equals(SearchConstants.JSON_TIMESERIES_REFERENCE_END)
188 ) return timeFramePart(node, variable);
189
190 if (
191 property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_VALUE_IRI) ||
192 property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_PROPERTY_IRI)
193 ) return referencedTimeseriesIRIPart(node, variable);
194 if (
195 property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION) ||
196 property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI)
197 ) return referencedTimeseriesAnnotationPart(node, variable);
198 return null;
199 }
200
201
202
203
204
205
206
207
208
209 private static String atPart(JsonNode node, String variable) {
210 String ret = "(";
211 String at =
212 switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
213 case SearchConstants.JSON_CREATED_AT -> Neo4jLabels.CREATED_AT;
214 case SearchConstants.JSON_UPDATED_AT -> Neo4jLabels.UPDATED_AT;
215 default -> "";
216 };
217 ret = ret + variable + "." + at + " ";
218 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
219 ret = ret + valuePart(node).toLowerCase();
220 ret = ret + ")";
221 return ret;
222 }
223
224 private static String timeFramePart(JsonNode node, String variable) {
225 String ret = "(";
226 String timeFrame =
227 switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
228 case SearchConstants.JSON_TIMESERIES_REFERENCE_START -> Neo4jLabels.TIMESERIES_REFERENCE_START;
229 case SearchConstants.JSON_TIMESERIES_REFERENCE_END -> Neo4jLabels.TIMESERIES_REFERENCE_END;
230 default -> "";
231 };
232 ret = ret + variable + "." + timeFrame + " ";
233 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
234 ret = ret + valuePart(node).toLowerCase();
235 ret = ret + ")";
236 return ret;
237 }
238
239 private static String hasAnnotationIRIPart(JsonNode node, String variable) {
240 String[] pair = CypherQueryHelper.splitByExactlyOneDoubleColon(node.get(SearchConstants.OP_VALUE).textValue());
241 String propertyIRI = pair[0];
242 String valueIRI = pair[1];
243 String ret = "(";
244 ret =
245 ret +
246 "EXISTS {MATCH (" +
247 variable +
248 ") - [:" +
249 Neo4jLabels.HAS_ANNOTATION +
250 "] -> (sem:" +
251 Neo4jLabels.SEMANTIC_ANNOTATION +
252 ") WHERE (sem." +
253 Neo4jLabels.PROPERTY_IRI +
254 " ";
255 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " \"" + propertyIRI + "\" AND ";
256 ret =
257 ret +
258 " sem." +
259 Neo4jLabels.VALUE_IRI +
260 " " +
261 operatorString(node.get(SearchConstants.OP_OPERATOR)) +
262 " \"" +
263 valueIRI;
264 ret = ret + "\")})";
265 return ret;
266 }
267
268 private static String hasAnnotationPart(JsonNode node, String variable) {
269 String[] pair = CypherQueryHelper.splitByExactlyOneDoubleColon(node.get(SearchConstants.OP_VALUE).textValue());
270 String propertyName = pair[0];
271 String valueName = pair[1];
272 String ret = "(";
273 ret =
274 ret +
275 "EXISTS {MATCH (" +
276 variable +
277 ") - [:" +
278 Neo4jLabels.HAS_ANNOTATION +
279 "] -> (sem:" +
280 Neo4jLabels.SEMANTIC_ANNOTATION +
281 ") WHERE (sem." +
282 Neo4jLabels.PROPERTY_NAME +
283 " ";
284 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " \"" + propertyName + "\" AND ";
285 ret =
286 ret +
287 " sem." +
288 Neo4jLabels.VALUE_NAME +
289 " " +
290 operatorString(node.get(SearchConstants.OP_OPERATOR)) +
291 " \"" +
292 valueName;
293 ret = ret + "\")})";
294 return ret;
295 }
296
297 private static String simpleIdPropertyPart(JsonNode node, String variable) {
298 String property = node.get(SearchConstants.OP_PROPERTY).textValue();
299
300 if (property.equals(SearchConstants.JSON_ID)) return neo4jIdPart(node, variable);
301
302 if (property.equals(SearchConstants.JSON_REFERENCED_COLLECTION_ID)) return referencedCollectionNeo4jIdPart(
303 node,
304 variable
305 );
306
307 if (property.equals(SearchConstants.JSON_REFERENCED_DATA_OBJECT_ID)) return referencedDataObjectNeo4jIdPart(
308 node,
309 variable
310 );
311
312 if (property.equals(SearchConstants.JSON_FILE_CONTAINER_ID)) return fileContainerIdPart(node, variable);
313
314 if (property.equals(SearchConstants.JSON_STRUCTURED_DATA_CONTAINER_ID)) return structuredDataContainerIdPart(
315 node,
316 variable
317 );
318
319 if (property.equals(SearchConstants.JSON_TIMESERIES_CONTAINER_ID)) return timeseriesContainerIdPart(node, variable);
320 if (property.equals(SearchConstants.JSON_REFERENCED_TIMESERIES_ID)) return referencedTimeseriesIdPart(
321 node,
322 variable
323 );
324
325 if (
326 property.equals(SearchConstants.JSON_SUCCESSOR_IDS) ||
327 property.equals(SearchConstants.JSON_PREDECESSOR_IDS) ||
328 property.equals(SearchConstants.JSON_CHILDREN_IDS) ||
329 property.equals(SearchConstants.JSON_PARENT_IDS)
330 ) return neighborhoodIdsPart(node, variable);
331 return null;
332 }
333
334 private static String byPart(JsonNode node, String variable) {
335 String ret = "(";
336 String by =
337 switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
338 case SearchConstants.JSON_CREATED_BY -> Neo4jLabels.CREATED_BY;
339 case SearchConstants.JSON_UPDATED_BY -> Neo4jLabels.UPDATED_BY;
340 default -> "";
341 };
342 ret =
343 ret + "EXISTS {MATCH (" + variable + ") - [:" + by + "] -> (u) WHERE toLower(u." + Neo4jLabels.USERNAME + ") ";
344 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
345 ret = ret + node.get(SearchConstants.OP_VALUE).toString().toLowerCase() + " ";
346 ret = ret + "})";
347 return ret;
348 }
349
350 private static String simpleQuintuplePropertyPart(JsonNode node, String variable) {
351 String ret = "(";
352 String quintupleProperty =
353 switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
354 case SearchConstants.JSON_REFERENCED_TIMESERIES_MEASUREMENT -> Neo4jLabels.MEASUREMENT;
355 case SearchConstants.JSON_REFERENCED_TIMESERIES_FIELD -> Neo4jLabels.FIELD;
356 case SearchConstants.JSON_REFERENCED_TIMESERIES_DEVICE -> Neo4jLabels.DEVICE;
357 case SearchConstants.JSON_REFERENCED_TIMESERIES_LOCATION -> Neo4jLabels.LOCATION;
358 case SearchConstants.JSON_REFERENCED_TIMESERIES_SYMBOLIC_NAME -> Neo4jLabels.SYMBOLIC_NAME;
359 default -> "";
360 };
361 ret =
362 ret +
363 "EXISTS {MATCH (" +
364 variable +
365 ") - [:" +
366 Neo4jLabels.HAS_PAYLOAD +
367 "] -> (tt:" +
368 Neo4jLabels.TIMESERIES_TUPLE +
369 ") WHERE tt." +
370 quintupleProperty +
371 " ";
372 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
373 ret = ret + node.get(SearchConstants.OP_VALUE).toString().toLowerCase() + " ";
374 ret = ret + "})";
375 return ret;
376 }
377
378 private static String neighborhoodIdsPart(JsonNode node, String variable) {
379 String operatorString = node.get(SearchConstants.OP_OPERATOR).textValue();
380 String neighborhoodProperty = node.get(SearchConstants.OP_PROPERTY).textValue();
381 String neighborhoodNeo4j =
382 switch (neighborhoodProperty) {
383 case SearchConstants.JSON_SUCCESSOR_IDS -> "-[:" + Neo4jLabels.HAS_SUCCESSOR + "]->";
384 case SearchConstants.JSON_PREDECESSOR_IDS -> "<-[:" + Neo4jLabels.HAS_SUCCESSOR + "]-";
385 case SearchConstants.JSON_CHILDREN_IDS -> "-[:" + Neo4jLabels.HAS_CHILD + "]->";
386 case SearchConstants.JSON_PARENT_IDS -> "<-[:" + Neo4jLabels.HAS_CHILD + "]-";
387 default -> "";
388 };
389 if (operatorString.equals(SearchConstants.JSON_CONTAINS)) return neighborhoodIdsContainsPart(
390 node,
391 variable,
392 neighborhoodNeo4j
393 );
394 if (operatorString.equals(SearchConstants.JSON_IS_CONTAINED_IN)) return neighborhoodIdsIsContainedInPart(
395 node,
396 variable,
397 neighborhoodNeo4j
398 );
399 if (operatorString.equals(SearchConstants.JSON_EQ)) return neighborhoodIdsEqualsPart(
400 node,
401 variable,
402 neighborhoodNeo4j
403 );
404 throw new ShepardParserException("illegal comparison operator " + operatorString);
405 }
406
407 private static String neighborhoodIdsEqualsPart(JsonNode node, String variable, String neighborhoodNeo4j) {
408 return (
409 "((" +
410 neighborhoodIdsIsContainedInPart(node, variable, neighborhoodNeo4j) +
411 ") AND (" +
412 neighborhoodIdsContainsPart(node, variable, neighborhoodNeo4j) +
413 "))"
414 );
415 }
416
417 private static String neighborhoodIdsIsContainedInPart(JsonNode node, String variable, String neighborhoodNeo4j) {
418 String ret = "(";
419 ArrayList<Long> neighborIds = new ArrayList<Long>();
420 for (JsonNode jsonNode : node.get(SearchConstants.OP_VALUE)) {
421 neighborIds.add(jsonNode.longValue());
422 }
423 if (neighborIds.size() == 0) return "(NOT EXISTS{MATCH (" + variable + ")" + neighborhoodNeo4j + "(neighborObj)})";
424 String arrayString = "[";
425 for (int i = 0; i < neighborIds.size() - 1; i++) arrayString = arrayString + neighborIds.get(i) + ",";
426 arrayString = arrayString + neighborIds.getLast() + "]";
427 ret =
428 ret +
429 "NOT EXISTS{MATCH (" +
430 variable +
431 ")" +
432 neighborhoodNeo4j +
433 "(neighborObj) WHERE (NOT id(neighborObj) IN " +
434 arrayString +
435 ")})";
436 return ret;
437 }
438
439 private static String neighborhoodIdsContainsPart(JsonNode node, String variable, String neighborhoodNeo4j) {
440 String ret = "(";
441 ArrayList<Long> neighborIds = new ArrayList<Long>();
442 for (JsonNode jsonNode : node.get(SearchConstants.OP_VALUE)) {
443 neighborIds.add(jsonNode.longValue());
444 }
445 if (neighborIds.size() == 0) return "(1=1)";
446 for (int i = 0; i < neighborIds.size() - 1; i++) {
447 ret =
448 ret +
449 "(EXISTS {MATCH (" +
450 variable +
451 ")" +
452 neighborhoodNeo4j +
453 "(neighborObj) WHERE id(neighborObj)=" +
454 neighborIds.get(i) +
455 "}) AND ";
456 }
457 ret =
458 ret +
459 "(EXISTS {MATCH (" +
460 variable +
461 ")" +
462 neighborhoodNeo4j +
463 "(neighborObj) WHERE id(neighborObj)=" +
464 neighborIds.getLast() +
465 "}))";
466 return ret;
467 }
468
469 private static String iRIPart(JsonNode node, String variable) {
470 String ret = "(";
471 String iriType = node.get(SearchConstants.OP_PROPERTY).textValue();
472 ret =
473 ret +
474 "EXISTS {MATCH (" +
475 variable +
476 ") - [] -> (sem:" +
477 Neo4jLabels.SEMANTIC_ANNOTATION +
478 ") WHERE (sem." +
479 iriType +
480 " ";
481 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
482 ret = ret + node.get(SearchConstants.OP_VALUE);
483 ret = ret + ")})";
484 return ret;
485 }
486
487 private static String referencedTimeseriesIRIPart(JsonNode node, String variable) {
488 String ret = "(";
489 String iriType =
490 switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
491 case SearchConstants.JSON_REFERENCED_TIMESERIES_VALUE_IRI -> Neo4jLabels.VALUE_IRI;
492 case SearchConstants.JSON_REFERENCED_TIMESERIES_PROPERTY_IRI -> Neo4jLabels.PROPERTY_IRI;
493 default -> "";
494 };
495 ret =
496 ret +
497 "EXISTS {MATCH (" +
498 variable +
499 ") - [:" +
500 Neo4jLabels.HAS_PAYLOAD +
501 "] -> (tt:" +
502 Neo4jLabels.TIMESERIES_TUPLE +
503 ") <- [:" +
504 Neo4jLabels.HAS_TIMESERIES_TUPLE +
505 "] - (ts:" +
506 Neo4jLabels.TIMESERIES +
507 ") - [:" +
508 Neo4jLabels.HAS_ANNOTATION +
509 "] -> (sem:" +
510 Neo4jLabels.SEMANTIC_ANNOTATION +
511 "), (" +
512 variable +
513 ") - [:" +
514 Neo4jLabels.IS_IN_CONTAINER +
515 "] -> (tsc:" +
516 Neo4jLabels.TIMESERIES_CONTAINER +
517 ") <- [:" +
518 Neo4jLabels.IS_IN_CONTAINER +
519 "] - (ts:" +
520 Neo4jLabels.TIMESERIES +
521 ") WHERE sem." +
522 iriType +
523 " ";
524 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
525 ret = ret + node.get(SearchConstants.OP_VALUE);
526 ret = ret + "})";
527 return ret;
528 }
529
530 private static String referencedTimeseriesIdPart(JsonNode node, String variable) {
531 String ret = "(";
532 ret =
533 ret +
534 "EXISTS {MATCH (" +
535 variable +
536 ")-[:" +
537 Neo4jLabels.HAS_PAYLOAD +
538 "]->(tt:" +
539 Neo4jLabels.TIMESERIES_TUPLE +
540 ")<-[:" +
541 Neo4jLabels.HAS_TIMESERIES_TUPLE +
542 "]-(ts:" +
543 Neo4jLabels.TIMESERIES +
544 "), (" +
545 variable +
546 ") - [:" +
547 Neo4jLabels.IS_IN_CONTAINER +
548 "] -> (tsc:" +
549 Neo4jLabels.TIMESERIES_CONTAINER +
550 ") <- [:" +
551 Neo4jLabels.IS_IN_CONTAINER +
552 "] - (ts:" +
553 Neo4jLabels.TIMESERIES +
554 ") WHERE ts." +
555 Neo4jLabels.TIMESERIES_ID +
556 " ";
557 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
558 ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
559 ret = ret + "})";
560 return ret;
561 }
562
563 private static String referencedTimeseriesAnnotationPart(JsonNode node, String variable) {
564 String propertyType =
565 switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
566 case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION -> Neo4jLabels.PROPERTY_NAME;
567 case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI -> Neo4jLabels.PROPERTY_IRI;
568 default -> "";
569 };
570 String valueType =
571 switch (node.get(SearchConstants.OP_PROPERTY).textValue()) {
572 case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION -> Neo4jLabels.VALUE_NAME;
573 case SearchConstants.JSON_REFERENCED_TIMESERIES_HAS_ANNOTATION_IRI -> Neo4jLabels.VALUE_IRI;
574 default -> "";
575 };
576 String[] pair = CypherQueryHelper.splitByExactlyOneDoubleColon(node.get(SearchConstants.OP_VALUE).textValue());
577 String property = pair[0];
578 String value = pair[1];
579 String ret = "(";
580 ret =
581 ret +
582 "EXISTS {MATCH (" +
583 variable +
584 ") - [:" +
585 Neo4jLabels.HAS_PAYLOAD +
586 "] -> (tt:" +
587 Neo4jLabels.TIMESERIES_TUPLE +
588 ") <- [:" +
589 Neo4jLabels.HAS_TIMESERIES_TUPLE +
590 "] - (ts:" +
591 Neo4jLabels.TIMESERIES +
592 ") - [:" +
593 Neo4jLabels.HAS_ANNOTATION +
594 "] -> (sem:" +
595 Neo4jLabels.SEMANTIC_ANNOTATION +
596 "), (" +
597 variable +
598 ") - [:is_in_container] -> (tsc:TimeseriesContainer) <- [:is_in_container] - (ts:Timeseries) WHERE sem." +
599 propertyType +
600 " ";
601 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
602 ret = ret + "\"" + property + "\"" + " AND sem." + valueType + " ";
603 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
604 ret = ret + "\"" + value + "\"";
605 ret = ret + "})";
606 return ret;
607 }
608
609 private static String timeseriesContainerIdPart(JsonNode node, String variable) {
610 return containerIdPart(node, variable, Neo4jLabels.TIMESERIES_CONTAINER);
611 }
612
613 private static String structuredDataContainerIdPart(JsonNode node, String variable) {
614 return containerIdPart(node, variable, Neo4jLabels.STRUCTURED_DATA_CONTAINER);
615 }
616
617 private static String fileContainerIdPart(JsonNode node, String variable) {
618 return containerIdPart(node, variable, Neo4jLabels.FILE_CONTAINER);
619 }
620
621 private static String containerIdPart(JsonNode node, String variable, String containerType) {
622 String ret = "(";
623 ret =
624 ret +
625 "EXISTS {MATCH (" +
626 variable +
627 ")-[:" +
628 Neo4jLabels.IS_IN_CONTAINER +
629 "]->(refCon:" +
630 containerType +
631 ") WHERE id(refCon) ";
632 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
633 ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
634 ret = ret + "})";
635 return ret;
636 }
637
638 private static String referencedDataObjectNeo4jIdPart(JsonNode node, String variable) {
639 String ret = "(";
640 ret =
641 ret +
642 "EXISTS {MATCH (" +
643 variable +
644 ")-[:" +
645 Neo4jLabels.POINTS_TO +
646 "]->(refDo:" +
647 Neo4jLabels.DATA_OBJECT +
648 ") WHERE id(refDo) ";
649 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
650 ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
651 ret = ret + "})";
652 return ret;
653 }
654
655 private static String referencedCollectionNeo4jIdPart(JsonNode node, String variable) {
656 String ret = "(";
657 ret =
658 ret +
659 "EXISTS {MATCH (" +
660 variable +
661 ")-[:" +
662 Neo4jLabels.POINTS_TO +
663 "]->(refCol:" +
664 Neo4jLabels.COLLECTION +
665 ") WHERE id(refCol) ";
666 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
667 ret = ret + node.get(SearchConstants.OP_VALUE) + " ";
668 ret = ret + "})";
669 return ret;
670 }
671
672 private static String neo4jIdPart(JsonNode node, String variable) {
673 String ret = "(";
674 ret = ret + "id(" + variable + ") ";
675 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + " ";
676 ret = ret + valuePart(node);
677 ret = ret + ")";
678 return ret;
679 }
680
681 private static String operatorString(JsonNode node) {
682 String operator = node.textValue();
683 return switch (operator) {
684 case SearchConstants.JSON_EQ -> "=";
685 case SearchConstants.JSON_CONTAINS -> "contains";
686 case SearchConstants.JSON_GT -> ">";
687 case SearchConstants.JSON_LT -> "<";
688 case SearchConstants.JSON_GE -> ">=";
689 case SearchConstants.JSON_LE -> "<=";
690 case SearchConstants.JSON_IN -> "IN";
691 case SearchConstants.JSON_NE -> "<>";
692 case SearchConstants.JSON_REGMATCH -> "=~";
693 default -> throw new ShepardParserException("unknown comparison operator " + operator);
694 };
695 }
696
697 private static String collectionDataObjectMatchPartWithoutVersion() {
698 String ret =
699 "MATCH (" +
700 SearchConstants.COLLECTION_IN_QUERY +
701 ":" +
702 Neo4jLabels.COLLECTION +
703 ")-[:has_dataobject]->(" +
704 SearchConstants.DATAOBJECT_IN_QUERY +
705 ":" +
706 Neo4jLabels.DATA_OBJECT +
707 ")";
708 return ret;
709 }
710
711 private static String collectionNeo4jIdWherePart(Long collectionId) {
712 String ret = "(id(" + SearchConstants.COLLECTION_IN_QUERY + ") = " + collectionId + ")";
713 return ret;
714 }
715
716 private static String notDeletedPart(String variable) {
717 String ret = "(" + variable + ".deleted = FALSE)";
718 return ret;
719 }
720
721 private static String collectionDataObjectNeo4jIdWherePart(Long collectionId, Long dataObjectId) {
722 String ret =
723 "(id(" +
724 SearchConstants.COLLECTION_IN_QUERY +
725 ") = " +
726 collectionId +
727 " AND id(" +
728 SearchConstants.DATAOBJECT_IN_QUERY +
729 ") = " +
730 dataObjectId +
731 ")";
732 return ret;
733 }
734
735 private static String collectionDataObjectTraversalNeo4jIdWherePart(Long collectionId, Long dataObjectId) {
736 String ret =
737 "(id(" + SearchConstants.COLLECTION_IN_QUERY + ") = " + collectionId + " AND id(d) = " + dataObjectId + ")";
738 return ret;
739 }
740
741 private static String basicReferenceMatchPartWithoutVersion() {
742 String ret =
743 "MATCH (" +
744 SearchConstants.COLLECTION_IN_QUERY +
745 ":" +
746 Neo4jLabels.COLLECTION +
747 ")-[:" +
748 Neo4jLabels.HAS_DATAOBJECT +
749 "]->(" +
750 SearchConstants.DATAOBJECT_IN_QUERY +
751 ":" +
752 Neo4jLabels.DATA_OBJECT +
753 ")-[:" +
754 Neo4jLabels.HAS_REFERENCE +
755 "]->(" +
756 SearchConstants.REFERENCE_IN_QUERY +
757 ":" +
758 Neo4jLabels.BASIC_REFERENCE +
759 ")";
760 return ret;
761 }
762
763 public static String collectionSelectionQueryWithNeo4jId(
764 String searchBodyQuery,
765 String userName,
766 SortingHelper sortOrder
767 ) {
768 String ret = "MATCH (" + SearchConstants.COLLECTION_IN_QUERY + ":" + Neo4jLabels.COLLECTION + ")";
769 ret = ret + " WHERE ";
770 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.COLLECTION_IN_QUERY);
771 ret = ret + " AND ";
772 ret = ret + notDeletedPart(SearchConstants.COLLECTION_IN_QUERY);
773 ret = ret + " AND ";
774 ret = ret + readableByPart(userName);
775 if (sortOrder.hasOrderByAttribute()) {
776 ret +=
777 " " +
778 CypherQueryHelper.getOrderByPart(
779 SearchConstants.COLLECTION_IN_QUERY,
780 sortOrder.getOrderByAttribute(),
781 sortOrder.getOrderDesc()
782 );
783 }
784 return ret;
785 }
786
787 public static String containerSelectionQueryWithNeo4jId(
788 String JSONQuery,
789 ContainerType containerType,
790 SortingHelper sortOrder,
791 String userName
792 ) {
793 String ret = "MATCH (" + containerType.getTypeAlias() + ":" + containerType.getTypeName() + ")";
794 ret = ret + " WHERE ";
795 ret = ret + getNeo4jWithNeo4jIdString(JSONQuery, containerType.getTypeAlias());
796 ret = ret + " AND ";
797 ret = ret + notDeletedPart(containerType.getTypeAlias());
798 ret = ret + " AND ";
799 ret = ret + CypherQueryHelper.getReadableByQuery(containerType.getTypeAlias(), userName);
800 if (sortOrder.hasOrderByAttribute()) {
801 ret +=
802 " " +
803 CypherQueryHelper.getOrderByPart(
804 containerType.getTypeAlias(),
805 sortOrder.getOrderByAttribute(),
806 sortOrder.getOrderDesc()
807 );
808 }
809 return ret;
810 }
811
812 public static String collectionDataObjectSelectionQueryWithNeo4jId(
813 Long collectionId,
814 String searchBodyQuery,
815 String username
816 ) {
817 String ret = "";
818 ret = ret + collectionDataObjectMatchPartWithoutVersion();
819 ret = ret + " WHERE ";
820 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
821 ret = ret + " AND ";
822 ret = ret + collectionNeo4jIdWherePart(collectionId);
823 ret = ret + " AND ";
824 ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
825 ret = ret + " AND ";
826 ret = ret + readableByPart(username);
827 return ret;
828 }
829
830 public static String collectionDataObjectDataObjectSelectionQueryWithNeo4jId(
831 SearchScope scope,
832 TraversalRules traversalRule,
833 String searchBodyQuery,
834 String username
835 ) {
836 String ret = "";
837 ret = ret + collectionDataObjectDataObjectMatchPartWithoutVersion(traversalRule);
838 ret = ret + " WHERE ";
839 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
840 ret = ret + " AND ";
841 ret = ret + collectionDataObjectTraversalNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
842 ret = ret + " AND ";
843 ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
844 ret = ret + " AND ";
845 ret = ret + readableByPart(username);
846 return ret;
847 }
848
849 public static String collectionDataObjectDataObjectSelectionQueryWithNeo4jId(
850 SearchScope scope,
851 String searchBodyQuery,
852 String username
853 ) {
854 String ret = "";
855 ret = ret + collectionDataObjectMatchPartWithoutVersion();
856 ret = ret + " WHERE ";
857 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
858 ret = ret + " AND ";
859 ret = ret + collectionDataObjectNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
860 ret = ret + " AND ";
861 ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
862 ret = ret + " AND ";
863 ret = ret + readableByPart(username);
864 return ret;
865 }
866
867 public static String dataObjectSelectionQueryWithNeo4jId(String searchBodyQuery, String username) {
868 String ret = "";
869 ret = ret + collectionDataObjectMatchPartWithoutVersion();
870 ret = ret + " WHERE ";
871 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.DATAOBJECT_IN_QUERY);
872 ret = ret + " AND ";
873 ret = ret + notDeletedPart(SearchConstants.DATAOBJECT_IN_QUERY);
874 ret = ret + " AND ";
875 ret = ret + readableByPart(username);
876 return ret;
877 }
878
879 public static String basicReferenceSelectionQueryWithNeo4jId(String searchBodyQuery, String username) {
880 String ret = "";
881 ret = ret + basicReferenceMatchPartWithoutVersion();
882 ret = ret + " WHERE ";
883 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
884 ret = ret + " AND ";
885 ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
886 ret = ret + " AND ";
887 ret = ret + readableByPart(username);
888 return ret;
889 }
890
891 public static String collectionBasicReferenceSelectionQueryWithNeo4jId(
892 String searchBodyQuery,
893 Long collectionId,
894 String username
895 ) {
896 String ret = "";
897 ret = ret + basicReferenceMatchPartWithoutVersion();
898 ret = ret + " WHERE ";
899 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
900 ret = ret + " AND ";
901 ret = ret + collectionNeo4jIdWherePart(collectionId);
902 ret = ret + " AND ";
903 ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
904 ret = ret + " AND ";
905 ret = ret + readableByPart(username);
906 return ret;
907 }
908
909 public static String collectionDataObjectReferenceSelectionQueryWithNeo4jId(
910 SearchScope scope,
911 String searchBodyQuery,
912 String username
913 ) {
914 String ret = "";
915 ret = ret + basicReferenceMatchPartWithoutVersion();
916 ret = ret + " WHERE ";
917 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
918 ret = ret + " AND ";
919 ret = ret + collectionDataObjectNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
920 ret = ret + " AND ";
921 ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
922 ret = ret + " AND ";
923 ret = ret + readableByPart(username);
924 return ret;
925 }
926
927 public static String collectionDataObjectBasicReferenceSelectionQueryWithNeo4jId(
928 SearchScope scope,
929 TraversalRules traversalRule,
930 String searchBodyQuery,
931 String username
932 ) {
933 String ret = "";
934 ret = ret + collectionDataObjectBasicReferenceMatchPartWithoutVersion(traversalRule);
935 ret = ret + " WHERE ";
936 ret = ret + getNeo4jWithNeo4jIdString(searchBodyQuery, SearchConstants.REFERENCE_IN_QUERY);
937 ret = ret + " AND ";
938 ret = ret + collectionDataObjectTraversalNeo4jIdWherePart(scope.getCollectionId(), scope.getDataObjectId());
939 ret = ret + " AND ";
940 ret = ret + notDeletedPart(SearchConstants.REFERENCE_IN_QUERY);
941 ret = ret + " AND ";
942 ret = ret + readableByPart(username);
943 return ret;
944 }
945
946 private static String collectionDataObjectDataObjectMatchPartWithoutVersion(TraversalRules traversalRule) {
947 String ret =
948 switch (traversalRule) {
949 case children -> "MATCH (" +
950 SearchConstants.COLLECTION_IN_QUERY +
951 ":" +
952 Neo4jLabels.COLLECTION +
953 ")-[:" +
954 Neo4jLabels.HAS_DATAOBJECT +
955 "]->(d:" +
956 Neo4jLabels.DATA_OBJECT +
957 ")-[:" +
958 Neo4jLabels.HAS_CHILD +
959 "*0..]->(" +
960 SearchConstants.DATAOBJECT_IN_QUERY +
961 ":" +
962 Neo4jLabels.DATA_OBJECT +
963 ")";
964 case parents -> "MATCH (" +
965 SearchConstants.COLLECTION_IN_QUERY +
966 ":" +
967 Neo4jLabels.COLLECTION +
968 ")-[:" +
969 Neo4jLabels.HAS_DATAOBJECT +
970 "]->(d:" +
971 Neo4jLabels.DATA_OBJECT +
972 ")<-[:" +
973 Neo4jLabels.HAS_CHILD +
974 "*0..]-(" +
975 SearchConstants.DATAOBJECT_IN_QUERY +
976 ":" +
977 Neo4jLabels.DATA_OBJECT +
978 ")";
979 case successors -> "MATCH (" +
980 SearchConstants.COLLECTION_IN_QUERY +
981 ":" +
982 Neo4jLabels.COLLECTION +
983 ")-[:" +
984 Neo4jLabels.HAS_DATAOBJECT +
985 "]->(d:" +
986 Neo4jLabels.DATA_OBJECT +
987 ")-[:" +
988 Neo4jLabels.HAS_SUCCESSOR +
989 "*0..]->(" +
990 SearchConstants.DATAOBJECT_IN_QUERY +
991 ":" +
992 Neo4jLabels.DATA_OBJECT +
993 ")";
994 case predecessors -> "MATCH (" +
995 SearchConstants.COLLECTION_IN_QUERY +
996 ":" +
997 Neo4jLabels.COLLECTION +
998 ")-[:" +
999 Neo4jLabels.HAS_DATAOBJECT +
1000 "]->(d:DataObject)<-[:has_successor*0..]-(" +
1001 SearchConstants.DATAOBJECT_IN_QUERY +
1002 ":" +
1003 Neo4jLabels.DATA_OBJECT +
1004 ")";
1005 default -> "";
1006 };
1007 return ret;
1008 }
1009
1010 private static String collectionDataObjectBasicReferenceMatchPartWithoutVersion(TraversalRules traversalRule) {
1011 String ret =
1012 switch (traversalRule) {
1013 case children -> "MATCH (" +
1014 SearchConstants.COLLECTION_IN_QUERY +
1015 ":" +
1016 Neo4jLabels.COLLECTION +
1017 ")-[:" +
1018 Neo4jLabels.HAS_DATAOBJECT +
1019 "]->(d:" +
1020 Neo4jLabels.DATA_OBJECT +
1021 ")-[:" +
1022 Neo4jLabels.HAS_CHILD +
1023 "*0..]->(" +
1024 SearchConstants.DATAOBJECT_IN_QUERY +
1025 ":" +
1026 Neo4jLabels.DATA_OBJECT +
1027 ")-[:" +
1028 Neo4jLabels.HAS_REFERENCE +
1029 "]->(" +
1030 SearchConstants.REFERENCE_IN_QUERY +
1031 ":" +
1032 Neo4jLabels.BASIC_REFERENCE +
1033 ")";
1034 case parents -> "MATCH (" +
1035 SearchConstants.COLLECTION_IN_QUERY +
1036 ":" +
1037 Neo4jLabels.COLLECTION +
1038 ")-[:" +
1039 Neo4jLabels.HAS_DATAOBJECT +
1040 "]->(d:" +
1041 Neo4jLabels.DATA_OBJECT +
1042 ")<-[:" +
1043 Neo4jLabels.HAS_CHILD +
1044 "*0..]-(" +
1045 SearchConstants.DATAOBJECT_IN_QUERY +
1046 ":" +
1047 Neo4jLabels.DATA_OBJECT +
1048 ")-[:" +
1049 Neo4jLabels.HAS_REFERENCE +
1050 "]->(" +
1051 SearchConstants.REFERENCE_IN_QUERY +
1052 ":" +
1053 Neo4jLabels.BASIC_REFERENCE +
1054 ")";
1055 case successors -> "MATCH (" +
1056 SearchConstants.COLLECTION_IN_QUERY +
1057 ":" +
1058 Neo4jLabels.COLLECTION +
1059 ")-[:" +
1060 Neo4jLabels.HAS_DATAOBJECT +
1061 "]->(d:" +
1062 Neo4jLabels.DATA_OBJECT +
1063 ")-[:" +
1064 Neo4jLabels.HAS_SUCCESSOR +
1065 "*0..]->(" +
1066 SearchConstants.DATAOBJECT_IN_QUERY +
1067 ":" +
1068 Neo4jLabels.DATA_OBJECT +
1069 ")-[:" +
1070 Neo4jLabels.HAS_REFERENCE +
1071 "]->(" +
1072 SearchConstants.REFERENCE_IN_QUERY +
1073 ":" +
1074 Neo4jLabels.BASIC_REFERENCE +
1075 ")";
1076 case predecessors -> "MATCH (" +
1077 SearchConstants.COLLECTION_IN_QUERY +
1078 ":" +
1079 Neo4jLabels.COLLECTION +
1080 ")-[:" +
1081 Neo4jLabels.HAS_DATAOBJECT +
1082 "]->(d:" +
1083 Neo4jLabels.DATA_OBJECT +
1084 ")<-[:" +
1085 Neo4jLabels.HAS_SUCCESSOR +
1086 "*0..]-(" +
1087 SearchConstants.DATAOBJECT_IN_QUERY +
1088 ":" +
1089 Neo4jLabels.DATA_OBJECT +
1090 ")-[:" +
1091 Neo4jLabels.HAS_REFERENCE +
1092 "]->(" +
1093 SearchConstants.REFERENCE_IN_QUERY +
1094 ":" +
1095 Neo4jLabels.BASIC_REFERENCE +
1096 ")";
1097 default -> "";
1098 };
1099 return ret;
1100 }
1101
1102 private static String readableByPart(String username) {
1103 String variable = SearchConstants.COLLECTION_IN_QUERY;
1104 return CypherQueryHelper.getReadableByQuery(variable, username);
1105 }
1106
1107 public static String userSelectionQuery(String query) {
1108 String ret = "";
1109 ret = ret + userMatchPart();
1110 ret = ret + " WHERE ";
1111 ret = ret + getNeo4jWithNeo4jIdString(query, Constants.USER_IN_QUERY);
1112 return ret;
1113 }
1114
1115 private static String userMatchPart() {
1116 String ret = "";
1117 ret = ret + "MATCH (" + Constants.USER_IN_QUERY + ":" + Neo4jLabels.USER + ")";
1118 return ret;
1119 }
1120
1121 public static String userGroupSelectionQuery(String query) {
1122 String ret = "";
1123 ret = ret + userGroupMatchPart();
1124 ret = ret + " WHERE ";
1125 ret = ret + getNeo4jWithNeo4jIdString(query, Constants.USERGROUP_IN_QUERY);
1126 return ret;
1127 }
1128
1129 private static String userGroupMatchPart() {
1130 String ret = "";
1131 ret = ret + "MATCH (" + Constants.USERGROUP_IN_QUERY + ":" + Neo4jLabels.USER_GROUP + ")";
1132 return ret;
1133 }
1134 }