/** * */ package de.mpiwg.itgroup.annotations.neo4j; /* * #%L * AnnotationManager * %% * Copyright (C) 2012 - 2014 MPIWG Berlin * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Lesser Public License for more details. * * You should have received a copy of the GNU General Lesser Public * License along with this program. If not, see * . * #L% * Author: Robert Casties (casties@mpiwg-berlin.mpg.de) */ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexHits; import de.mpiwg.itgroup.annotations.Actor; import de.mpiwg.itgroup.annotations.Annotation; import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes; import de.mpiwg.itgroup.annotations.Group; import de.mpiwg.itgroup.annotations.Person; import de.mpiwg.itgroup.annotations.Resource; import de.mpiwg.itgroup.annotations.Tag; import de.mpiwg.itgroup.annotations.Target; import de.mpiwg.itgroup.annotations.Uri; /** * Neo4J based Annotation store. * * @author casties * */ public class AnnotationStore { protected static Logger logger = Logger.getLogger(AnnotationStore.class); protected GraphDatabaseService graphDb; public static enum NodeTypes { ANNOTATION, PERSON, TARGET, GROUP, TAG, RESOURCE } // types of nodes that should not be automatically deleted. public Set permanentNodeTypes = new HashSet(Arrays.asList("PERSON", "GROUP", "TAG")); protected List> nodeIndexes; public static enum RelationTypes implements RelationshipType { ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF, HAS_TAG, PART_OF } public static String ANNOTATION_URI_PREFIX = ""; public AnnotationStore(GraphDatabaseService graphDb) { super(); this.graphDb = graphDb; nodeIndexes = new ArrayList>(5); // List.set(enum.ordinal(), val) seems not to work. try (Transaction tx = graphDb.beginTx()) { nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations")); nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons")); nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets")); nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups")); nodeIndexes.add(NodeTypes.TAG.ordinal(), graphDb.index().forNodes("tags")); nodeIndexes.add(NodeTypes.RESOURCE.ordinal(), graphDb.index().forNodes("resources")); tx.success(); } } protected Index getNodeIndex(NodeTypes type) { return nodeIndexes.get(type.ordinal()); } /** * @param userUri * @return */ public Node getPersonNodeByUri(String userUri) { return getNodeFromIndex("uri", userUri, NodeTypes.PERSON); } /** * @param tagUri * @return */ public Node getTagNodeByUri(String tagUri) { return getNodeFromIndex("uri", tagUri, NodeTypes.TAG); } /** * @param resourceUri * @return */ public Node getResourceNodeByUri(String resourceUri) { return getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE); } /** * @param targetUri * @return */ public Node getTargetNodeByUri(String targetUri) { return getNodeFromIndex("uri", targetUri, NodeTypes.RESOURCE); } /** * Returns the Node with the given key and value. Key has to be indexed. * * @param key * @param value * @param type * @return */ public Node getNodeFromIndex(String key, String value, NodeTypes type) { if (key == null || value == null) return null; Node node = null; try (Transaction tx = graphDb.beginTx()) { node = getNodeIndex(type).get(key, value).getSingle(); tx.success(); } return node; } /** * Returns list of Actors of given type (Group or Person). Key has to be * indexed. * * @param key * @param query * @param type * @return */ @SuppressWarnings("unchecked") protected List getActors(String key, String query, NodeTypes type) { ArrayList actors = new ArrayList(); Index idx = getNodeIndex(type); if (key == null) { key = "uri"; query = "*"; } try (Transaction tx = graphDb.beginTx()) { IndexHits actorNodes = idx.query(key, query); for (Node actorNode : actorNodes) { Actor actor = createActorFromNode(actorNode); actors.add((T) actor); } tx.success(); } return actors; } /** * Returns list of groups. Key has to be indexed. * * @param key * @param query * @return */ public List getGroups(String key, String query) { List groups = getActors(key, query, NodeTypes.GROUP); return groups; } /** * Returns list of Persons. Key has to be indexed. * * @param key * @param query * @return */ public List getPersons(String key, String query) { List persons = getActors(key, query, NodeTypes.PERSON); return persons; } /** * Returns list of uri-like objects of given type (Target or Resource). Key * has to be indexed. * * @param key * @param query * @param type * @return */ @SuppressWarnings("unchecked") protected List getUris(String key, String query, NodeTypes type) { ArrayList uris = new ArrayList(); Index idx = getNodeIndex(type); if (key == null) { key = "uri"; query = "*"; } try (Transaction tx = graphDb.beginTx()) { IndexHits actorNodes = idx.query(key, query); for (Node actorNode : actorNodes) { Uri uri = createUriFromNode(actorNode); uris.add((T) uri); } tx.success(); } return uris; } /** * Returns list of Targets. Key has to be indexed. * * @param key * @param query * @return */ public List getTargets(String key, String query) { List targets = getUris(key, query, NodeTypes.TARGET); return targets; } /** * Returns list of Resources. Key has to be indexed. * * @param key * @param query * @return */ public List getResources(String key, String query) { List targets = getUris(key, query, NodeTypes.RESOURCE); return targets; } /** * Returns List of Annotations. Key has to be indexed. * * @param key * @param query * @return */ public List getAnnotations(String key, String query) { ArrayList annotations = new ArrayList(); Index idx = getNodeIndex(NodeTypes.ANNOTATION); if (key == null) { key = "id"; query = "*"; } try (Transaction tx = graphDb.beginTx()) { IndexHits annotNodes = idx.query(key, query); for (Node annotNode : annotNodes) { Annotation annotation = createAnnotationFromNode(annotNode); annotations.add(annotation); } tx.success(); } return annotations; } /** * Returns List of Tags. Key has to be indexed. * * @param key * @param query * @return */ public List getTags(String key, String query) { ArrayList tags = new ArrayList(); Index idx = getNodeIndex(NodeTypes.TAG); if (key == null) { key = "uri"; query = "*"; } try (Transaction tx = graphDb.beginTx()) { IndexHits groupNodes = idx.query(key, query); for (Node groupNode : groupNodes) { Tag tag = createTagFromNode(groupNode); tags.add(tag); } tx.success(); } return tags; } /** * Returns List of Groups the person is member of. * * @param person * @return */ public List getGroupsForPersonNode(Node person) { ArrayList groups = new ArrayList(); Iterable rels = person.getRelationships(RelationTypes.MEMBER_OF); for (Relationship rel : rels) { Node groupNode = rel.getEndNode(); Actor group = createActorFromNode(groupNode); // make sure we're getting a group if (!(group instanceof Group)) { logger.error("target of MEMBER_OF is not GROUP! rel=" + rel); continue; } groups.add((Group) group); } return groups; } /** * Returns if person with uri is in Group group. * * @param person * @param group * @return */ public boolean isPersonInGroup(Person person, Group group) { Node pn = getPersonNodeByUri(person.getUriString()); if (pn == null) return false; // optimized version of getGroupsForPersonNode try (Transaction tx = graphDb.beginTx()) { Iterable rels = pn.getRelationships(RelationTypes.MEMBER_OF); for (Relationship rel : rels) { Node gn = rel.getEndNode(); if (gn.getProperty("uri", "").equals(group.getUriString()) || gn.getProperty("id", "").equals(group.getId())) { tx.success(); return true; } } tx.success(); } return false; } /** * Returns the members of the group. * * @param group * @return */ public List getMembersOfGroup(Group group) { ArrayList members = new ArrayList(); Node gn = getActorNode(group); Iterable rels = gn.getRelationships(RelationTypes.MEMBER_OF); for (Relationship rel : rels) { Node memberNode = rel.getStartNode(); Actor member = createActorFromNode(memberNode); // make sure we're getting a group if (!(member instanceof Person)) { logger.error("source of MEMBER_OF is not PERSON! rel=" + rel); continue; } members.add((Person) member); } return members; } /** * Add Person newMember to Group group. * * @param group * @param member */ public Person addGroupMember(Group group, Person member) { Node gn = getActorNode(group); Node pn = getActorNode(member); Person addedMember = null; if (gn != null && pn != null) { getOrCreateRelation(pn, RelationTypes.MEMBER_OF, gn); addedMember = member; } return addedMember; } /** * Delete Person oldMember from Group group. * * @param group * @param member */ public void deleteGroupMember(Group group, Person member) { Node gn = getActorNode(group); Node pn = getActorNode(member); Iterable rels = gn.getRelationships(RelationTypes.MEMBER_OF); for (Relationship rel : rels) { Node mn = rel.getStartNode(); if (mn.equals(pn)) { try (Transaction tx = graphDb.beginTx()) { rel.delete(); tx.success(); } // there should be only one break; } } } /** * Returns the stored Actor matching the given one. * * @param actor * @return */ public Actor getActor(Actor actor) { Node actorNode = getActorNode(actor); Actor storedActor = createActorFromNode(actorNode); return storedActor; } /** * Stores an Actor (Person or Group). Creates a new actor Node or update an * existing one. * * @param actor * @return */ public Actor storeActor(Actor actor) { Node actorNode = getOrCreateActorNode(actor); try (Transaction tx = graphDb.beginTx()) { // id String id = actor.getId(); if (id != null) { actorNode.setProperty("id", id); } // name String name = actor.getName(); if (name != null) { actorNode.setProperty("name", name); } // uri String uri = actor.getUri(); if (uri != null) { actorNode.setProperty("uri", uri); } tx.success(); } Actor storedActor = createActorFromNode(actorNode); return storedActor; } /** * Deletes the given Actor. * * @param actor */ public void deleteActor(Actor actor) { String uri = actor.getUriString(); Index idx; if (actor.isGroup()) { idx = getNodeIndex(NodeTypes.GROUP); } else { idx = getNodeIndex(NodeTypes.PERSON); } Node actorNode = idx.get("uri", uri).getSingle(); if (actorNode != null) { // delete relations try (Transaction tx = graphDb.beginTx()) { for (Relationship rel : actorNode.getRelationships()) { rel.delete(); } if (!actorNode.hasRelationship()) { // this shouldn't happen deleteNode(actorNode); } else { logger.error("deleteActor: unable to delete: Node still has relations."); } tx.success(); } } } /** * Returns the Annotation with the given id. * * @param id * @return */ public Annotation getAnnotationById(String id) { Annotation annot = null; try (Transaction tx = graphDb.beginTx()) { Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle(); annot = createAnnotationFromNode(annotNode); tx.success(); } return annot; } /** * Returns an Annotation object from an annotation-Node. * * @param annotNode * @return */ public Annotation createAnnotationFromNode(Node annotNode) { Annotation annot = new Annotation(); try (Transaction tx = graphDb.beginTx()) { annot.setUri((String) annotNode.getProperty("id", null)); annot.setBodyText((String) annotNode.getProperty("bodyText", null)); annot.setBodyUri((String) annotNode.getProperty("bodyUri", null)); /* * get annotation target and resource from relation */ for (Relationship rel : annotNode.getRelationships(RelationTypes.ANNOTATES)) { Node target = rel.getEndNode(); String type = (String) target.getProperty("TYPE"); if (type.equals("TARGET")) { annot.setTarget(new Target((String) target.getProperty("uri", null))); } else if (type.equals("RESOURCE")) { annot.setResource(new Resource((String) target.getProperty("uri", null))); } } if (annot.getTarget() == null) { logger.warn("annotation " + annotNode + " has no target node!"); } // get fragment from attribute annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null)); String ft = (String) annotNode.getProperty("fragmentType", null); if (ft != null) { annot.setFragmentType(FragmentTypes.valueOf(ft)); } /* * get creator from relation */ Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null); if (creatorRel != null) { Node creatorNode = creatorRel.getStartNode(); Actor creator = createActorFromNode(creatorNode); annot.setCreator(creator); } else { logger.warn("annotation " + annotNode + " has no creator node!"); } /* * get creation date */ annot.setCreated((String) annotNode.getProperty("created", null)); /* * get permissions */ Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null); if (adminRel != null) { Node adminNode = adminRel.getEndNode(); Actor admin = createActorFromNode(adminNode); annot.setAdminPermission(admin); } Relationship deleteRel = getRelation(annotNode, RelationTypes.PERMITS_DELETE, null); if (deleteRel != null) { Node deleteNode = deleteRel.getEndNode(); Actor delete = createActorFromNode(deleteNode); annot.setDeletePermission(delete); } Relationship updateRel = getRelation(annotNode, RelationTypes.PERMITS_UPDATE, null); if (updateRel != null) { Node updateNode = updateRel.getEndNode(); Actor update = createActorFromNode(updateNode); annot.setUpdatePermission(update); } Relationship readRel = getRelation(annotNode, RelationTypes.PERMITS_READ, null); if (readRel != null) { Node readNode = readRel.getEndNode(); Actor read = createActorFromNode(readNode); annot.setReadPermission(read); } /* * get tags */ Set tags = new HashSet(); for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) { String tag = (String) rel.getEndNode().getProperty("name", null); if (tag != null) { tags.add(tag); } } annot.setTags(tags); tx.success(); } return annot; } /** * Returns an Actor object from a node. * * @param actorNode * @return */ protected Actor createActorFromNode(Node actorNode) { if (actorNode == null) return null; try (Transaction tx = graphDb.beginTx()) { String id = (String) actorNode.getProperty("id", null); String uri = (String) actorNode.getProperty("uri", null); String name = (String) actorNode.getProperty("name", null); String type = (String) actorNode.getProperty("TYPE", null); tx.success(); if (type != null && type.equals("PERSON")) { return new Person(id, uri, name); } else if (type != null && type.equals("GROUP")) { return new Group(id, uri, name); } } return null; } public Tag createTagFromNode(Node tagNode) { if (tagNode == null) return null; String id; String uri; String name; try (Transaction tx = graphDb.beginTx()) { name = (String) tagNode.getProperty("name", null); uri = (String) tagNode.getProperty("uri", null); id = (String) tagNode.getProperty("id", null); tx.success(); } return new Tag(id, uri, name); } /** * @param resourceNode * @return */ public Resource createResourceFromNode(Node resourceNode) { return (Resource) createUriFromNode(resourceNode); } /** * @param targetNode * @return */ public Target createTargetFromNode(Node targetNode) { return (Target) createUriFromNode(targetNode); } protected Uri createUriFromNode(Node uriNode) { if (uriNode == null) return null; try (Transaction tx = graphDb.beginTx()) { String uri = (String) uriNode.getProperty("uri", null); String type = (String) uriNode.getProperty("TYPE", null); tx.success(); if (type != null && type.equals("TARGET")) { return new Target(uri); } else if (type != null && type.equals("RESOURCE")) { return new Resource(uri); } } return null; } /** * Store a new annotation in the store or update an existing one. Returns * the stored annotation. * * @param annot * @return */ public Annotation storeAnnotation(Annotation annot) { Node annotNode = null; try (Transaction tx = graphDb.beginTx()) { /* * create or get the annotation */ String id = annot.getUri(); if (id == null) { id = createRessourceURI("annot:"); } annotNode = getOrCreateAnnotationNode(id); /* * the annotation body */ String bodyText = annot.getBodyText(); if (bodyText != null) { annotNode.setProperty("bodyText", bodyText); } String bodyUri = annot.getBodyUri(); if (bodyUri != null) { annotNode.setProperty("bodyUri", bodyUri); } /* * the annotation target */ Target target = annot.getTarget(); Node targetNode = null; if (target != null) { targetNode = getOrCreateUriNode(target.getUri(), NodeTypes.TARGET); getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, targetNode); } /* * The fragment part of the annotation target. */ String targetFragment = annot.getTargetFragment(); FragmentTypes fragmentType = annot.getFragmentType(); if (targetFragment != null) { annotNode.setProperty("targetFragment", targetFragment); annotNode.setProperty("fragmentType", fragmentType.name()); } /* * the annotation resource */ Resource resource = annot.getResource(); if (resource != null) { Node resourceNode = getOrCreateUriNode(resource.getUri(), NodeTypes.RESOURCE); getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, resourceNode); getOrCreateRelation(targetNode, RelationTypes.PART_OF, resourceNode); } /* * The creator of this annotation. */ Actor creator = annot.getCreator(); if (creator != null) { Node creatorNode = getOrCreateActorNode(creator); getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode); } /* * The creation date of this annotation. */ String created = annot.getCreated(); if (created != null) { annotNode.setProperty("created", created); } /* * Permissions for this annotation. */ setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission()); setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission()); setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission()); setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission()); /* * Tags on this annotation. */ Set newTags = annot.getTags(); // we ignore existing tags if tags == null if (newTags != null) { List oldHasTags = new ArrayList(); for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) { oldHasTags.add(rel); } // adjust to new tags if (newTags.isEmpty()) { // remove old tags if (!oldHasTags.isEmpty()) { for (Relationship rel : oldHasTags) { rel.delete(); // TODO: should we delete orphan nodes too? } } } else { if (!oldHasTags.isEmpty()) { // adjust old tags for (Relationship rel : oldHasTags) { String oldTag = (String) rel.getEndNode().getProperty("name", null); if (newTags.contains(oldTag)) { // tag exists newTags.remove(oldTag); } else { // tag exists no longer rel.delete(); // TODO: should we delete orphan nodes too? } } } if (!newTags.isEmpty()) { // still tags to add for (String tag : newTags) { // create new tag Node tagNode = getOrCreateTagNode(new Tag(null, null, tag)); getOrCreateRelation(annotNode, RelationTypes.HAS_TAG, tagNode); } } } } tx.success(); } // re-read and return annotation Annotation storedAnnot = createAnnotationFromNode(annotNode); return storedAnnot; } /** * Deletes the annotation with the given id. * * @param id */ public void deleteAnnotationById(String id) { try (Transaction tx = graphDb.beginTx()) { Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle(); if (annotNode != null) { // delete related objects for (Relationship rel : annotNode.getRelationships()) { // delete relation and the related node if it has no other // relations and is not permanent Node other = rel.getOtherNode(annotNode); rel.delete(); if (!(other.hasRelationship() || permanentNodeTypes.contains(other.getProperty("TYPE", null)))) { deleteNode(other); } } if (!annotNode.hasRelationship()) { deleteNode(annotNode); } else { logger.error("deleteById: unable to delete: Node still has relations."); } } tx.success(); } } /** * Returns all annotations with the given uri and/or user. * * @param uri * @param userUri * @return */ public List searchAnnotationByUriUser(String targetUri, String userUri) { List annotations = new ArrayList(); if (targetUri != null) { // there should be only one Node target = getNodeFromIndex("uri", targetUri, NodeTypes.TARGET); if (target != null) { try (Transaction tx = graphDb.beginTx()) { Iterable relations = target.getRelationships(RelationTypes.ANNOTATES); for (Relationship relation : relations) { Node ann = relation.getStartNode(); if (ann.getProperty("TYPE", "").equals("ANNOTATION")) { Annotation annot = createAnnotationFromNode(ann); annotations.add(annot); } else { logger.error("ANNOTATES relation does not start with ANNOTATION: " + ann); } } tx.success(); } } } if (userUri != null) { // there should be only one Node person = getPersonNodeByUri(userUri); if (person != null) { try (Transaction tx = graphDb.beginTx()) { Iterable relations = person.getRelationships(RelationTypes.CREATED); for (Relationship relation : relations) { Node ann = relation.getEndNode(); if (ann.getProperty("TYPE", "").equals("ANNOTATION")) { Annotation annot = createAnnotationFromNode(ann); annotations.add(annot); } else { logger.error("CREATED relation does not end with ANNOTATION: " + ann); } } tx.success(); } } } // TODO: if both uri and user are given we should intersect return annotations; } /** * Returns Relationship of type from Node start to Node end. Creates one if * it doesn't exist. * * @param start * @param type * @param end * @return */ protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) { if (start == null || end == null) return null; if (start.hasRelationship()) { // there are relations try (Transaction tx = graphDb.beginTx()) { Iterable rels = start.getRelationships(type, Direction.OUTGOING); for (Relationship rel : rels) { if (rel.getEndNode().equals(end)) { // relation exists tx.success(); return rel; } } tx.success(); } } // create new one Relationship rel; try (Transaction tx = graphDb.beginTx()) { rel = start.createRelationshipTo(end, type); tx.success(); } return rel; } protected Node getOrCreateAnnotationNode(String id) { Index idx = getNodeIndex(NodeTypes.ANNOTATION); Node annotation; try (Transaction tx = graphDb.beginTx()) { IndexHits annotations = idx.get("id", id); annotation = annotations.getSingle(); if (annotation == null) { // does not exist yet annotation = graphDb.createNode(); annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name()); annotation.setProperty("id", id); idx.add(annotation, "id", id); } tx.success(); } return annotation; } protected Node getOrCreateUriNode(String uri, NodeTypes type) { Index idx = getNodeIndex(type); Node target; try (Transaction tx = graphDb.beginTx()) { IndexHits targets = idx.get("uri", uri); target = targets.getSingle(); if (target == null) { // does not exist yet target = graphDb.createNode(); target.setProperty("TYPE", type.name()); target.setProperty("uri", uri); idx.add(target, "uri", uri); } tx.success(); } return target; } protected Node getActorNode(Actor actor) { // Person/Group is identified by URI or id String uri = actor.getUriString(); Index idx; Node person; if (actor.isGroup()) { idx = getNodeIndex(NodeTypes.GROUP); } else { idx = getNodeIndex(NodeTypes.PERSON); } try (Transaction tx = graphDb.beginTx()) { IndexHits persons = idx.get("uri", uri); person = persons.getSingle(); tx.success(); } return person; } protected Node getOrCreateActorNode(Actor actor) { // Person/Group is identified by URI or id String uri = actor.getUriString(); String name = actor.getName(); String id = actor.getId(); Node person; try (Transaction tx = graphDb.beginTx()) { Index idx; if (actor.isGroup()) { idx = getNodeIndex(NodeTypes.GROUP); } else { idx = getNodeIndex(NodeTypes.PERSON); } IndexHits persons = idx.get("uri", uri); person = persons.getSingle(); if (person == null) { // does not exist yet person = graphDb.createNode(); if (actor.isGroup()) { person.setProperty("TYPE", NodeTypes.GROUP.name()); } else { person.setProperty("TYPE", NodeTypes.PERSON.name()); } person.setProperty("uri", uri); idx.add(person, "uri", uri); if (name != null) { person.setProperty("name", name); } if (id != null) { person.setProperty("id", id); } } tx.success(); } return person; } protected Node getOrCreateTagNode(Tag inTag) { Index idx = getNodeIndex(NodeTypes.TAG); String tagname = inTag.getName(); Node tag; try (Transaction tx = graphDb.beginTx()) { IndexHits tags = idx.get("name", tagname); tag = tags.getSingle(); if (tag == null) { // does not exist yet tag = graphDb.createNode(); tag.setProperty("TYPE", NodeTypes.TAG.name()); tag.setProperty("name", tagname); idx.add(tag, "name", tagname); tag.setProperty("id", inTag.getId()); tag.setProperty("uri", inTag.getUri()); idx.add(tag, "uri", inTag.getUri()); } tx.success(); } return tag; } /** * Create or update permissions relations for an annotation. * * @param annotNode * @param type * @param annot */ protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) { Node newActorNode = null; if (actor != null) { newActorNode = getOrCreateActorNode(actor); } Relationship rel = getRelation(annotNode, type, null); if (rel != null) { // relation exists try (Transaction tx = graphDb.beginTx()) { Node oldActorNode = rel.getEndNode(); if (!oldActorNode.equals(newActorNode)) { // new admin is different rel.delete(); tx.success(); } if (newActorNode != null) { rel = getOrCreateRelation(annotNode, type, newActorNode); } tx.success(); } } else { // no relation yet if (newActorNode != null) { rel = getOrCreateRelation(annotNode, type, newActorNode); } } } /** * Unindexes and deletes given Node if it has no relations. * * @param node */ protected void deleteNode(Node node) { try (Transaction tx = graphDb.beginTx()) { if (node.hasRelationship()) { logger.error("deleteNode: unable to delete: Node still has relations."); } else { String ts = (String) node.getProperty("TYPE", null); try { NodeTypes type = NodeTypes.valueOf(ts); getNodeIndex(type).remove(node); } catch (Exception e) { logger.error("deleteNode: unable to get TYPE of node: " + node); } node.delete(); } tx.success(); } } /** * returns the (first) Relationship of RelationTypes type from Node start. * * @param start * @param type * @param direction * @return */ protected Relationship getRelation(Node start, RelationTypes type, Direction direction) { Iterable rels; try (Transaction tx = graphDb.beginTx()) { if (direction == null) { // ignore direction rels = start.getRelationships(type); } else { rels = start.getRelationships(type, direction); } tx.success(); for (Relationship rel : rels) { // just the first one return rel; } } return null; } /** * Erzeuge eine urn aus der aktuellen Zeit in millis * * @return */ private String createRessourceURI(String prefix) { Calendar cal = Calendar.getInstance(); long time = cal.getTimeInMillis(); return String.format("%s%s%s", ANNOTATION_URI_PREFIX, prefix, time); } public List getAnnotationsByTag(String tagUri) { ArrayList ret = new ArrayList(); Node tag = getTagNodeByUri(tagUri); if (tag != null) { try (Transaction tx = graphDb.beginTx()) { Iterable rels = tag.getRelationships(Direction.INCOMING, RelationTypes.HAS_TAG); for (Relationship rel : rels) { Node node = rel.getStartNode(); ret.add(createAnnotationFromNode(node)); } tx.success(); } } return ret; } public List getAnnotationsByResource(String resourceUri) { ArrayList ret = new ArrayList(); Node res = getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE); if (res != null) { try (Transaction tx = graphDb.beginTx()) { Iterable rels = res.getRelationships(Direction.INCOMING, RelationTypes.ANNOTATES); for (Relationship rel : rels) { Node an = rel.getStartNode(); Node rn = rel.getEndNode(); if (rn.getProperty("TYPE", "").equals("RESOURCE")) { logger.error("getAnnotationsByResource got ANNOTATES != RESOURCE"); } ret.add(createAnnotationFromNode(an)); } tx.success(); } } return ret; } }