source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 101:7268c3ca025b

Last change on this file since 101:7268c3ca025b was 101:7268c3ca025b, checked in by casties, 9 years ago

make admin ui view of all annotations scale better.

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