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