View Javadoc
1   package de.dlr.shepard.common.neo4j.daos;
2   
3   import de.dlr.shepard.common.neo4j.NeoConnector;
4   import de.dlr.shepard.common.util.Constants;
5   import de.dlr.shepard.common.util.CypherQueryHelper;
6   import de.dlr.shepard.common.util.CypherQueryHelper.Neighborhood;
7   import de.dlr.shepard.common.util.TraversalRules;
8   import io.quarkus.logging.Log;
9   import java.util.Collection;
10  import java.util.HashMap;
11  import java.util.Map;
12  import org.neo4j.ogm.cypher.Filter;
13  import org.neo4j.ogm.model.Result;
14  import org.neo4j.ogm.session.Session;
15  
16  public abstract class GenericDAO<T> {
17  
18    protected static final int DEPTH_ENTITY = 1;
19  
20    protected Session session = null;
21  
22    protected GenericDAO() {
23      session = NeoConnector.getInstance().getNeo4jSession();
24    }
25  
26    /**
27     * Find all instances of a certain entity T
28     *
29     * @return an Iterable over the found entities
30     */
31    public Collection<T> findAll() {
32      Collection<T> iter = session.loadAll(getEntityType(), DEPTH_ENTITY);
33      return iter;
34    }
35  
36    /**
37     * Find the entity with the given id
38     *
39     * @param id The given id
40     * @return The entity with the given id or null
41     */
42    public T findByNeo4jId(long id) {
43      return session.load(getEntityType(), id, DEPTH_ENTITY);
44    }
45  
46    /**
47     * Find the entity with the given id without any related entities
48     *
49     * @param id The given id
50     * @return The entity with the given id or null
51     */
52    public T findLightByNeo4jId(long id) {
53      return session.load(getEntityType(), id, 0);
54    }
55  
56    /**
57     * Find entities matching the given filter
58     *
59     * @param filter The given filter
60     * @return An iterable with the found entities
61     */
62    public Collection<T> findMatching(Filter filter) {
63      return session.loadAll(getEntityType(), filter, DEPTH_ENTITY);
64    }
65  
66    /**
67     * Delete an entity
68     *
69     * Caution: Regarding the official documentation ids will be reused by neo4j.
70     * @see <a href='https://neo4j.com/docs/ogm-manual/current/reference/#reference:annotating-entities:entity-identifier'>See official documentation for further information on the id issue</a>
71     *
72     * @param id The entity to be deleted
73     * @return Whether the deletion was successful or not
74     */
75    public boolean deleteByNeo4jId(long id) {
76      T entity = session.load(getEntityType(), id);
77      if (entity != null) {
78        session.delete(entity);
79        return true;
80      }
81      return false;
82    }
83  
84    /**
85     * Save an entity and all related entities
86     *
87     * @param entity The entity to be saved
88     * @return the saved entity
89     */
90    public T createOrUpdate(T entity) {
91      session.save(entity, DEPTH_ENTITY);
92      return entity;
93    }
94  
95    public void clearSession() {
96      session.clear();
97    }
98  
99    /**
100    * CAUTION: The query runs against the database and is not checked. You can do
101    * anything you want.
102    *
103    * @param query     The query
104    * @param paramsMap Map of parameters
105    * @return Iterable The result
106    */
107   public Iterable<T> findByQuery(String query, Map<String, Object> paramsMap) {
108     Log.debugf("Run query: %s", query);
109     StringBuilder str = new StringBuilder();
110     for (var entry : paramsMap.entrySet()) {
111       str.append("(" + entry.getKey() + ", " + entry.getValue() + "), ");
112     }
113     Log.debugf("queryParams: %s", str.toString());
114     Iterable<T> iter = session.query(getEntityType(), query, paramsMap);
115     return iter;
116   }
117 
118   public boolean runQuery(String query, Map<String, Object> paramsMap) {
119     Log.debugf("Run query: %s", query);
120     Result result = session.query(query, paramsMap);
121     return result.queryStatistics().containsUpdates();
122   }
123 
124   public String getSearchForReachableReferencesByShepardIdQuery(
125     TraversalRules traversalRule,
126     long collectionShepardId,
127     long startShepardId,
128     String userName
129   ) {
130     String ret = "MATCH path = (col:Collection)-[:has_dataobject]->";
131     ret += getTraversalRulesPath(traversalRule);
132     ret += "-[hr:has_reference]->(r:" + getEntityType().getSimpleName() + ")";
133     ret += getWithPart("ns", "ret");
134     ret +=
135       " WHERE d." +
136       Constants.SHEPARD_ID +
137       " = " +
138       startShepardId +
139       " AND col." +
140       Constants.SHEPARD_ID +
141       " = " +
142       collectionShepardId;
143     ret += getReturnPart("ns", "ret", "col", userName);
144     return ret;
145   }
146 
147   public String getSearchForReachableReferencesByNeo4jIdQuery(
148     TraversalRules traversalRule,
149     long collectionShepardId,
150     long startShepardId,
151     String userName
152   ) {
153     String ret = "MATCH path = (col:Collection)-[:has_dataobject]->";
154     ret += getTraversalRulesPath(traversalRule);
155     ret += "-[hr:has_reference]->(r:" + getEntityType().getSimpleName() + ")";
156     ret += getWithPart("ns", "ret");
157     ret += " WHERE id(d) = " + startShepardId + " AND id(col) = " + collectionShepardId;
158     ret += getReturnPart("ns", "ret", "col", userName);
159     return ret;
160   }
161 
162   private String getTraversalRulesPath(TraversalRules traversalRule) {
163     if (traversalRule == null) return "(d:DataObject)";
164     return switch (traversalRule) {
165       case children -> "(d:DataObject)-[:has_child*0..]->(e:DataObject)";
166       case parents -> "(d:DataObject)<-[:has_child*0..]-(e:DataObject)";
167       case successors -> "(d:DataObject)-[:has_successor*0..]->(e:DataObject)";
168       case predecessors -> "(d:DataObject)<-[:has_successor*0..]-(e:DataObject)";
169       default -> "(d:DataObject)";
170     };
171   }
172 
173   public String getSearchForReachableReferencesQuery(long collectionId, String userName) {
174     String ret = "MATCH path = (col:Collection)-[:has_dataobject]->(do:DataObject)";
175     ret += "-[hr:has_reference]->(r:" + getEntityType().getSimpleName() + ")";
176     ret += getWithPart("ns", "ret");
177     ret += " WHERE id(col) = " + collectionId;
178     ret += getReturnPart("ns", "ret", "col", userName);
179     return ret;
180   }
181 
182   public String getSearchForReachableReferencesByShepardIdQuery(long collectionShepardId, String userName) {
183     String ret = "MATCH path = (col:Collection)-[:has_dataobject]->(do:DataObject)";
184     ret += "-[hr:has_reference]->(r:" + getEntityType().getSimpleName() + ")";
185     ret += getWithPart("ns", "ret");
186     ret += " WHERE col." + Constants.SHEPARD_ID + " = " + collectionShepardId;
187     ret += getReturnPart("ns", "ret", "col", userName);
188     return ret;
189   }
190 
191   public String getSearchForReachableReferencesQuery(long collectionId, long startId, String userName) {
192     String ret = "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)";
193     ret += "-[hr:has_reference]->(r:" + getEntityType().getSimpleName() + ")";
194     ret += getWithPart("ns", "ret");
195     ret += " WHERE id(d) = " + startId + " AND id(col) = " + collectionId;
196     ret += getReturnPart("ns", "ret", "col", userName);
197     return ret;
198   }
199 
200   public String getSearchForReachableReferencesByShepardIdQuery(
201     long collectionShepardId,
202     long startShepardId,
203     String userName
204   ) {
205     String ret = "MATCH path = (col:Collection)-[:has_dataobject]->(d:DataObject)";
206     ret += "-[hr:has_reference]->(r:" + getEntityType().getSimpleName() + ")";
207     ret += getWithPart("ns", "ret");
208     ret +=
209       " WHERE d." +
210       Constants.SHEPARD_ID +
211       " = " +
212       startShepardId +
213       " AND col." +
214       Constants.SHEPARD_ID +
215       " = " +
216       collectionShepardId;
217     ret += getReturnPart("ns", "ret", "col", userName);
218     return ret;
219   }
220 
221   private String getWithPart(String nodesVar, String retVar) {
222     return " WITH nodes(path) as " + nodesVar + ", r as " + retVar;
223   }
224 
225   private String getReturnPart(String nodesVar, String retVar, String collectionVar, String username) {
226     String ret = "";
227     ret += " AND NONE(node IN " + nodesVar + " WHERE (node.deleted = TRUE))";
228     ret += " AND " + CypherQueryHelper.getReadableByQuery(collectionVar, username);
229     ret += " " + CypherQueryHelper.getReturnPart(retVar, Neighborhood.EVERYTHING);
230     return ret;
231   }
232 
233   public void deleteRelation(long fromId, long toId, String fromType, String toType, String relationName) {
234     String query = String.format(
235       "MATCH (a:%s {shepardId: %s})-[r:%s]->(b:%s {shepardId: %s}) DELETE r;",
236       fromType,
237       fromId,
238       relationName,
239       toType,
240       toId
241     );
242     session.query(query, new HashMap<String, String>());
243   }
244 
245   public abstract Class<T> getEntityType();
246 }