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

Last change on this file since 98:5a764c625290 was 98:5a764c625290, checked in by casties, 9 years ago

oops, forgot to store updated timestamp in db.

File size: 40.2 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;
35import java.util.logging.Logger;
36
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("de.mpiwg.itgroup.annotations.neo4j.AnnotationStore");
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        try (Transaction tx = graphDb.beginTx()) {
317                Iterable<Relationship> rels = person.getRelationships(RelationTypes.MEMBER_OF);
318                for (Relationship rel : rels) {
319                        Node groupNode = rel.getEndNode();
320                        Actor group = createActorFromNode(groupNode);
321                        // make sure we're getting a group
322                        if (!(group instanceof Group)) {
323                                logger.severe("target of MEMBER_OF is not GROUP! rel=" + rel);
324                                continue;
325                        }
326                        groups.add((Group) group);
327                }
328                tx.success();
329        }
330        return groups;
331    }
332
333    /**
334     * Returns if person with uri is in Group group.
335     *
336     * @param person
337     * @param group
338     * @return
339     */
340    public boolean isPersonInGroup(Person person, Group group) {
341        Node pn = getPersonNodeByUri(person.getUriString());
342        if (pn == null)
343            return false;
344        // optimized version of getGroupsForPersonNode
345        try (Transaction tx = graphDb.beginTx()) {
346            Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF);
347            for (Relationship rel : rels) {
348                Node gn = rel.getEndNode();
349                if (gn.getProperty("uri", "").equals(group.getUriString()) 
350                        || gn.getProperty("id", "").equals(group.getId())) {
351                    tx.success();
352                    return true;
353                }
354            }
355            tx.success();
356        }
357        return false;
358    }
359
360    /**
361     * Returns the members of the group.
362     *
363     * @param group
364     * @return
365     */
366    public List<Person> getMembersOfGroup(Group group) {
367        ArrayList<Person> members = new ArrayList<Person>();
368        Node gn = getActorNode(group);
369        try (Transaction tx = graphDb.beginTx()) {
370                Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
371                for (Relationship rel : rels) {
372                        Node memberNode = rel.getStartNode();
373                        Actor member = createActorFromNode(memberNode);
374                        // make sure we're getting a group
375                        if (!(member instanceof Person)) {
376                                logger.severe("source of MEMBER_OF is not PERSON! rel=" + rel);
377                                continue;
378                        }
379                        members.add((Person) member);
380                }
381                tx.success();
382        }
383        return members;
384    }
385
386    /**
387     * Add Person newMember to Group group.
388     *
389     * @param group
390     * @param member
391     */
392    public Person addGroupMember(Group group, Person member) {
393        Node gn = getActorNode(group);
394        Node pn = getActorNode(member);
395        Person addedMember = null;
396        if (gn != null && pn != null) {
397            getOrCreateRelation(pn, RelationTypes.MEMBER_OF, gn);
398            addedMember = member;
399        }
400        return addedMember;
401    }
402
403    /**
404     * Delete Person oldMember from Group group.
405     *
406     * @param group
407     * @param member
408     */
409    public void deleteGroupMember(Group group, Person member) {
410        Node gn = getActorNode(group);
411        Node pn = getActorNode(member);
412        try (Transaction tx = graphDb.beginTx()) {
413                Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
414                for (Relationship rel : rels) {
415                        Node mn = rel.getStartNode();
416                        if (mn.equals(pn)) {
417                    rel.delete();
418                }
419                // there should be only one
420                break;
421            }
422            tx.success();
423        }
424    }
425
426    /**
427     * Returns the stored Actor matching the given one.
428     *
429     * @param actor
430     * @return
431     */
432    public Actor getActor(Actor actor) {
433        Node actorNode = getActorNode(actor);
434        Actor storedActor = createActorFromNode(actorNode);
435        return storedActor;
436    }
437
438    /**
439     * Stores an Actor (Person or Group). Creates a new actor Node or update an
440     * existing one.
441     *
442     * @param actor
443     * @return
444     */
445    public Actor storeActor(Actor actor) {
446        Node actorNode = getOrCreateActorNode(actor);
447        try (Transaction tx = graphDb.beginTx()) {
448            // id
449            String id = actor.getId();
450            if (id != null) {
451                actorNode.setProperty("id", id);
452            }
453            // name
454            String name = actor.getName();
455            if (name != null) {
456                actorNode.setProperty("name", name);
457            }
458            // uri
459            String uri = actor.getUri();
460            if (uri != null) {
461                actorNode.setProperty("uri", uri);
462            }
463            tx.success();
464        }
465        Actor storedActor = createActorFromNode(actorNode);
466        return storedActor;
467    }
468
469    /**
470     * Deletes the given Actor.
471     *
472     * @param actor
473     */
474    public void deleteActor(Actor actor) {
475        String uri = actor.getUriString();
476        Index<Node> idx;
477        if (actor.isGroup()) {
478            idx = getNodeIndex(NodeTypes.GROUP);
479        } else {
480            idx = getNodeIndex(NodeTypes.PERSON);
481        }
482        try (Transaction tx = graphDb.beginTx()) {
483                Node actorNode = idx.get("uri", uri).getSingle();
484                if (actorNode != null) {
485                        // delete relations
486                for (Relationship rel : actorNode.getRelationships()) {
487                        rel.delete();
488                }
489                if (!actorNode.hasRelationship()) {
490                    // this shouldn't happen
491                    deleteNode(actorNode);
492                } else {
493                    logger.severe("deleteActor: unable to delete: Node still has relations.");
494                }
495            }
496            tx.success();
497        }
498    }
499
500    /**
501     * Returns the Annotation with the given id.
502     *
503     * @param id
504     * @return
505     */
506    public Annotation getAnnotationById(String id) {
507        Annotation annot = null;
508        try (Transaction tx = graphDb.beginTx()) {
509            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
510            annot = createAnnotationFromNode(annotNode);
511            tx.success();
512        }
513        return annot;
514    }
515
516    /**
517     * Returns an Annotation object from an annotation-Node.
518     *
519     * @param annotNode
520     * @return
521     */
522    public Annotation createAnnotationFromNode(Node annotNode) {
523        Annotation annot = new Annotation();
524        try (Transaction tx = graphDb.beginTx()) {
525            annot.setUri((String) annotNode.getProperty("id", null));
526            annot.setBodyText((String) annotNode.getProperty("bodyText", null));
527            annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
528            annot.setQuote((String) annotNode.getProperty("quote", null));
529            /*
530             * get annotation target and resource from relation
531             */
532            for (Relationship rel : annotNode.getRelationships(RelationTypes.ANNOTATES)) {
533                Node target = rel.getEndNode();
534                String type = (String) target.getProperty("TYPE");
535                if (type.equals("TARGET")) {
536                    annot.setTarget(new Target((String) target.getProperty("uri", null)));
537                } else if (type.equals("RESOURCE")) {
538                    annot.setResource(new Resource((String) target.getProperty("uri", null)));
539                }
540            }
541            if (annot.getTarget() == null) {
542                logger.warning("annotation " + annotNode + " has no target node!");
543            }
544            // get fragment from attribute
545            annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null));
546            String ft = (String) annotNode.getProperty("fragmentType", null);
547            if (ft != null) {
548                annot.setFragmentType(FragmentTypes.valueOf(ft));
549            }
550            /*
551             * get creator from relation
552             */
553            Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null);
554            if (creatorRel != null) {
555                Node creatorNode = creatorRel.getStartNode();
556                Actor creator = createActorFromNode(creatorNode);
557                annot.setCreator(creator);
558            } else {
559                logger.warning("annotation " + annotNode + " has no creator node!");
560            }
561            /*
562             * get creation and update date
563             */
564            annot.setCreated((String) annotNode.getProperty("created", null));
565            annot.setUpdated((String) annotNode.getProperty("updated", null));
566            /*
567             * get permissions
568             */
569            Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null);
570            if (adminRel != null) {
571                Node adminNode = adminRel.getEndNode();
572                Actor admin = createActorFromNode(adminNode);
573                annot.setAdminPermission(admin);
574            }
575            Relationship deleteRel = getRelation(annotNode, RelationTypes.PERMITS_DELETE, null);
576            if (deleteRel != null) {
577                Node deleteNode = deleteRel.getEndNode();
578                Actor delete = createActorFromNode(deleteNode);
579                annot.setDeletePermission(delete);
580            }
581            Relationship updateRel = getRelation(annotNode, RelationTypes.PERMITS_UPDATE, null);
582            if (updateRel != null) {
583                Node updateNode = updateRel.getEndNode();
584                Actor update = createActorFromNode(updateNode);
585                annot.setUpdatePermission(update);
586            }
587            Relationship readRel = getRelation(annotNode, RelationTypes.PERMITS_READ, null);
588            if (readRel != null) {
589                Node readNode = readRel.getEndNode();
590                Actor read = createActorFromNode(readNode);
591                annot.setReadPermission(read);
592            }
593            /*
594             * get tags
595             */
596            Set<String> tags = new HashSet<String>();
597            for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
598                String tag = (String) rel.getEndNode().getProperty("name", null);
599                if (tag != null) {
600                    tags.add(tag);
601                }
602            }
603            annot.setTags(tags);
604
605            tx.success();
606        }
607        return annot;
608    }
609
610    /**
611     * Returns an Actor object from a node.
612     *
613     * @param actorNode
614     * @return
615     */
616    protected Actor createActorFromNode(Node actorNode) {
617        if (actorNode == null)
618            return null;
619        try (Transaction tx = graphDb.beginTx()) {
620            String id = (String) actorNode.getProperty("id", null);
621            String uri = (String) actorNode.getProperty("uri", null);
622            String name = (String) actorNode.getProperty("name", null);
623            String type = (String) actorNode.getProperty("TYPE", null);
624            tx.success();
625            if (type != null && type.equals("PERSON")) {
626                return new Person(id, uri, name);
627            } else if (type != null && type.equals("GROUP")) {
628                return new Group(id, uri, name);
629            }
630        }
631        return null;
632    }
633
634    public Tag createTagFromNode(Node tagNode) {
635        if (tagNode == null)
636            return null;
637        String id;
638        String uri;
639        String name;
640        try (Transaction tx = graphDb.beginTx()) {
641            name = (String) tagNode.getProperty("name", null);
642            uri = (String) tagNode.getProperty("uri", null);
643            id = (String) tagNode.getProperty("id", null);
644            tx.success();
645        }
646        return new Tag(id, uri, name);
647
648    }
649
650    /**
651     * @param resourceNode
652     * @return
653     */
654    public Resource createResourceFromNode(Node resourceNode) {
655        return (Resource) createUriFromNode(resourceNode);
656    }
657
658    /**
659     * @param targetNode
660     * @return
661     */
662    public Target createTargetFromNode(Node targetNode) {
663        return (Target) createUriFromNode(targetNode);
664    }
665
666    protected Uri createUriFromNode(Node uriNode) {
667        if (uriNode == null)
668            return null;
669        try (Transaction tx = graphDb.beginTx()) {
670            String uri = (String) uriNode.getProperty("uri", null);
671            String type = (String) uriNode.getProperty("TYPE", null);
672            tx.success();
673            if (type != null && type.equals("TARGET")) {
674                return new Target(uri);
675            } else if (type != null && type.equals("RESOURCE")) {
676                return new Resource(uri);
677            }
678        }
679        return null;
680    }
681
682    /**
683     * Store a new annotation in the store or update an existing one. Returns
684     * the stored annotation.
685     *
686     * @param annot
687     * @return
688     */
689    public Annotation storeAnnotation(Annotation annot) {
690        Node annotNode = null;
691        try (Transaction tx = graphDb.beginTx()) {
692            /*
693             * create or get the annotation
694             */
695            String id = annot.getUri();
696            if (id == null) {
697                id = createRessourceURI("annot:");
698            }
699            annotNode = getOrCreateAnnotationNode(id);
700
701            /*
702             * the annotation body
703             */
704            String bodyText = annot.getBodyText();
705            if (bodyText != null) {
706                annotNode.setProperty("bodyText", bodyText);
707            }
708            String bodyUri = annot.getBodyUri();
709            if (bodyUri != null) {
710                annotNode.setProperty("bodyUri", bodyUri);
711            }
712           
713            /*
714             * the annotation quote
715             */
716            String quote = annot.getQuote();
717            if (quote != null) {
718                annotNode.setProperty("quote", quote);
719            }
720               
721            /*
722             * the annotation target
723             */
724            Target target = annot.getTarget();
725            Node targetNode = null;
726            if (target != null) {
727                targetNode = getOrCreateUriNode(target.getUri(), NodeTypes.TARGET);
728                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, targetNode);
729            }
730
731            /*
732             * The fragment part of the annotation target.
733             */
734            String targetFragment = annot.getTargetFragment();
735            FragmentTypes fragmentType = annot.getFragmentType();
736            if (targetFragment != null) {
737                annotNode.setProperty("targetFragment", targetFragment);
738                annotNode.setProperty("fragmentType", fragmentType.name());
739            }
740
741            /*
742             * the annotation resource
743             */
744            Resource resource = annot.getResource();
745            if (resource != null) {
746                Node resourceNode = getOrCreateUriNode(resource.getUri(), NodeTypes.RESOURCE);
747                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, resourceNode);
748                getOrCreateRelation(targetNode, RelationTypes.PART_OF, resourceNode);
749            }
750
751            /*
752             * The creator of this annotation.
753             */
754            Actor creator = annot.getCreator();
755            if (creator != null) {
756                Node creatorNode = getOrCreateActorNode(creator);
757                getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode);
758            }
759
760            /*
761             * The creation and update date of this annotation.
762             */
763            String created = annot.getCreated();
764            if (created != null) {
765                annotNode.setProperty("created", created);
766            }
767            String updated = annot.getUpdated();
768            if (updated != null) {
769                annotNode.setProperty("updated", updated);
770            }
771
772            /*
773             * Permissions for this annotation.
774             */
775            setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission());
776            setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission());
777            setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission());
778            setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
779
780            /*
781             * Tags on this annotation.
782             */
783            Set<String> newTags = annot.getTags();
784            // we ignore existing tags if tags == null
785            if (newTags != null) {
786                List<Relationship> oldHasTags = new ArrayList<Relationship>();
787                for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
788                    oldHasTags.add(rel);
789                }
790                // adjust to new tags
791                if (newTags.isEmpty()) {
792                    // remove old tags
793                    if (!oldHasTags.isEmpty()) {
794                        for (Relationship rel : oldHasTags) {
795                            rel.delete();
796                            // TODO: should we delete orphan nodes too?
797                        }
798                    }
799                } else {
800                    if (!oldHasTags.isEmpty()) {
801                        // adjust old tags
802                        for (Relationship rel : oldHasTags) {
803                            String oldTag = (String) rel.getEndNode().getProperty("name", null);
804                            if (newTags.contains(oldTag)) {
805                                // tag exists
806                                newTags.remove(oldTag);
807                            } else {
808                                // tag exists no longer
809                                rel.delete();
810                                // TODO: should we delete orphan nodes too?
811                            }
812                        }
813                    }
814                    if (!newTags.isEmpty()) {
815                        // still tags to add
816                        for (String tag : newTags) {
817                            // create new tag
818                            Node tagNode = getOrCreateTagNode(new Tag(null, null, tag));
819                            getOrCreateRelation(annotNode, RelationTypes.HAS_TAG, tagNode);
820                        }
821                    }
822
823                }
824            }
825            tx.success();
826        }
827
828        // re-read and return annotation
829        Annotation storedAnnot = createAnnotationFromNode(annotNode);
830        return storedAnnot;
831    }
832
833    /**
834     * Deletes the annotation with the given id.
835     *
836     * @param id
837     */
838    public void deleteAnnotationById(String id) {
839        try (Transaction tx = graphDb.beginTx()) {
840            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
841            if (annotNode != null) {
842                // delete related objects
843                for (Relationship rel : annotNode.getRelationships()) {
844                    // delete relation and the related node if it has no other
845                    // relations and is not permanent
846                    Node other = rel.getOtherNode(annotNode);
847                    rel.delete();
848                    if (!(other.hasRelationship() || permanentNodeTypes.contains(other.getProperty("TYPE", null)))) {
849                        deleteNode(other);
850                    }
851                }
852                if (!annotNode.hasRelationship()) {
853                    deleteNode(annotNode);
854                } else {
855                    logger.severe("deleteById: unable to delete: Node still has relations.");
856                }
857            }
858            tx.success();
859        }
860    }
861
862    /**
863     * Returns all annotations with the given uri and/or user.
864     *
865     * @param uri
866     * @param userUri
867     * @return
868     */
869    public List<Annotation> searchAnnotationByUriUser(String targetUri, String userUri) {
870        List<Annotation> annotations = new ArrayList<Annotation>();
871        if (targetUri != null) {
872            // there should be only one
873            Node target = getNodeFromIndex("uri", targetUri, NodeTypes.TARGET);
874            if (target != null) {
875                try (Transaction tx = graphDb.beginTx()) {
876                    Iterable<Relationship> relations = target.getRelationships(RelationTypes.ANNOTATES);
877                    for (Relationship relation : relations) {
878                        Node ann = relation.getStartNode();
879                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
880                            Annotation annot = createAnnotationFromNode(ann);
881                            annotations.add(annot);
882                        } else {
883                            logger.severe("ANNOTATES relation does not start with ANNOTATION: " + ann);
884                        }
885                    }
886                    tx.success();
887                }
888            }
889        }
890        if (userUri != null) {
891            // there should be only one
892            Node person = getPersonNodeByUri(userUri);
893            if (person != null) {
894                try (Transaction tx = graphDb.beginTx()) {
895                    Iterable<Relationship> relations = person.getRelationships(RelationTypes.CREATED);
896                    for (Relationship relation : relations) {
897                        Node ann = relation.getEndNode();
898                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
899                            Annotation annot = createAnnotationFromNode(ann);
900                            annotations.add(annot);
901                        } else {
902                            logger.severe("CREATED relation does not end with ANNOTATION: " + ann);
903                        }
904                    }
905                    tx.success();
906                }
907            }
908        }
909        // TODO: if both uri and user are given we should intersect
910
911        return annotations;
912    }
913
914    /**
915     * Returns Relationship of type from Node start to Node end. Creates one if
916     * it doesn't exist.
917     *
918     * @param start
919     * @param type
920     * @param end
921     * @return
922     */
923    protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
924        if (start == null || end == null)
925            return null;
926        if (start.hasRelationship()) {
927            // there are relations
928            try (Transaction tx = graphDb.beginTx()) {
929                Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING);
930                for (Relationship rel : rels) {
931                    if (rel.getEndNode().equals(end)) {
932                        // relation exists
933                        tx.success();
934                        return rel;
935                    }
936                }
937                tx.success();
938            }
939        }
940        // create new one
941        Relationship rel;
942        try (Transaction tx = graphDb.beginTx()) {
943            rel = start.createRelationshipTo(end, type);
944            tx.success();
945        }
946        return rel;
947    }
948
949    protected Node getOrCreateAnnotationNode(String id) {
950        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
951        Node annotation;
952        try (Transaction tx = graphDb.beginTx()) {
953            IndexHits<Node> annotations = idx.get("id", id);
954            annotation = annotations.getSingle();
955            if (annotation == null) {
956                // does not exist yet
957                annotation = graphDb.createNode();
958                annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name());
959                annotation.setProperty("id", id);
960                idx.add(annotation, "id", id);
961            }
962            tx.success();
963        }
964        return annotation;
965    }
966
967    protected Node getOrCreateUriNode(String uri, NodeTypes type) {
968        Index<Node> idx = getNodeIndex(type);
969        Node target;
970        try (Transaction tx = graphDb.beginTx()) {
971            IndexHits<Node> targets = idx.get("uri", uri);
972            target = targets.getSingle();
973            if (target == null) {
974                // does not exist yet
975                target = graphDb.createNode();
976                target.setProperty("TYPE", type.name());
977                target.setProperty("uri", uri);
978                idx.add(target, "uri", uri);
979            }
980            tx.success();
981        }
982        return target;
983    }
984
985    protected Node getActorNode(Actor actor) {
986        // Person/Group is identified by URI or id
987        String uri = actor.getUriString();
988        Index<Node> idx;
989        Node person;
990        if (actor.isGroup()) {
991            idx = getNodeIndex(NodeTypes.GROUP);
992        } else {
993            idx = getNodeIndex(NodeTypes.PERSON);
994        }
995        try (Transaction tx = graphDb.beginTx()) {
996            IndexHits<Node> persons = idx.get("uri", uri);
997            person = persons.getSingle();
998            tx.success();
999        }
1000        return person;
1001    }
1002
1003    protected Node getOrCreateActorNode(Actor actor) {
1004        // Person/Group is identified by URI or id
1005        String uri = actor.getUriString();
1006        String name = actor.getName();
1007        String id = actor.getId();
1008        Node person;
1009        try (Transaction tx = graphDb.beginTx()) {
1010            Index<Node> idx;
1011            if (actor.isGroup()) {
1012                idx = getNodeIndex(NodeTypes.GROUP);
1013            } else {
1014                idx = getNodeIndex(NodeTypes.PERSON);
1015            }
1016            IndexHits<Node> persons = idx.get("uri", uri);
1017            person = persons.getSingle();
1018            if (person == null) {
1019                // does not exist yet
1020                person = graphDb.createNode();
1021                if (actor.isGroup()) {
1022                    person.setProperty("TYPE", NodeTypes.GROUP.name());
1023                } else {
1024                    person.setProperty("TYPE", NodeTypes.PERSON.name());
1025                }
1026                person.setProperty("uri", uri);
1027                idx.add(person, "uri", uri);
1028                if (name != null) {
1029                    person.setProperty("name", name);
1030                }
1031                if (id != null) {
1032                    person.setProperty("id", id);
1033                }
1034            }
1035            tx.success();
1036        }
1037        return person;
1038    }
1039
1040    protected Node getOrCreateTagNode(Tag inTag) {
1041        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
1042        String tagname = inTag.getName();
1043        Node tag;
1044        try (Transaction tx = graphDb.beginTx()) {
1045            IndexHits<Node> tags = idx.get("name", tagname);
1046            tag = tags.getSingle();
1047            if (tag == null) {
1048                // does not exist yet
1049                tag = graphDb.createNode();
1050                tag.setProperty("TYPE", NodeTypes.TAG.name());
1051                tag.setProperty("name", tagname);
1052                idx.add(tag, "name", tagname);
1053
1054                tag.setProperty("id", inTag.getId());
1055                tag.setProperty("uri", inTag.getUri());
1056                idx.add(tag, "uri", inTag.getUri());
1057            }
1058            tx.success();
1059        }
1060        return tag;
1061    }
1062
1063    /**
1064     * Create or update permissions relations for an annotation.
1065     *
1066     * @param annotNode
1067     * @param type
1068     * @param annot
1069     */
1070    protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) {
1071        Node newActorNode = null;
1072        if (actor != null) {
1073            newActorNode = getOrCreateActorNode(actor);
1074        }
1075        Relationship rel = getRelation(annotNode, type, null);
1076        if (rel != null) {
1077            // relation exists
1078            try (Transaction tx = graphDb.beginTx()) {
1079                Node oldActorNode = rel.getEndNode();
1080                if (!oldActorNode.equals(newActorNode)) {
1081                    // new admin is different
1082                    rel.delete();
1083                    tx.success();
1084                }
1085                if (newActorNode != null) {
1086                    rel = getOrCreateRelation(annotNode, type, newActorNode);
1087                }
1088                tx.success();
1089            }
1090        } else {
1091            // no relation yet
1092            if (newActorNode != null) {
1093                rel = getOrCreateRelation(annotNode, type, newActorNode);
1094            }
1095        }
1096    }
1097
1098    /**
1099     * Unindexes and deletes given Node if it has no relations.
1100     *
1101     * @param node
1102     */
1103    protected void deleteNode(Node node) {
1104        try (Transaction tx = graphDb.beginTx()) {
1105            if (node.hasRelationship()) {
1106                logger.severe("deleteNode: unable to delete: Node still has relations.");
1107            } else {
1108                String ts = (String) node.getProperty("TYPE", null);
1109                try {
1110                    NodeTypes type = NodeTypes.valueOf(ts);
1111                    getNodeIndex(type).remove(node);
1112                } catch (Exception e) {
1113                    logger.severe("deleteNode: unable to get TYPE of node: " + node);
1114                }
1115                node.delete();
1116            }
1117            tx.success();
1118        }
1119    }
1120
1121    /**
1122     * returns the (first) Relationship of RelationTypes type from Node start.
1123     *
1124     * @param start
1125     * @param type
1126     * @param direction
1127     * @return
1128     */
1129    protected Relationship getRelation(Node start, RelationTypes type, Direction direction) {
1130        Iterable<Relationship> rels;
1131        try (Transaction tx = graphDb.beginTx()) {
1132            if (direction == null) {
1133                // ignore direction
1134                rels = start.getRelationships(type);
1135            } else {
1136                rels = start.getRelationships(type, direction);
1137            }
1138            tx.success();
1139            for (Relationship rel : rels) {
1140                // just the first one
1141                return rel;
1142            }
1143        }
1144        return null;
1145    }
1146
1147    /**
1148     * Erzeuge eine urn aus der aktuellen Zeit in millis
1149     *
1150     * @return
1151     */
1152    private String createRessourceURI(String prefix) {
1153        Calendar cal = Calendar.getInstance();
1154        long time = cal.getTimeInMillis();
1155        return String.format("%s%s%s", ANNOTATION_URI_PREFIX, prefix, time);
1156    }
1157
1158    public List<Annotation> getAnnotationsByTag(String tagUri) {
1159        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1160        Node tag = getTagNodeByUri(tagUri);
1161        if (tag != null) {
1162            try (Transaction tx = graphDb.beginTx()) {
1163                Iterable<Relationship> rels = tag.getRelationships(Direction.INCOMING, RelationTypes.HAS_TAG);
1164                for (Relationship rel : rels) {
1165                    Node node = rel.getStartNode();
1166                    ret.add(createAnnotationFromNode(node));
1167                }
1168                tx.success();
1169            }
1170        }
1171        return ret;
1172    }
1173
1174    public List<Annotation> getAnnotationsByResource(String resourceUri) {
1175        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1176        Node res = getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE);
1177        if (res != null) {
1178            try (Transaction tx = graphDb.beginTx()) {
1179                Iterable<Relationship> rels = res.getRelationships(Direction.INCOMING, RelationTypes.ANNOTATES);
1180                for (Relationship rel : rels) {
1181                    Node an = rel.getStartNode();
1182                    Node rn = rel.getEndNode();
1183                    if (rn.getProperty("TYPE", "").equals("RESOURCE")) {
1184                        logger.severe("getAnnotationsByResource got ANNOTATES != RESOURCE");
1185                    }
1186                    ret.add(createAnnotationFromNode(an));
1187                }
1188                tx.success();
1189            }
1190        }
1191        return ret;
1192    }
1193
1194}
Note: See TracBrowser for help on using the repository browser.