source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 59:e2f86ef9b871

Last change on this file since 59:e2f86ef9b871 was 59:e2f86ef9b871, checked in by casties, 11 years ago

make annotation uri in store configurable. fix npe with no tags.

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