source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 13:abe25edf2178

Last change on this file since 13:abe25edf2178 was 13:abe25edf2178, checked in by casties, 12 years ago

storing and retrieving permissions works now.

File size: 14.1 KB
Line 
1/**
2 *
3 */
4package de.mpiwg.itgroup.annotations.neo4j;
5
6import java.util.ArrayList;
7import java.util.Calendar;
8import java.util.List;
9
10import org.apache.log4j.Logger;
11import org.neo4j.graphdb.Direction;
12import org.neo4j.graphdb.GraphDatabaseService;
13import org.neo4j.graphdb.Node;
14import org.neo4j.graphdb.Relationship;
15import org.neo4j.graphdb.RelationshipType;
16import org.neo4j.graphdb.Transaction;
17import org.neo4j.graphdb.index.Index;
18import org.neo4j.graphdb.index.IndexHits;
19
20import de.mpiwg.itgroup.annotations.Actor;
21import de.mpiwg.itgroup.annotations.Annotation;
22import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes;
23import de.mpiwg.itgroup.annotations.Group;
24import de.mpiwg.itgroup.annotations.Person;
25
26/**
27 * @author casties
28 *
29 */
30public class AnnotationStore {
31
32        protected static Logger logger = Logger.getLogger(AnnotationStore.class);
33
34        protected GraphDatabaseService graphDb;
35
36        public static enum NodeTypes {
37                ANNOTATION, PERSON, TARGET
38        }
39
40        protected List<Index<Node>> nodeIndexes;
41
42        public static enum RelationTypes implements RelationshipType {
43                ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ
44        }
45
46        public static String ANNOTATION_URI_BASE = "http://entities.mpiwg-berlin.mpg.de/annotations/";
47
48        public AnnotationStore(GraphDatabaseService graphDb) {
49                super();
50                this.graphDb = graphDb;
51                nodeIndexes = new ArrayList<Index<Node>>(3);
52                // List.set(enum.ordinal(), val) seems not to work.
53                nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index()
54                                .forNodes("annotations"));
55                nodeIndexes.add(NodeTypes.PERSON.ordinal(),
56                                graphDb.index().forNodes("persons"));
57                nodeIndexes.add(NodeTypes.TARGET.ordinal(),
58                                graphDb.index().forNodes("targets"));
59        }
60
61        protected Index<Node> getNodeIndex(NodeTypes type) {
62                return nodeIndexes.get(type.ordinal());
63        }
64
65        /**
66         * Returns the Annotation with the given id.
67         *
68         * @param id
69         * @return
70         */
71        public Annotation getAnnotationById(String id) {
72                Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id)
73                                .getSingle();
74                Annotation annot = createAnnotationFromNode(annotNode);
75                return annot;
76        }
77
78        /**
79         * Returns an Annotation object from an annotation-Node.
80         *
81         * @param annotNode
82         * @return
83         */
84        public Annotation createAnnotationFromNode(Node annotNode) {
85                Annotation annot = new Annotation();
86                annot.setUri((String) annotNode.getProperty("id", null));
87                annot.setBodyText((String) annotNode.getProperty("bodyText", null));
88                annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
89                // get annotation target from relation
90                Relationship targetRel = getRelation(annotNode,
91                                RelationTypes.ANNOTATES, null);
92                if (targetRel != null) {
93                        Node target = targetRel.getEndNode();
94                        annot.setTargetBaseUri((String) target.getProperty("uri", null));
95                } else {
96                        logger.error("annotation " + annotNode + " has no target node!");
97                }
98                annot.setTargetFragment((String) annotNode.getProperty(
99                                "targetFragment", null));
100                String ft = (String) annotNode.getProperty("fragmentType", null);
101                if (ft != null) {
102                        annot.setFragmentType(FragmentTypes.valueOf(ft));
103                }
104                // get creator from relation
105                Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED,
106                                null);
107                if (creatorRel != null) {
108                        Node creatorNode = creatorRel.getStartNode();
109                        Actor creator = createActorFromNode(creatorNode);
110                        annot.setCreator(creator);
111                } else {
112                        logger.error("annotation " + annotNode + " has no creator node!");
113                }
114                // get creation date
115                annot.setCreated((String) annotNode.getProperty("created", null));
116                // get permissions
117                Relationship adminRel = getRelation(annotNode,
118                                RelationTypes.PERMITS_ADMIN, null);
119                if (adminRel != null) {
120                        Node adminNode = adminRel.getEndNode();
121                        Actor admin = createActorFromNode(adminNode);
122                        annot.setAdminPermission(admin);
123                }
124                Relationship deleteRel = getRelation(annotNode,
125                                RelationTypes.PERMITS_DELETE, null);
126                if (deleteRel != null) {
127                        Node deleteNode = deleteRel.getEndNode();
128                        Actor delete = createActorFromNode(deleteNode);
129                        annot.setDeletePermission(delete);
130                }
131                Relationship updateRel = getRelation(annotNode,
132                                RelationTypes.PERMITS_UPDATE, null);
133                if (updateRel != null) {
134                        Node updateNode = updateRel.getEndNode();
135                        Actor update = createActorFromNode(updateNode);
136                        annot.setUpdatePermission(update);
137                }
138                Relationship readRel = getRelation(annotNode,
139                                RelationTypes.PERMITS_READ, null);
140                if (readRel != null) {
141                        Node readNode = readRel.getEndNode();
142                        Actor read = createActorFromNode(readNode);
143                        annot.setReadPermission(read);
144                }
145
146                return annot;
147        }
148
149        /**
150         * Returns an Actor object from a node.
151         *
152         * @param actorNode
153         * @return
154         */
155        protected Actor createActorFromNode(Node actorNode) {
156                String uri = (String) actorNode.getProperty("uri", null);
157                String name = (String) actorNode.getProperty("name", null);
158                String type = (String) actorNode.getProperty("TYPE", null);
159                if (type != null && type.equals("PERSON")) {
160                        return new Person(uri, name);
161                } else if (type != null && type.equals("GROUP")) {
162                        return new Group(uri, name);
163                }
164                return null;
165        }
166
167        /**
168         * Store a new annotation in the store or update an existing one. Returns
169         * the stored annotation.
170         *
171         * @param annot
172         * @return
173         */
174        public Annotation storeAnnotation(Annotation annot) {
175                Node annotNode = null;
176                Transaction tx = graphDb.beginTx();
177                try {
178                        /*
179                         * create or get the annotation
180                         */
181                        String id = annot.getUri();
182                        if (id == null) {
183                                id = createRessourceURI("annot:");
184                        }
185                        annotNode = getOrCreateAnnotationNode(id);
186
187                        /*
188                         * the annotation body
189                         */
190                        String bodyText = annot.getBodyText();
191                        if (bodyText != null) {
192                                annotNode.setProperty("bodyText", bodyText);
193                        }
194                        String bodyUri = annot.getBodyUri();
195                        if (bodyUri != null) {
196                                annotNode.setProperty("bodyUri", bodyUri);
197                        }
198
199                        /*
200                         * the annotation target
201                         */
202                        String targetBaseUri = annot.getTargetBaseUri();
203                        if (targetBaseUri != null) {
204                                Node target = getOrCreateTargetNode(targetBaseUri);
205                                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, target);
206                        }
207
208                        /*
209                         * The fragment part of the annotation target.
210                         */
211                        String targetFragment = annot.getTargetFragment();
212                        FragmentTypes fragmentType = annot.getFragmentType();
213                        if (targetFragment != null) {
214                                annotNode.setProperty("targetFragment", targetFragment);
215                                annotNode.setProperty("fragmentType", fragmentType.name());
216                        }
217
218                        /*
219                         * The creator of this annotation.
220                         */
221                        Actor creator = annot.getCreator();
222                        if (creator != null) {
223                                Node creatorNode = getOrCreatePersonNode(creator);
224                                getOrCreateRelation(creatorNode, RelationTypes.CREATED,
225                                                annotNode);
226                        }
227
228                        /*
229                         * The creation date of this annotation.
230                         */
231                        String created = annot.getCreated();
232                        if (created != null) {
233                                annotNode.setProperty("created", created);
234                        }
235
236                        /*
237                         * Permissions for this annotation.
238                         */
239                        setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN,
240                                        annot.getAdminPermission());
241                        setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE,
242                                        annot.getDeletePermission());
243                        setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE,
244                                        annot.getUpdatePermission());
245                        setPermissionRelation(annotNode, RelationTypes.PERMITS_READ,
246                                        annot.getReadPermission());
247
248                        tx.success();
249                } finally {
250                        tx.finish();
251                }
252
253                // re-read and return annotation
254                Annotation storedAnnot = createAnnotationFromNode(annotNode);
255                return storedAnnot;
256        }
257
258        /**
259         * Deletes the annotation with the given id.
260         *
261         * @param id
262         */
263        public void deleteById(String id) {
264                Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id)
265                                .getSingle();
266                if (annotNode != null) {
267                        // delete related objects
268                        Transaction tx = graphDb.beginTx();
269                        try {
270                                for (Relationship rel : annotNode.getRelationships()) {
271                                        // delete relation and the related node if it has no other
272                                        // relations
273                                        Node other = rel.getOtherNode(annotNode);
274                                        rel.delete();
275                                        if (!other.hasRelationship()) {
276                                                deleteNode(other);
277                                        }
278                                }
279                                if (!annotNode.hasRelationship()) {
280                                        deleteNode(annotNode);
281                                } else {
282                                        logger.error("deleteById: unable to delete: Node still has relations.");
283                                }
284                                tx.success();
285                        } finally {
286                                tx.finish();
287                        }
288                }
289        }
290
291        /**
292         * Returns all annotations with the given uri and/or user.
293         *
294         * @param uri
295         * @param userUri
296         * @param limit
297         * @param offset
298         * @return
299         */
300        public List<Annotation> searchByUriUser(String targetUri, String userUri,
301                        String limit, String offset) {
302                List<Annotation> annotations = new ArrayList<Annotation>();
303                if (targetUri != null) {
304                        // there should be only one
305                        Node target = getNodeIndex(NodeTypes.TARGET).get("uri", targetUri)
306                                        .getSingle();
307                        if (target != null) {
308                                Iterable<Relationship> relations = target
309                                                .getRelationships(RelationTypes.ANNOTATES);
310                                for (Relationship relation : relations) {
311                                        Node ann = relation.getStartNode();
312                                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
313                                                Annotation annot = createAnnotationFromNode(ann);
314                                                annotations.add(annot);
315                                        } else {
316                                                logger.error("ANNOTATES relation does not start with ANNOTATION: "
317                                                                + ann);
318                                        }
319                                }
320                        }
321                }
322                if (userUri != null) {
323                        // there should be only one
324                        Node person = getNodeIndex(NodeTypes.PERSON).get("uri", userUri)
325                                        .getSingle();
326                        if (person != null) {
327                                Iterable<Relationship> relations = person
328                                                .getRelationships(RelationTypes.CREATED);
329                                for (Relationship relation : relations) {
330                                        Node ann = relation.getEndNode();
331                                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
332                                                Annotation annot = createAnnotationFromNode(ann);
333                                                annotations.add(annot);
334                                        } else {
335                                                logger.error("CREATED relation does not end with ANNOTATION: "
336                                                                + ann);
337                                        }
338                                }
339                        }
340                }
341                return annotations;
342        }
343
344        protected Relationship getOrCreateRelation(Node start,
345                        RelationshipType type, Node end) {
346                if (start.hasRelationship()) {
347                        // there are relations
348                        Iterable<Relationship> rels = start.getRelationships(type,
349                                        Direction.OUTGOING);
350                        for (Relationship rel : rels) {
351                                if (rel.getEndNode().equals(end)) {
352                                        // relation exists
353                                        return rel;
354                                }
355                        }
356                }
357                // create new one
358                Relationship rel;
359                Transaction tx = graphDb.beginTx();
360                try {
361                        rel = start.createRelationshipTo(end, type);
362                        tx.success();
363                } finally {
364                        tx.finish();
365                }
366                return rel;
367        }
368
369        protected Node getOrCreateAnnotationNode(String id) {
370                Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
371                IndexHits<Node> annotations = idx.get("id", id);
372                Node annotation = annotations.getSingle();
373                if (annotation == null) {
374                        // does not exist yet
375                        Transaction tx = graphDb.beginTx();
376                        try {
377                                annotation = graphDb.createNode();
378                                annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name());
379                                annotation.setProperty("id", id);
380                                idx.add(annotation, "id", id);
381                                tx.success();
382                        } finally {
383                                tx.finish();
384                        }
385                }
386                return annotation;
387        }
388
389        protected Node getOrCreateTargetNode(String uri) {
390                Index<Node> idx = getNodeIndex(NodeTypes.TARGET);
391                IndexHits<Node> targets = idx.get("uri", uri);
392                Node target = targets.getSingle();
393                if (target == null) {
394                        // does not exist yet
395                        Transaction tx = graphDb.beginTx();
396                        try {
397                                target = graphDb.createNode();
398                                target.setProperty("TYPE", NodeTypes.TARGET.name());
399                                target.setProperty("uri", uri);
400                                idx.add(target, "uri", uri);
401                                tx.success();
402                        } finally {
403                                tx.finish();
404                        }
405                }
406                return target;
407        }
408
409        protected Node getOrCreatePersonNode(Actor actor) {
410                // Person is identified by URI
411                String uri = actor.getUriString();
412                String name = actor.getName();
413                Index<Node> idx = getNodeIndex(NodeTypes.PERSON);
414                IndexHits<Node> persons = idx.get("uri", uri);
415                Node person = persons.getSingle();
416                if (person == null) {
417                        // does not exist yet
418                        Transaction tx = graphDb.beginTx();
419                        try {
420                                person = graphDb.createNode();
421                                person.setProperty("TYPE", NodeTypes.PERSON.name());
422                                person.setProperty("uri", uri);
423                                idx.add(person, "uri", uri);
424                                if (name != null) {
425                                        person.setProperty("name", name);
426                                }
427                                tx.success();
428                        } finally {
429                                tx.finish();
430                        }
431                }
432                return person;
433        }
434
435        /**
436         * Create or update permissions relations for an annotation.
437         *
438         * @param annotNode
439         * @param type
440         * @param annot
441         */
442        protected void setPermissionRelation(Node annotNode, RelationTypes type,
443                        Actor actor) {
444                Node newActorNode = null;
445                if (actor != null) {
446                        newActorNode = getOrCreatePersonNode(actor);
447                }
448                Relationship rel = getRelation(annotNode, type, null);
449                if (rel != null) {
450                        // relation exists
451                        Node oldActorNode = rel.getEndNode();
452                        if (!oldActorNode.equals(newActorNode)) {
453                                // new admin is different
454                                rel.delete();
455                                if (newActorNode != null) {
456                                        rel = getOrCreateRelation(annotNode, type, newActorNode);
457                                }
458                        }
459                } else {
460                        // no relation yet
461                        if (newActorNode != null) {
462                                rel = getOrCreateRelation(annotNode, type, newActorNode);
463                        }
464                }
465        }
466
467        /**
468         * Unindexes and deletes given Node if it has no relations.
469         *
470         * @param node
471         */
472        protected void deleteNode(Node node) {
473                Transaction tx = graphDb.beginTx();
474                try {
475                        if (node.hasRelationship()) {
476                                logger.error("deleteNode: unable to delete: Node still has relations.");
477                        } else {
478                                String ts = (String) node.getProperty("TYPE", null);
479                                try {
480                                        NodeTypes type = NodeTypes.valueOf(ts);
481                                        getNodeIndex(type).remove(node);
482                                } catch (Exception e) {
483                                        logger.error("deleteNode: unable to get TYPE of node: "
484                                                        + node);
485                                }
486                                node.delete();
487                        }
488                        tx.success();
489                } finally {
490                        tx.finish();
491                }
492        }
493
494        protected Relationship getRelation(Node start, RelationTypes type,
495                        Direction direction) {
496                Iterable<Relationship> rels;
497                if (direction == null) {
498                        // ignore direction
499                        rels = start.getRelationships(type);
500                } else {
501                        rels = start.getRelationships(type, direction);
502                }
503                for (Relationship rel : rels) {
504                        return rel;
505                }
506                return null;
507        }
508
509        /**
510         * Erzeuge eine urn aus der aktuellen Zeit in millis
511         *
512         * @return
513         */
514        private String createRessourceURI(String prefix) {
515
516                Calendar cal = Calendar.getInstance();
517
518                long time = cal.getTimeInMillis();
519
520                return String.format("%s%s%s", ANNOTATION_URI_BASE, prefix, time);
521
522        }
523
524}
Note: See TracBrowser for help on using the repository browser.