View Javadoc
1   package de.dlr.shepard.common.util;
2   
3   import de.dlr.shepard.common.configuration.feature.toggles.VersioningFeatureToggle;
4   import de.dlr.shepard.common.neo4j.endpoints.OrderByAttribute;
5   import de.dlr.shepard.common.search.endpoints.BasicContainerAttributes;
6   import java.util.List;
7   import java.util.UUID;
8   import java.util.stream.Collectors;
9   
10  public class CypherQueryHelper {
11  
12    public enum Neighborhood {
13      EVERYTHING,
14      OUTGOING,
15      ESSENTIAL,
16    }
17  
18    private CypherQueryHelper() {}
19  
20    public static String getObjectPartWithVersion(String variable, String type, boolean hasName, String versionVariable) {
21      String ret = getObjectPart(variable, type, hasName);
22      ret = ret + "-[:has_version]->(" + versionVariable + ":Version)";
23      return ret;
24    }
25  
26    public static String getObjectPart(String variable, String type, boolean hasName) {
27      if (hasName) return getObjectPartWithName(variable, type);
28      else return getObjectPartWithoutName(variable, type);
29    }
30  
31    private static String getObjectPartWithName(String variable, String type) {
32      var namePart = "{ name : $name, deleted: FALSE }";
33      var result = String.format("(%s:%s %s)", variable, type, namePart);
34      return result;
35    }
36  
37    private static String getObjectPartWithoutName(String variable, String type) {
38      var namePart = "{ deleted: FALSE }";
39      var result = String.format("(%s:%s %s)", variable, type, namePart);
40      return result;
41    }
42  
43    public static String getPaginationPart() {
44      return "SKIP $offset LIMIT $size";
45    }
46  
47    public static String getPaginationPart(PaginationHelper paginationParams) {
48      return String.format("SKIP %d LIMIT %d", paginationParams.getOffset(), paginationParams.getSize());
49    }
50  
51    public static String getReturnPart(String entity) {
52      return getReturnPart(entity, Neighborhood.EVERYTHING, 1);
53    }
54  
55    public static String getReturnPart(String entity, int depth) {
56      return getReturnPart(entity, Neighborhood.EVERYTHING, depth);
57    }
58  
59    public static String getReturnPart(String entity, Neighborhood neighborhood) {
60      return getReturnPart(entity, neighborhood, 1);
61    }
62  
63    public static String getReturnPart(String entity, Neighborhood neighborhood, PaginationHelper pagination) {
64      return getReturnPart(entity, neighborhood, 1, pagination);
65    }
66  
67    public static String getReturnCountPart(String entity, Neighborhood neighborhood) {
68      return (getNeighborhoodPart(entity, neighborhood, 1) + " RETURN " + String.format("COUNT(%s)", entity));
69    }
70  
71    public static String getReturnPart(String entity, Neighborhood neighborhood, int depth) {
72      return (
73        getNeighborhoodPart(entity, neighborhood, depth) +
74        " RETURN " +
75        String.format("%s, nodes(path), relationships(path)", entity)
76      );
77    }
78  
79    public static String getReturnPart(String entity, Neighborhood neighborhood, int depth, PaginationHelper pagination) {
80      return (
81        getNeighborhoodPart(entity, neighborhood, depth) +
82        (pagination != null ? " " + CypherQueryHelper.getPaginationPart(pagination) : "") +
83        " RETURN " +
84        String.format("%s, nodes(path), relationships(path)", entity)
85      );
86    }
87  
88    private static String getNeighborhoodPart(String entity, Neighborhood neighborhood, int depth) {
89      // Clamp the depth between 1 and 3 nodes
90      depth = Math.max(1, Math.min(3, depth));
91      String match =
92        switch (neighborhood) {
93          case EVERYTHING -> "path=(%s)-[*0..%d]-(n) WHERE n.deleted = FALSE OR n.deleted IS NULL";
94          case OUTGOING -> "path=(%s)-[*0..%d]->(n) WHERE n.deleted = FALSE OR n.deleted IS NULL";
95          case ESSENTIAL -> "path=(%s)-[*0..%d]->(n) WHERE n:Permission OR n:User";
96        };
97      return "MATCH " + String.format(match, entity, depth);
98    }
99  
100   public static String getReturnPartLight(String entity) {
101     return "RETURN " + entity;
102   }
103 
104   public static String getOrderByPart(String variable, OrderByAttribute orderByAttribute, Boolean orderDesc) {
105     String ret;
106     boolean isString = orderByAttribute.isString();
107     if (!isString) ret = "ORDER BY " + variable + "." + orderByAttribute;
108     else if (
109       orderByAttribute instanceof BasicContainerAttributes &&
110       ((BasicContainerAttributes) orderByAttribute) == BasicContainerAttributes.type
111     ) ret = "ORDER BY LABELS(" + variable + ")";
112     else ret = "ORDER BY toLower(" + variable + "." + orderByAttribute + ")";
113     if (orderByAttribute.toString() == "id") ret = "ORDER BY id(" + variable + ")";
114     if (orderDesc != null && orderDesc) ret = ret + " DESC";
115     return ret;
116   }
117 
118   public static String getShepardIdPart(String variable, long shepardId) {
119     return variable + "." + Constants.SHEPARD_ID + " = " + shepardId;
120   }
121 
122   public static String getShepardIdsPart(String variable, List<Long> shepardIds) {
123     String commaSeparatedIds = shepardIds.stream().map(String::valueOf).collect(Collectors.joining(","));
124     return variable + "." + Constants.SHEPARD_ID + " in [" + commaSeparatedIds + "]";
125   }
126 
127   public static String getReadableByQuery(String variable, String username) {
128     String ret = String.format(
129       """
130       (NOT exists((%s)-[:has_permissions]->(:Permissions)) \
131       OR exists((%s)-[:has_permissions]->(:Permissions)-[:readable_by|owned_by]->(:User { username: "%s" })) \
132       OR exists((%s)-[:has_permissions]->(:Permissions {permissionType: "Public"})) \
133       OR exists((%s)-[:has_permissions]->(:Permissions {permissionType: "PublicReadable"})) \
134       OR exists((%s)-[:has_permissions]->(:Permissions)-[:readable_by_group]->(:UserGroup)<-[:is_in_group]-(:User { username: "%s"})))""",
135       variable,
136       variable,
137       username,
138       variable,
139       variable,
140       variable,
141       username
142     );
143     return ret;
144   }
145 
146   public static String getVersionHeadPart(String variable) {
147     if (VersioningFeatureToggle.isEnabled()) {
148       return "(" + variable + ".isHEADVersion = true)";
149     }
150     return "(1=1)";
151   }
152 
153   public static String getVersionPart(String variable, UUID versionUID) {
154     return "(" + variable + ".uid = '" + versionUID + "')";
155   }
156 }