source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 66:5b568de5ee0d

Last change on this file since 66:5b568de5ee0d was 66:5b568de5ee0d, checked in by casties, 10 years ago

updated to new Neo4J version 2.0. doesn't use new features. problems with neo4j admin web ui.

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