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
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 }