View Javadoc
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    * This is a fix described in:
137    * https://gitlab.com/dlr-shepard/shepard/-/issues/389
138    * We use new delimiter characters for attributes ('||'), but want to support
139    * the old search functionality using '.' as a delimiter.
140    *
141    * @param property
142    * @return property string, if it contained 'attributes.', it is going to be
143    * replaced by 'attributes||'
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     // search for creating user
171     if (
172       property.equals(SearchConstants.JSON_CREATED_BY) || property.equals(SearchConstants.JSON_UPDATED_BY)
173     ) return byPart(node, variable);
174     // search for createdAt/updatedAt
175     if (
176       property.equals(SearchConstants.JSON_CREATED_AT) || property.equals(SearchConstants.JSON_UPDATED_AT)
177     ) return atPart(node, variable);
178     // search for SemanticAnnotationIRIs
179     if (
180       property.equals(SearchConstants.JSON_VALUE_IRI) || property.equals(SearchConstants.JSON_PROPERTY_IRI)
181     ) return iriPart(node, variable);
182     // search for SemanticAnnotations
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     // search for start/end of TimeseriesReferences
186     if (
187       property.equals(SearchConstants.JSON_TIMESERIES_REFERENCE_START) ||
188       property.equals(SearchConstants.JSON_TIMESERIES_REFERENCE_END)
189     ) return timeFramePart(node, variable);
190     // search for SemanticAnnotations of referenced Timeseries
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    * method used for searching according to a timestamp
204    * used for createdAt and updatedAt
205    *
206    * @param node the JsonNode containing a search triple with updatedAt or createdAt as property
207    * @param variable the variable in the resulting neo4j query representing the noe4jNode under consideration
208    * @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
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     // for simple id
301     if (property.equals(SearchConstants.JSON_ID)) return neo4jIdPart(node, variable);
302     // for CollectionReferences
303     if (property.equals(SearchConstants.JSON_REFERENCED_COLLECTION_ID)) return referencedCollectionNeo4jIdPart(
304       node,
305       variable
306     );
307     // for DataObjectReferences
308     if (property.equals(SearchConstants.JSON_REFERENCED_DATA_OBJECT_ID)) return referencedDataObjectNeo4jIdPart(
309       node,
310       variable
311     );
312     // for FileReferences
313     if (property.equals(SearchConstants.JSON_FILE_CONTAINER_ID)) return fileContainerIdPart(node, variable);
314     // for StructuredDataReferences
315     if (property.equals(SearchConstants.JSON_STRUCTURED_DATA_CONTAINER_ID)) return structuredDataContainerIdPart(
316       node,
317       variable
318     );
319     // for TimeseriesReferences
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     // for neighborhoodIds
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 }