source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 70:2b1e6df5e21a

Last change on this file since 70:2b1e6df5e21a was 70:2b1e6df5e21a, checked in by casties, 10 years ago

added lgpl_v3 license information.

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