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