source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 67:875a97f8b8da

Last change on this file since 67:875a97f8b8da was 67:875a97f8b8da, checked in by casties, 10 years ago

more quick fixes for neo4j 2.0.

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