1 package de.dlr.shepard.context.collection.daos;
2
3 import de.dlr.shepard.auth.users.entities.User;
4 import de.dlr.shepard.common.util.Constants;
5 import de.dlr.shepard.common.util.CypherQueryHelper;
6 import de.dlr.shepard.common.util.Neo4jLabels;
7 import de.dlr.shepard.common.util.QueryParamHelper;
8 import de.dlr.shepard.context.collection.entities.DataObject;
9 import de.dlr.shepard.context.version.daos.VersionableEntityDAO;
10 import jakarta.enterprise.context.RequestScoped;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.Date;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.UUID;
18 import java.util.stream.StreamSupport;
19 import org.neo4j.cypherdsl.core.Cypher;
20 import org.neo4j.cypherdsl.core.Node;
21
22 @RequestScoped
23 public class DataObjectDAO extends VersionableEntityDAO<DataObject> {
24
25 @Override
26 public Class<DataObject> getEntityType() {
27 return DataObject.class;
28 }
29
30
31
32
33
34
35
36
37 public List<DataObject> findByCollectionByNeo4jIds(long collectionId, QueryParamHelper params) {
38 Map<String, Object> paramsMap = new HashMap<>();
39 paramsMap.put("name", params.getName());
40 if (params.hasPagination()) {
41 paramsMap.put("offset", params.getPagination().getOffset());
42 paramsMap.put("size", params.getPagination().getSize());
43 }
44 String match =
45 "MATCH (c:Collection)-[hdo:has_dataobject]->" +
46 CypherQueryHelper.getObjectPart("d", "DataObject", params.hasName());
47 String where = " WHERE ID(c)=" + collectionId;
48
49 if (params.hasParentId()) {
50 if (params.getParentId() == -1) {
51 where += " AND NOT EXISTS((d)<-[:has_child]-(:DataObject {deleted: FALSE}))";
52 } else {
53 match += "<-[:has_child]-(parent:DataObject {deleted: FALSE})";
54 where += " AND ID(parent)=" + params.getParentId();
55 }
56 }
57
58 if (params.hasPredecessorId()) {
59 if (params.getPredecessorId() == -1) {
60 where += " AND NOT EXISTS((d)<-[:has_successor]-(:DataObject {deleted: FALSE}))";
61 } else {
62 match += "<-[:has_successor]-(predecessor:DataObject {deleted: FALSE})";
63 where += " AND ID(predecessor)=" + params.getPredecessorId();
64 }
65 }
66 if (params.hasSuccessorId()) {
67 if (params.getSuccessorId() == -1) {
68 where += " AND NOT EXISTS((d)-[:has_successor]->(:DataObject {deleted: FALSE}))";
69 } else {
70 match += "-[:has_successor]->(successor:DataObject {deleted: FALSE})";
71 where += " AND ID(successor)=" + params.getSuccessorId();
72 }
73 }
74
75 String query = match + where + " WITH d";
76 if (params.hasOrderByAttribute()) {
77 query += " " + CypherQueryHelper.getOrderByPart("d", params.getOrderByAttribute(), params.getOrderDesc());
78 }
79 if (params.hasPagination()) {
80 query += " " + CypherQueryHelper.getPaginationPart();
81 }
82 query += " " + CypherQueryHelper.getReturnPart("d");
83 var result = new ArrayList<DataObject>();
84 for (var obj : findByQuery(query, paramsMap)) {
85 List<DataObject> parentList = obj.getParent() != null ? List.of(obj.getParent()) : Collections.emptyList();
86 if (
87 matchCollection(obj, collectionId) &&
88 matchName(obj, params.getName()) &&
89 matchRelated(parentList, params.getParentId()) &&
90 matchRelated(obj.getSuccessors(), params.getSuccessorId()) &&
91 matchRelated(obj.getPredecessors(), params.getPredecessorId())
92 ) {
93 result.add(obj);
94 }
95 }
96
97 return result;
98 }
99
100 public List<DataObject> findByCollectionByShepardIds(
101 long collectionShepardId,
102 QueryParamHelper paramsWithShepardIds
103 ) {
104 return findByCollectionByShepardIds(collectionShepardId, paramsWithShepardIds, null);
105 }
106
107
108
109
110 public void deleteHasSuccessorRelation(long predecessorShepardId, long successorShepardId) {
111 deleteRelation(
112 predecessorShepardId,
113 successorShepardId,
114 getEntityType().getSimpleName(),
115 getEntityType().getSimpleName(),
116 Neo4jLabels.HAS_SUCCESSOR
117 );
118 }
119
120
121
122
123 public void deleteHasChildRelation(long parentShepardId, long childShepardId) {
124 deleteRelation(
125 parentShepardId,
126 childShepardId,
127 getEntityType().getSimpleName(),
128 getEntityType().getSimpleName(),
129 Neo4jLabels.HAS_CHILD
130 );
131 }
132
133
134
135
136
137 public void deleteAllAttributes(DataObject dataObject) {
138 if (dataObject.getAttributes() == null || dataObject.getAttributes().isEmpty()) return;
139 Node d = Cypher.node("DataObject");
140 String st = Cypher.match(d)
141 .where(d.internalId().isEqualTo(Cypher.literalOf(dataObject.getId())))
142 .remove(dataObject.getAttributes().keySet().stream().map(key -> d.property("attributes||" + key)).toList())
143 .build()
144 .getCypher();
145 session.query(st, new HashMap<String, String>());
146 }
147
148
149
150
151
152
153
154
155 public List<DataObject> findByCollectionByShepardIds(
156 long collectionShepardId,
157 QueryParamHelper paramsWithShepardIds,
158 UUID versionUID
159 ) {
160 Map<String, Object> paramsMap = new HashMap<>();
161 paramsMap.put("name", paramsWithShepardIds.getName());
162 if (paramsWithShepardIds.hasPagination()) {
163 paramsMap.put("offset", paramsWithShepardIds.getPagination().getOffset());
164 paramsMap.put("size", paramsWithShepardIds.getPagination().getSize());
165 }
166 String match =
167 "MATCH (v:Version)<-[:has_version]-(c:Collection)-[hdo:has_dataobject]->" +
168 CypherQueryHelper.getObjectPart("d", "DataObject", paramsWithShepardIds.hasName());
169 String where = " WHERE c." + Constants.SHEPARD_ID + "=" + collectionShepardId + " AND ";
170
171 if (versionUID == null) where = where + CypherQueryHelper.getVersionHeadPart("v");
172
173 else where = where + CypherQueryHelper.getVersionPart("v", versionUID);
174 if (paramsWithShepardIds.hasParentId()) {
175 if (paramsWithShepardIds.getParentId() == -1) {
176 where += " AND NOT EXISTS((d)<-[:has_child]-(:DataObject {deleted: FALSE}))";
177 } else {
178 match +=
179 "<-[:has_child]-(parent:DataObject {deleted: FALSE, " +
180 Constants.SHEPARD_ID +
181 ": " +
182 paramsWithShepardIds.getParentId() +
183 "})";
184 }
185 }
186
187 if (paramsWithShepardIds.hasPredecessorId()) {
188 if (paramsWithShepardIds.getPredecessorId() == -1) {
189 where += " AND NOT EXISTS((d)<-[:has_successor]-(:DataObject {deleted: FALSE}))";
190 } else {
191 match +=
192 "<-[:has_successor]-(predecessor:DataObject {deleted: FALSE, " +
193 Constants.SHEPARD_ID +
194 ": " +
195 paramsWithShepardIds.getPredecessorId() +
196 "})";
197 }
198 }
199 if (paramsWithShepardIds.hasSuccessorId()) {
200 if (paramsWithShepardIds.getSuccessorId() == -1) {
201 where += " AND NOT EXISTS((d)-[:has_successor]->(:DataObject {deleted: FALSE}))";
202 } else {
203 match +=
204 "-[:has_successor]->(successor:DataObject {deleted: FALSE, " +
205 Constants.SHEPARD_ID +
206 ": " +
207 paramsWithShepardIds.getSuccessorId() +
208 "})";
209 }
210 }
211
212 String query = match + where + " WITH d";
213 if (paramsWithShepardIds.hasOrderByAttribute()) {
214 query +=
215 " " +
216 CypherQueryHelper.getOrderByPart(
217 "d",
218 paramsWithShepardIds.getOrderByAttribute(),
219 paramsWithShepardIds.getOrderDesc()
220 );
221 }
222 if (paramsWithShepardIds.hasPagination()) {
223 query += " " + CypherQueryHelper.getPaginationPart();
224 }
225 query += " " + CypherQueryHelper.getReturnPart("d");
226 var result = new ArrayList<DataObject>();
227 for (var obj : findByQuery(query, paramsMap)) {
228 List<DataObject> parentList = obj.getParent() != null ? List.of(obj.getParent()) : Collections.emptyList();
229 if (
230 matchCollectionByShepardId(obj, collectionShepardId) &&
231 matchName(obj, paramsWithShepardIds.getName()) &&
232 matchRelatedByShepardId(parentList, paramsWithShepardIds.getParentId()) &&
233 matchRelatedByShepardId(obj.getSuccessors(), paramsWithShepardIds.getSuccessorId()) &&
234 matchRelatedByShepardId(obj.getPredecessors(), paramsWithShepardIds.getPredecessorId())
235 ) {
236 result.add(obj);
237 }
238 }
239
240 return result;
241 }
242
243
244
245
246
247
248
249
250
251 public boolean deleteDataObjectByNeo4jId(long id, User updatedBy, Date updatedAt) {
252 var dataObject = findByNeo4jId(id);
253 dataObject.setUpdatedBy(updatedBy);
254 dataObject.setUpdatedAt(updatedAt);
255 dataObject.setDeleted(true);
256 createOrUpdate(dataObject);
257 String query = String.format(
258 "MATCH (d:DataObject) WHERE ID(d) = %d OPTIONAL MATCH (d)-[:has_reference]->(r:BasicReference) " +
259 "FOREACH (n in [d,r] | SET n.deleted = true)",
260 id
261 );
262 var result = runQuery(query, Collections.emptyMap());
263 return result;
264 }
265
266
267
268
269
270
271
272
273
274 public boolean deleteDataObjectByShepardId(long shepardId, User updatedBy, Date updatedAt) {
275 DataObject dataObject = findByShepardId(shepardId);
276 dataObject.setUpdatedBy(updatedBy);
277 dataObject.setUpdatedAt(updatedAt);
278 dataObject.setDeleted(true);
279 createOrUpdate(dataObject);
280 String query = String.format(
281 "MATCH (d:DataObject) WHERE ID(d) = %d OPTIONAL MATCH (d)-[:has_reference]->(r:BasicReference) " +
282 "FOREACH (n in [d,r] | SET n.deleted = true)",
283 dataObject.getId()
284 );
285 var result = runQuery(query, Collections.emptyMap());
286 return result;
287 }
288
289 private boolean matchName(DataObject obj, String name) {
290 return name == null || name.equalsIgnoreCase(obj.getName());
291 }
292
293 private boolean matchRelated(List<DataObject> related, Long id) {
294 if (id == null) {
295 return true;
296 } else if (id == -1) {
297
298 return related.stream().allMatch(DataObject::isDeleted);
299 } else {
300
301 return related.stream().anyMatch(d -> !d.isDeleted() && d.getId().equals(id));
302 }
303 }
304
305 private boolean matchRelatedByShepardId(List<DataObject> related, Long shepardId) {
306 if (shepardId == null) {
307 return true;
308 } else if (shepardId == -1) {
309
310 return related.stream().allMatch(DataObject::isDeleted);
311 } else {
312
313 return related.stream().anyMatch(d -> !d.isDeleted() && d.getShepardId().equals(shepardId));
314 }
315 }
316
317 private boolean matchCollection(DataObject obj, long collectionId) {
318 return obj.getCollection() != null && obj.getCollection().getId().equals(collectionId);
319 }
320
321 private boolean matchCollectionByShepardId(DataObject obj, long collectionShepardId) {
322 return obj.getCollection() != null && obj.getCollection().getShepardId().equals(collectionShepardId);
323 }
324
325 public List<DataObject> getDataObjectsByQuery(String query) {
326 var queryResult = findByQuery(query, Collections.emptyMap());
327 List<DataObject> ret = StreamSupport.stream(queryResult.spliterator(), false).toList();
328 return ret;
329 }
330 }