source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 68:39bc52f9b102

Last change on this file since 68:39bc52f9b102 was 68:39bc52f9b102, checked in by casties, 10 years ago

(hopefully) fixed issues with neo4j 2.0 transactions.

File size: 38.7 KB
Line 
1/**
2 *
3 */
4package de.mpiwg.itgroup.annotations.neo4j;
5
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.Calendar;
9import java.util.HashSet;
10import java.util.List;
11import java.util.Set;
12
13import org.apache.log4j.Logger;
14import org.neo4j.graphdb.Direction;
15import org.neo4j.graphdb.GraphDatabaseService;
16import org.neo4j.graphdb.Node;
17import org.neo4j.graphdb.Relationship;
18import org.neo4j.graphdb.RelationshipType;
19import org.neo4j.graphdb.Transaction;
20import org.neo4j.graphdb.index.Index;
21import org.neo4j.graphdb.index.IndexHits;
22
23import de.mpiwg.itgroup.annotations.Actor;
24import de.mpiwg.itgroup.annotations.Annotation;
25import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes;
26import de.mpiwg.itgroup.annotations.Group;
27import de.mpiwg.itgroup.annotations.Person;
28import de.mpiwg.itgroup.annotations.Resource;
29import de.mpiwg.itgroup.annotations.Tag;
30import de.mpiwg.itgroup.annotations.Target;
31import de.mpiwg.itgroup.annotations.Uri;
32
33/**
34 * Neo4J based Annotation store.
35 *
36 * @author casties
37 *
38 */
39public class AnnotationStore {
40
41    protected static Logger logger = Logger.getLogger(AnnotationStore.class);
42
43    protected GraphDatabaseService graphDb;
44
45    public static enum NodeTypes {
46        ANNOTATION, PERSON, TARGET, GROUP, TAG, RESOURCE
47    }
48
49    // types of nodes that should not be automatically deleted.
50    public Set<String> permanentNodeTypes = new HashSet<String>(Arrays.asList("PERSON", "GROUP", "TAG"));
51
52    protected List<Index<Node>> nodeIndexes;
53
54    public static enum RelationTypes implements RelationshipType {
55        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF, HAS_TAG, PART_OF
56    }
57
58    public static String ANNOTATION_URI_PREFIX = "";
59
60    public AnnotationStore(GraphDatabaseService graphDb) {
61        super();
62        this.graphDb = graphDb;
63        nodeIndexes = new ArrayList<Index<Node>>(5);
64        // List.set(enum.ordinal(), val) seems not to work.
65        try (Transaction tx = graphDb.beginTx()) {
66            nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations"));
67            nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons"));
68            nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets"));
69            nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups"));
70            nodeIndexes.add(NodeTypes.TAG.ordinal(), graphDb.index().forNodes("tags"));
71            nodeIndexes.add(NodeTypes.RESOURCE.ordinal(), graphDb.index().forNodes("resources"));
72            tx.success();
73        }
74    }
75
76    protected Index<Node> getNodeIndex(NodeTypes type) {
77        return nodeIndexes.get(type.ordinal());
78    }
79
80    /**
81     * @param userUri
82     * @return
83     */
84    public Node getPersonNodeByUri(String userUri) {
85        return getNodeFromIndex("uri", userUri, NodeTypes.PERSON);
86    }
87
88    /**
89     * @param tagUri
90     * @return
91     */
92    public Node getTagNodeByUri(String tagUri) {
93        return getNodeFromIndex("uri", tagUri, NodeTypes.TAG);
94    }
95
96    /**
97     * @param resourceUri
98     * @return
99     */
100    public Node getResourceNodeByUri(String resourceUri) {
101        return getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE);
102    }
103
104    /**
105     * @param targetUri
106     * @return
107     */
108    public Node getTargetNodeByUri(String targetUri) {
109        return getNodeFromIndex("uri", targetUri, NodeTypes.RESOURCE);
110    }
111
112    /**
113     * Returns the Node with the given key and value. Key has to be indexed.
114     *
115     * @param key
116     * @param value
117     * @param type
118     * @return
119     */
120    public Node getNodeFromIndex(String key, String value, NodeTypes type) {
121        if (key == null || value == null)
122            return null;
123        Node node = null;
124        try (Transaction tx = graphDb.beginTx()) {
125            node = getNodeIndex(type).get(key, value).getSingle();
126            tx.success();
127        }
128        return node;
129    }
130
131    /**
132     * Returns list of Actors of given type (Group or Person). Key has to be
133     * indexed.
134     *
135     * @param key
136     * @param query
137     * @param type
138     * @return
139     */
140    @SuppressWarnings("unchecked")
141    protected <T extends Actor> List<T> getActors(String key, String query, NodeTypes type) {
142        ArrayList<T> actors = new ArrayList<T>();
143        Index<Node> idx = getNodeIndex(type);
144        if (key == null) {
145            key = "uri";
146            query = "*";
147        }
148        try (Transaction tx = graphDb.beginTx()) {
149            IndexHits<Node> actorNodes = idx.query(key, query);
150            for (Node actorNode : actorNodes) {
151                Actor actor = createActorFromNode(actorNode);
152                actors.add((T) actor);
153            }
154            tx.success();           
155        }
156        return actors;
157    }
158
159    /**
160     * Returns list of groups. Key has to be indexed.
161     *
162     * @param key
163     * @param query
164     * @return
165     */
166    public List<Group> getGroups(String key, String query) {
167        List<Group> groups = getActors(key, query, NodeTypes.GROUP);
168        return groups;
169    }
170
171    /**
172     * Returns list of Persons. Key has to be indexed.
173     *
174     * @param key
175     * @param query
176     * @return
177     */
178    public List<Person> getPersons(String key, String query) {
179        List<Person> persons = getActors(key, query, NodeTypes.PERSON);
180        return persons;
181    }
182
183    /**
184     * Returns list of uri-like objects of given type (Target or Resource). Key
185     * has to be indexed.
186     *
187     * @param key
188     * @param query
189     * @param type
190     * @return
191     */
192    @SuppressWarnings("unchecked")
193    protected <T extends Uri> List<T> getUris(String key, String query, NodeTypes type) {
194        ArrayList<T> uris = new ArrayList<T>();
195        Index<Node> idx = getNodeIndex(type);
196        if (key == null) {
197            key = "uri";
198            query = "*";
199        }
200        try (Transaction tx = graphDb.beginTx()) {
201            IndexHits<Node> actorNodes = idx.query(key, query);
202            for (Node actorNode : actorNodes) {
203                Uri uri = createUriFromNode(actorNode);
204                uris.add((T) uri);
205            }
206            tx.success();
207        }
208        return uris;
209    }
210
211    /**
212     * Returns list of Targets. Key has to be indexed.
213     *
214     * @param key
215     * @param query
216     * @return
217     */
218    public List<Target> getTargets(String key, String query) {
219        List<Target> targets = getUris(key, query, NodeTypes.TARGET);
220        return targets;
221    }
222
223    /**
224     * Returns list of Resources. Key has to be indexed.
225     *
226     * @param key
227     * @param query
228     * @return
229     */
230    public List<Resource> getResources(String key, String query) {
231        List<Resource> targets = getUris(key, query, NodeTypes.RESOURCE);
232        return targets;
233    }
234
235    /**
236     * Returns List of Annotations. Key has to be indexed.
237     *
238     * @param key
239     * @param query
240     * @return
241     */
242    public List<Annotation> getAnnotations(String key, String query) {
243        ArrayList<Annotation> annotations = new ArrayList<Annotation>();
244        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
245        if (key == null) {
246            key = "id";
247            query = "*";
248        }
249        try (Transaction tx = graphDb.beginTx()) {
250            IndexHits<Node> annotNodes = idx.query(key, query);
251            for (Node annotNode : annotNodes) {
252                Annotation annotation = createAnnotationFromNode(annotNode);
253                annotations.add(annotation);
254            }
255            tx.success();
256        }
257        return annotations;
258    }
259
260    /**
261     * Returns List of Tags. Key has to be indexed.
262     *
263     * @param key
264     * @param query
265     * @return
266     */
267    public List<Tag> getTags(String key, String query) {
268        ArrayList<Tag> tags = new ArrayList<Tag>();
269        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
270        if (key == null) {
271            key = "uri";
272            query = "*";
273        }
274        try (Transaction tx = graphDb.beginTx()) {
275            IndexHits<Node> groupNodes = idx.query(key, query);
276            for (Node groupNode : groupNodes) {
277                Tag tag = createTagFromNode(groupNode);
278                tags.add(tag);
279            }
280            tx.success();
281        }
282        return tags;
283    }
284
285    /**
286     * Returns List of Groups the person is member of.
287     *
288     * @param person
289     * @return
290     */
291    public List<Group> getGroupsForPersonNode(Node person) {
292        ArrayList<Group> groups = new ArrayList<Group>();
293        Iterable<Relationship> rels = person.getRelationships(RelationTypes.MEMBER_OF);
294        for (Relationship rel : rels) {
295            Node groupNode = rel.getEndNode();
296            Actor group = createActorFromNode(groupNode);
297            // make sure we're getting a group
298            if (!(group instanceof Group)) {
299                logger.error("target of MEMBER_OF is not GROUP! rel=" + rel);
300                continue;
301            }
302            groups.add((Group) group);
303        }
304        return groups;
305    }
306
307    /**
308     * Returns if person with uri is in Group group.
309     *
310     * @param person
311     * @param group
312     * @return
313     */
314    public boolean isPersonInGroup(Person person, Group group) {
315        Node pn = getPersonNodeByUri(person.getUriString());
316        if (pn == null)
317            return false;
318        // optimized version of getGroupsForPersonNode
319        try (Transaction tx = graphDb.beginTx()) {
320            Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF);
321            for (Relationship rel : rels) {
322                Node gn = rel.getEndNode();
323                if (gn.getProperty("uri", "").equals(group.getUriString()) 
324                        || gn.getProperty("id", "").equals(group.getId())) {
325                    tx.success();
326                    return true;
327                }
328            }
329            tx.success();
330        }
331        return false;
332    }
333
334    /**
335     * Returns the members of the group.
336     *
337     * @param group
338     * @return
339     */
340    public List<Person> getMembersOfGroup(Group group) {
341        ArrayList<Person> members = new ArrayList<Person>();
342        Node gn = getActorNode(group);
343        Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
344        for (Relationship rel : rels) {
345            Node memberNode = rel.getStartNode();
346            Actor member = createActorFromNode(memberNode);
347            // make sure we're getting a group
348            if (!(member instanceof Person)) {
349                logger.error("source of MEMBER_OF is not PERSON! rel=" + rel);
350                continue;
351            }
352            members.add((Person) member);
353        }
354        return members;
355    }
356
357    /**
358     * Add Person newMember to Group group.
359     *
360     * @param group
361     * @param member
362     */
363    public Person addGroupMember(Group group, Person member) {
364        Node gn = getActorNode(group);
365        Node pn = getActorNode(member);
366        Person addedMember = null;
367        if (gn != null && pn != null) {
368            getOrCreateRelation(pn, RelationTypes.MEMBER_OF, gn);
369            addedMember = member;
370        }
371        return addedMember;
372    }
373
374    /**
375     * Delete Person oldMember from Group group.
376     *
377     * @param group
378     * @param member
379     */
380    public void deleteGroupMember(Group group, Person member) {
381        Node gn = getActorNode(group);
382        Node pn = getActorNode(member);
383        Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
384        for (Relationship rel : rels) {
385            Node mn = rel.getStartNode();
386            if (mn.equals(pn)) {
387                try (Transaction tx = graphDb.beginTx()) {
388                    rel.delete();
389                    tx.success();
390                }
391                // there should be only one
392                break;
393            }
394        }
395    }
396
397    /**
398     * Returns the stored Actor matching the given one.
399     *
400     * @param actor
401     * @return
402     */
403    public Actor getActor(Actor actor) {
404        Node actorNode = getActorNode(actor);
405        Actor storedActor = createActorFromNode(actorNode);
406        return storedActor;
407    }
408
409    /**
410     * Stores an Actor (Person or Group). Creates a new actor Node or update an
411     * existing one.
412     *
413     * @param actor
414     * @return
415     */
416    public Actor storeActor(Actor actor) {
417        Node actorNode = getOrCreateActorNode(actor);
418        try (Transaction tx = graphDb.beginTx()) {
419            // id
420            String id = actor.getId();
421            if (id != null) {
422                actorNode.setProperty("id", id);
423            }
424            // name
425            String name = actor.getName();
426            if (name != null) {
427                actorNode.setProperty("name", name);
428            }
429            // uri
430            String uri = actor.getUri();
431            if (uri != null) {
432                actorNode.setProperty("uri", uri);
433            }
434            tx.success();
435        }
436        Actor storedActor = createActorFromNode(actorNode);
437        return storedActor;
438    }
439
440    /**
441     * Deletes the given Actor.
442     *
443     * @param actor
444     */
445    public void deleteActor(Actor actor) {
446        String uri = actor.getUriString();
447        Index<Node> idx;
448        if (actor.isGroup()) {
449            idx = getNodeIndex(NodeTypes.GROUP);
450        } else {
451            idx = getNodeIndex(NodeTypes.PERSON);
452        }
453        Node actorNode = idx.get("uri", uri).getSingle();
454        if (actorNode != null) {
455            // delete relations
456            try (Transaction tx = graphDb.beginTx()) {
457                for (Relationship rel : actorNode.getRelationships()) {
458                    rel.delete();
459                }
460                if (!actorNode.hasRelationship()) {
461                    // this shouldn't happen
462                    deleteNode(actorNode);
463                } else {
464                    logger.error("deleteActor: unable to delete: Node still has relations.");
465                }
466                tx.success();
467            }
468        }
469    }
470
471    /**
472     * Returns the Annotation with the given id.
473     *
474     * @param id
475     * @return
476     */
477    public Annotation getAnnotationById(String id) {
478        Annotation annot = null;
479        try (Transaction tx = graphDb.beginTx()) {
480            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
481            annot = createAnnotationFromNode(annotNode);
482            tx.success();
483        }
484        return annot;
485    }
486
487    /**
488     * Returns an Annotation object from an annotation-Node.
489     *
490     * @param annotNode
491     * @return
492     */
493    public Annotation createAnnotationFromNode(Node annotNode) {
494        Annotation annot = new Annotation();
495        try (Transaction tx = graphDb.beginTx()) {
496            annot.setUri((String) annotNode.getProperty("id", null));
497            annot.setBodyText((String) annotNode.getProperty("bodyText", null));
498            annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
499            /*
500             * get annotation target and resource from relation
501             */
502            for (Relationship rel : annotNode.getRelationships(RelationTypes.ANNOTATES)) {
503                Node target = rel.getEndNode();
504                String type = (String) target.getProperty("TYPE");
505                if (type.equals("TARGET")) {
506                    annot.setTarget(new Target((String) target.getProperty("uri", null)));
507                } else if (type.equals("RESOURCE")) {
508                    annot.setResource(new Resource((String) target.getProperty("uri", null)));
509                }
510            }
511            if (annot.getTarget() == null) {
512                logger.warn("annotation " + annotNode + " has no target node!");
513            }
514            // get fragment from attribute
515            annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null));
516            String ft = (String) annotNode.getProperty("fragmentType", null);
517            if (ft != null) {
518                annot.setFragmentType(FragmentTypes.valueOf(ft));
519            }
520            /*
521             * get creator from relation
522             */
523            Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null);
524            if (creatorRel != null) {
525                Node creatorNode = creatorRel.getStartNode();
526                Actor creator = createActorFromNode(creatorNode);
527                annot.setCreator(creator);
528            } else {
529                logger.warn("annotation " + annotNode + " has no creator node!");
530            }
531            /*
532             * get creation date
533             */
534            annot.setCreated((String) annotNode.getProperty("created", null));
535            /*
536             * get permissions
537             */
538            Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null);
539            if (adminRel != null) {
540                Node adminNode = adminRel.getEndNode();
541                Actor admin = createActorFromNode(adminNode);
542                annot.setAdminPermission(admin);
543            }
544            Relationship deleteRel = getRelation(annotNode, RelationTypes.PERMITS_DELETE, null);
545            if (deleteRel != null) {
546                Node deleteNode = deleteRel.getEndNode();
547                Actor delete = createActorFromNode(deleteNode);
548                annot.setDeletePermission(delete);
549            }
550            Relationship updateRel = getRelation(annotNode, RelationTypes.PERMITS_UPDATE, null);
551            if (updateRel != null) {
552                Node updateNode = updateRel.getEndNode();
553                Actor update = createActorFromNode(updateNode);
554                annot.setUpdatePermission(update);
555            }
556            Relationship readRel = getRelation(annotNode, RelationTypes.PERMITS_READ, null);
557            if (readRel != null) {
558                Node readNode = readRel.getEndNode();
559                Actor read = createActorFromNode(readNode);
560                annot.setReadPermission(read);
561            }
562            /*
563             * get tags
564             */
565            Set<String> tags = new HashSet<String>();
566            for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
567                String tag = (String) rel.getEndNode().getProperty("name", null);
568                if (tag != null) {
569                    tags.add(tag);
570                }
571            }
572            annot.setTags(tags);
573
574            tx.success();
575        }
576        return annot;
577    }
578
579    /**
580     * Returns an Actor object from a node.
581     *
582     * @param actorNode
583     * @return
584     */
585    protected Actor createActorFromNode(Node actorNode) {
586        if (actorNode == null)
587            return null;
588        try (Transaction tx = graphDb.beginTx()) {
589            String id = (String) actorNode.getProperty("id", null);
590            String uri = (String) actorNode.getProperty("uri", null);
591            String name = (String) actorNode.getProperty("name", null);
592            String type = (String) actorNode.getProperty("TYPE", null);
593            tx.success();
594            if (type != null && type.equals("PERSON")) {
595                return new Person(id, uri, name);
596            } else if (type != null && type.equals("GROUP")) {
597                return new Group(id, uri, name);
598            }
599        }
600        return null;
601    }
602
603    public Tag createTagFromNode(Node tagNode) {
604        if (tagNode == null)
605            return null;
606        String id;
607        String uri;
608        String name;
609        try (Transaction tx = graphDb.beginTx()) {
610            name = (String) tagNode.getProperty("name", null);
611            uri = (String) tagNode.getProperty("uri", null);
612            id = (String) tagNode.getProperty("id", null);
613            tx.success();
614        }
615        return new Tag(id, uri, name);
616
617    }
618
619    /**
620     * @param resourceNode
621     * @return
622     */
623    public Resource createResourceFromNode(Node resourceNode) {
624        return (Resource) createUriFromNode(resourceNode);
625    }
626
627    /**
628     * @param targetNode
629     * @return
630     */
631    public Target createTargetFromNode(Node targetNode) {
632        return (Target) createUriFromNode(targetNode);
633    }
634
635    protected Uri createUriFromNode(Node uriNode) {
636        if (uriNode == null)
637            return null;
638        try (Transaction tx = graphDb.beginTx()) {
639            String uri = (String) uriNode.getProperty("uri", null);
640            String type = (String) uriNode.getProperty("TYPE", null);
641            tx.success();
642            if (type != null && type.equals("TARGET")) {
643                return new Target(uri);
644            } else if (type != null && type.equals("RESOURCE")) {
645                return new Resource(uri);
646            }
647        }
648        return null;
649    }
650
651    /**
652     * Store a new annotation in the store or update an existing one. Returns
653     * the stored annotation.
654     *
655     * @param annot
656     * @return
657     */
658    public Annotation storeAnnotation(Annotation annot) {
659        Node annotNode = null;
660        try (Transaction tx = graphDb.beginTx()) {
661            /*
662             * create or get the annotation
663             */
664            String id = annot.getUri();
665            if (id == null) {
666                id = createRessourceURI("annot:");
667            }
668            annotNode = getOrCreateAnnotationNode(id);
669
670            /*
671             * the annotation body
672             */
673            String bodyText = annot.getBodyText();
674            if (bodyText != null) {
675                annotNode.setProperty("bodyText", bodyText);
676            }
677            String bodyUri = annot.getBodyUri();
678            if (bodyUri != null) {
679                annotNode.setProperty("bodyUri", bodyUri);
680            }
681               
682            /*
683             * the annotation target
684             */
685            Target target = annot.getTarget();
686            Node targetNode = null;
687            if (target != null) {
688                targetNode = getOrCreateUriNode(target.getUri(), NodeTypes.TARGET);
689                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, targetNode);
690            }
691
692            /*
693             * The fragment part of the annotation target.
694             */
695            String targetFragment = annot.getTargetFragment();
696            FragmentTypes fragmentType = annot.getFragmentType();
697            if (targetFragment != null) {
698                annotNode.setProperty("targetFragment", targetFragment);
699                annotNode.setProperty("fragmentType", fragmentType.name());
700            }
701
702            /*
703             * the annotation resource
704             */
705            Resource resource = annot.getResource();
706            if (resource != null) {
707                Node resourceNode = getOrCreateUriNode(resource.getUri(), NodeTypes.RESOURCE);
708                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, resourceNode);
709                getOrCreateRelation(targetNode, RelationTypes.PART_OF, resourceNode);
710            }
711
712            /*
713             * The creator of this annotation.
714             */
715            Actor creator = annot.getCreator();
716            if (creator != null) {
717                Node creatorNode = getOrCreateActorNode(creator);
718                getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode);
719            }
720
721            /*
722             * The creation date of this annotation.
723             */
724            String created = annot.getCreated();
725            if (created != null) {
726                annotNode.setProperty("created", created);
727            }
728
729            /*
730             * Permissions for this annotation.
731             */
732            setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission());
733            setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission());
734            setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission());
735            setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
736
737            /*
738             * Tags on this annotation.
739             */
740            Set<String> newTags = annot.getTags();
741            // we ignore existing tags if tags == null
742            if (newTags != null) {
743                List<Relationship> oldHasTags = new ArrayList<Relationship>();
744                for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
745                    oldHasTags.add(rel);
746                }
747                // adjust to new tags
748                if (newTags.isEmpty()) {
749                    // remove old tags
750                    if (!oldHasTags.isEmpty()) {
751                        for (Relationship rel : oldHasTags) {
752                            rel.delete();
753                            // TODO: should we delete orphan nodes too?
754                        }
755                    }
756                } else {
757                    if (!oldHasTags.isEmpty()) {
758                        // adjust old tags
759                        for (Relationship rel : oldHasTags) {
760                            String oldTag = (String) rel.getEndNode().getProperty("name", null);
761                            if (newTags.contains(oldTag)) {
762                                // tag exists
763                                newTags.remove(oldTag);
764                            } else {
765                                // tag exists no longer
766                                rel.delete();
767                                // TODO: should we delete orphan nodes too?
768                            }
769                        }
770                    }
771                    if (!newTags.isEmpty()) {
772                        // still tags to add
773                        for (String tag : newTags) {
774                            // create new tag
775                            Node tagNode = getOrCreateTagNode(new Tag(null, null, tag));
776                            getOrCreateRelation(annotNode, RelationTypes.HAS_TAG, tagNode);
777                        }
778                    }
779
780                }
781            }
782            tx.success();
783        }
784
785        // re-read and return annotation
786        Annotation storedAnnot = createAnnotationFromNode(annotNode);
787        return storedAnnot;
788    }
789
790    /**
791     * Deletes the annotation with the given id.
792     *
793     * @param id
794     */
795    public void deleteAnnotationById(String id) {
796        try (Transaction tx = graphDb.beginTx()) {
797            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
798            if (annotNode != null) {
799                // delete related objects
800                for (Relationship rel : annotNode.getRelationships()) {
801                    // delete relation and the related node if it has no other
802                    // relations and is not permanent
803                    Node other = rel.getOtherNode(annotNode);
804                    rel.delete();
805                    if (!(other.hasRelationship() || permanentNodeTypes.contains(other.getProperty("TYPE", null)))) {
806                        deleteNode(other);
807                    }
808                }
809                if (!annotNode.hasRelationship()) {
810                    deleteNode(annotNode);
811                } else {
812                    logger.error("deleteById: unable to delete: Node still has relations.");
813                }
814            }
815            tx.success();
816        }
817    }
818
819    /**
820     * Returns all annotations with the given uri and/or user.
821     *
822     * @param uri
823     * @param userUri
824     * @return
825     */
826    public List<Annotation> searchAnnotationByUriUser(String targetUri, String userUri) {
827        List<Annotation> annotations = new ArrayList<Annotation>();
828        if (targetUri != null) {
829            // there should be only one
830            Node target = getNodeFromIndex("uri", targetUri, NodeTypes.TARGET);
831            if (target != null) {
832                try (Transaction tx = graphDb.beginTx()) {
833                    Iterable<Relationship> relations = target.getRelationships(RelationTypes.ANNOTATES);
834                    for (Relationship relation : relations) {
835                        Node ann = relation.getStartNode();
836                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
837                            Annotation annot = createAnnotationFromNode(ann);
838                            annotations.add(annot);
839                        } else {
840                            logger.error("ANNOTATES relation does not start with ANNOTATION: " + ann);
841                        }
842                    }
843                    tx.success();
844                }
845            }
846        }
847        if (userUri != null) {
848            // there should be only one
849            Node person = getPersonNodeByUri(userUri);
850            if (person != null) {
851                try (Transaction tx = graphDb.beginTx()) {
852                    Iterable<Relationship> relations = person.getRelationships(RelationTypes.CREATED);
853                    for (Relationship relation : relations) {
854                        Node ann = relation.getEndNode();
855                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
856                            Annotation annot = createAnnotationFromNode(ann);
857                            annotations.add(annot);
858                        } else {
859                            logger.error("CREATED relation does not end with ANNOTATION: " + ann);
860                        }
861                    }
862                    tx.success();
863                }
864            }
865        }
866        // TODO: if both uri and user are given we should intersect
867
868        return annotations;
869    }
870
871    /**
872     * Returns Relationship of type from Node start to Node end. Creates one if
873     * it doesn't exist.
874     *
875     * @param start
876     * @param type
877     * @param end
878     * @return
879     */
880    protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
881        if (start == null || end == null)
882            return null;
883        if (start.hasRelationship()) {
884            // there are relations
885            try (Transaction tx = graphDb.beginTx()) {
886                Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING);
887                for (Relationship rel : rels) {
888                    if (rel.getEndNode().equals(end)) {
889                        // relation exists
890                        tx.success();
891                        return rel;
892                    }
893                }
894                tx.success();
895            }
896        }
897        // create new one
898        Relationship rel;
899        try (Transaction tx = graphDb.beginTx()) {
900            rel = start.createRelationshipTo(end, type);
901            tx.success();
902        }
903        return rel;
904    }
905
906    protected Node getOrCreateAnnotationNode(String id) {
907        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
908        Node annotation;
909        try (Transaction tx = graphDb.beginTx()) {
910            IndexHits<Node> annotations = idx.get("id", id);
911            annotation = annotations.getSingle();
912            if (annotation == null) {
913                // does not exist yet
914                annotation = graphDb.createNode();
915                annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name());
916                annotation.setProperty("id", id);
917                idx.add(annotation, "id", id);
918            }
919            tx.success();
920        }
921        return annotation;
922    }
923
924    protected Node getOrCreateUriNode(String uri, NodeTypes type) {
925        Index<Node> idx = getNodeIndex(type);
926        Node target;
927        try (Transaction tx = graphDb.beginTx()) {
928            IndexHits<Node> targets = idx.get("uri", uri);
929            target = targets.getSingle();
930            if (target == null) {
931                // does not exist yet
932                target = graphDb.createNode();
933                target.setProperty("TYPE", type.name());
934                target.setProperty("uri", uri);
935                idx.add(target, "uri", uri);
936            }
937            tx.success();
938        }
939        return target;
940    }
941
942    protected Node getActorNode(Actor actor) {
943        // Person/Group is identified by URI or id
944        String uri = actor.getUriString();
945        Index<Node> idx;
946        Node person;
947        if (actor.isGroup()) {
948            idx = getNodeIndex(NodeTypes.GROUP);
949        } else {
950            idx = getNodeIndex(NodeTypes.PERSON);
951        }
952        try (Transaction tx = graphDb.beginTx()) {
953            IndexHits<Node> persons = idx.get("uri", uri);
954            person = persons.getSingle();
955            tx.success();
956        }
957        return person;
958    }
959
960    protected Node getOrCreateActorNode(Actor actor) {
961        // Person/Group is identified by URI or id
962        String uri = actor.getUriString();
963        String name = actor.getName();
964        String id = actor.getId();
965        Node person;
966        try (Transaction tx = graphDb.beginTx()) {
967            Index<Node> idx;
968            if (actor.isGroup()) {
969                idx = getNodeIndex(NodeTypes.GROUP);
970            } else {
971                idx = getNodeIndex(NodeTypes.PERSON);
972            }
973            IndexHits<Node> persons = idx.get("uri", uri);
974            person = persons.getSingle();
975            if (person == null) {
976                // does not exist yet
977                person = graphDb.createNode();
978                if (actor.isGroup()) {
979                    person.setProperty("TYPE", NodeTypes.GROUP.name());
980                } else {
981                    person.setProperty("TYPE", NodeTypes.PERSON.name());
982                }
983                person.setProperty("uri", uri);
984                idx.add(person, "uri", uri);
985                if (name != null) {
986                    person.setProperty("name", name);
987                }
988                if (id != null) {
989                    person.setProperty("id", id);
990                }
991            }
992            tx.success();
993        }
994        return person;
995    }
996
997    protected Node getOrCreateTagNode(Tag inTag) {
998        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
999        String tagname = inTag.getName();
1000        Node tag;
1001        try (Transaction tx = graphDb.beginTx()) {
1002            IndexHits<Node> tags = idx.get("name", tagname);
1003            tag = tags.getSingle();
1004            if (tag == null) {
1005                // does not exist yet
1006                tag = graphDb.createNode();
1007                tag.setProperty("TYPE", NodeTypes.TAG.name());
1008                tag.setProperty("name", tagname);
1009                idx.add(tag, "name", tagname);
1010
1011                tag.setProperty("id", inTag.getId());
1012                tag.setProperty("uri", inTag.getUri());
1013                idx.add(tag, "uri", inTag.getUri());
1014            }
1015            tx.success();
1016        }
1017        return tag;
1018    }
1019
1020    /**
1021     * Create or update permissions relations for an annotation.
1022     *
1023     * @param annotNode
1024     * @param type
1025     * @param annot
1026     */
1027    protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) {
1028        Node newActorNode = null;
1029        if (actor != null) {
1030            newActorNode = getOrCreateActorNode(actor);
1031        }
1032        Relationship rel = getRelation(annotNode, type, null);
1033        if (rel != null) {
1034            // relation exists
1035            try (Transaction tx = graphDb.beginTx()) {
1036                Node oldActorNode = rel.getEndNode();
1037                if (!oldActorNode.equals(newActorNode)) {
1038                    // new admin is different
1039                    rel.delete();
1040                    tx.success();
1041                }
1042                if (newActorNode != null) {
1043                    rel = getOrCreateRelation(annotNode, type, newActorNode);
1044                }
1045                tx.success();
1046            }
1047        } else {
1048            // no relation yet
1049            if (newActorNode != null) {
1050                rel = getOrCreateRelation(annotNode, type, newActorNode);
1051            }
1052        }
1053    }
1054
1055    /**
1056     * Unindexes and deletes given Node if it has no relations.
1057     *
1058     * @param node
1059     */
1060    protected void deleteNode(Node node) {
1061        try (Transaction tx = graphDb.beginTx()) {
1062            if (node.hasRelationship()) {
1063                logger.error("deleteNode: unable to delete: Node still has relations.");
1064            } else {
1065                String ts = (String) node.getProperty("TYPE", null);
1066                try {
1067                    NodeTypes type = NodeTypes.valueOf(ts);
1068                    getNodeIndex(type).remove(node);
1069                } catch (Exception e) {
1070                    logger.error("deleteNode: unable to get TYPE of node: " + node);
1071                }
1072                node.delete();
1073            }
1074            tx.success();
1075        }
1076    }
1077
1078    /**
1079     * returns the (first) Relationship of RelationTypes type from Node start.
1080     *
1081     * @param start
1082     * @param type
1083     * @param direction
1084     * @return
1085     */
1086    protected Relationship getRelation(Node start, RelationTypes type, Direction direction) {
1087        Iterable<Relationship> rels;
1088        try (Transaction tx = graphDb.beginTx()) {
1089            if (direction == null) {
1090                // ignore direction
1091                rels = start.getRelationships(type);
1092            } else {
1093                rels = start.getRelationships(type, direction);
1094            }
1095            tx.success();
1096            for (Relationship rel : rels) {
1097                // just the first one
1098                return rel;
1099            }
1100        }
1101        return null;
1102    }
1103
1104    /**
1105     * Erzeuge eine urn aus der aktuellen Zeit in millis
1106     *
1107     * @return
1108     */
1109    private String createRessourceURI(String prefix) {
1110        Calendar cal = Calendar.getInstance();
1111        long time = cal.getTimeInMillis();
1112        return String.format("%s%s%s", ANNOTATION_URI_PREFIX, prefix, time);
1113    }
1114
1115    public List<Annotation> getAnnotationsByTag(String tagUri) {
1116        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1117        Node tag = getTagNodeByUri(tagUri);
1118        if (tag != null) {
1119            try (Transaction tx = graphDb.beginTx()) {
1120                Iterable<Relationship> rels = tag.getRelationships(Direction.INCOMING, RelationTypes.HAS_TAG);
1121                for (Relationship rel : rels) {
1122                    Node node = rel.getStartNode();
1123                    ret.add(createAnnotationFromNode(node));
1124                }
1125                tx.success();
1126            }
1127        }
1128        return ret;
1129    }
1130
1131    public List<Annotation> getAnnotationsByResource(String resourceUri) {
1132        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1133        Node res = getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE);
1134        if (res != null) {
1135            try (Transaction tx = graphDb.beginTx()) {
1136                Iterable<Relationship> rels = res.getRelationships(Direction.INCOMING, RelationTypes.ANNOTATES);
1137                for (Relationship rel : rels) {
1138                    Node an = rel.getStartNode();
1139                    Node rn = rel.getEndNode();
1140                    if (rn.getProperty("TYPE", "").equals("RESOURCE")) {
1141                        logger.error("getAnnotationsByResource got ANNOTATES != RESOURCE");
1142                    }
1143                    ret.add(createAnnotationFromNode(an));
1144                }
1145                tx.success();
1146            }
1147        }
1148        return ret;
1149    }
1150
1151}
Note: See TracBrowser for help on using the repository browser.