/** * */ package de.mpiwg.itgroup.annotations.neo4j; import java.util.ArrayList; import java.util.Calendar; import java.util.List; 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.Annotation; import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes; /** * @author casties * */ public class AnnotationStore { protected static Logger logger = Logger.getLogger(AnnotationStore.class); protected GraphDatabaseService graphDb; public static enum RelationTypes implements RelationshipType { ANNOTATES, CREATED } public static String ANNOTATION_URI_BASE = "http://entities.mpiwg-berlin.mpg.de/annotations/"; public AnnotationStore(GraphDatabaseService graphDb) { super(); this.graphDb = graphDb; } public Annotation getAnnotationById(String id) { Index idx = graphDb.index().forNodes("annotations"); Node annotNode = idx.get("id", id).getSingle(); Annotation annot = createAnnotation(annotNode); return annot; } /** * Returns an Annotation object from an annotation-Node. * * @param annotNode * @return */ public Annotation createAnnotation(Node annotNode) { Annotation annot = new Annotation(); annot.setUri((String) annotNode.getProperty("id", null)); annot.setBodyText((String) annotNode.getProperty("bodyText", null)); annot.setBodyUri((String) annotNode.getProperty("bodyUri", null)); // get annotation target from relation Iterable targetRels = annotNode.getRelationships(RelationTypes.ANNOTATES); for (Relationship targetRel : targetRels) { Node target = targetRel.getEndNode(); annot.setTargetBaseUri((String) target.getProperty("uri", null)); // just the first one break; } annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null)); String ft = (String) annotNode.getProperty("fragmentType", null); if (ft != null) { annot.setFragmentType(FragmentTypes.valueOf(ft)); } // get creator form relation Iterable creatorRels = annotNode.getRelationships(RelationTypes.CREATED); for (Relationship creatorRel : creatorRels) { Node creator = creatorRel.getStartNode(); annot.setCreatorUri((String) creator.getProperty("uri", null)); annot.setCreatorName((String) creator.getProperty("name", null)); // just the first one break; } annot.setCreated((String) annotNode.getProperty("created", null)); return annot; } /** * 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; Transaction tx = graphDb.beginTx(); try { /* * 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 */ String targetBaseUri = annot.getTargetBaseUri(); if (targetBaseUri != null) { Node target = getOrCreateTargetNode(targetBaseUri); getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, target); } /* * 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 name of the creator of this annotation. */ String creatorName = annot.getCreatorName(); /* * The URI of the creator of this annotation. */ String creatorUri = annot.getCreatorUri(); if (creatorUri != null) { Node creator = getOrCreatePersonNode(creatorUri, creatorName); getOrCreateRelation(creator, RelationTypes.CREATED, annotNode); } /* * The creation date of this annotation. */ String created = annot.getCreated(); if (created != null) { annotNode.setProperty("created", created); } tx.success(); } finally { tx.finish(); } // re-read and return annotation Annotation storedAnnot = createAnnotation(annotNode); return storedAnnot; } /** * Deletes the annotation with the id. * * @param id */ public void deleteById(String id) { // TODO Auto-generated method stub } /** * Returns all annotations with the given uri and/or user. * * @param uri * @param userUri * @param limit * @param offset * @return */ public List searchByUriUser(String targetUri, String userUri, String limit, String offset) { List annotations = new ArrayList(); if (targetUri != null) { Index targetIdx = graphDb.index().forNodes("targets"); // there should be only one Node target = targetIdx.get("uri", targetUri).getSingle(); if (target != null) { Iterable relations = target.getRelationships(RelationTypes.ANNOTATES); for (Relationship relation : relations) { Node ann = relation.getStartNode(); if (ann.getProperty("TYPE", "").equals("ANNOTATION")) { Annotation annot = createAnnotation(ann); annotations.add(annot); } else { logger.error("ANNOTATES relation does not start with ANNOTATION: " + ann); } } } } if (userUri != null) { Index persIdx = graphDb.index().forNodes("persons"); // there should be only one Node person = persIdx.get("uri", userUri).getSingle(); if (person != null) { Iterable relations = person.getRelationships(RelationTypes.CREATED); for (Relationship relation : relations) { Node ann = relation.getEndNode(); if (ann.getProperty("TYPE", "").equals("ANNOTATION")) { Annotation annot = createAnnotation(ann); annotations.add(annot); } else { logger.error("CREATED relation does not end with ANNOTATION: " + ann); } } } } return annotations; } protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) { if (start.hasRelationship()) { // there are relations Iterable rels = start.getRelationships(type, Direction.OUTGOING); for (Relationship rel : rels) { if (rel.getEndNode().equals(end)) { // relation exists return rel; } } } // create new one Relationship rel; Transaction tx = graphDb.beginTx(); try { rel = start.createRelationshipTo(end, type); tx.success(); } finally { tx.finish(); } return rel; } protected Node getOrCreateAnnotationNode(String id) { Index idx = graphDb.index().forNodes("annotations"); IndexHits annotations = idx.get("id", id); Node annotation = annotations.getSingle(); if (annotation == null) { // does not exist yet Transaction tx = graphDb.beginTx(); try { annotation = graphDb.createNode(); annotation.setProperty("TYPE", "ANNOTATION"); annotation.setProperty("id", id); idx.add(annotation, "id", id); tx.success(); } finally { tx.finish(); } } return annotation; } protected Node getOrCreateTargetNode(String uri) { Index idx = graphDb.index().forNodes("targets"); IndexHits targets = idx.get("uri", uri); Node target = targets.getSingle(); if (target == null) { // does not exist yet Transaction tx = graphDb.beginTx(); try { target = graphDb.createNode(); target.setProperty("TYPE", "TARGET"); target.setProperty("uri", uri); idx.add(target, "uri", uri); tx.success(); } finally { tx.finish(); } } return target; } protected Node getOrCreatePersonNode(String uri, String name) { Index idx = graphDb.index().forNodes("persons"); // Person is identified by URI IndexHits persons = idx.get("uri", uri); Node person = persons.getSingle(); if (person == null) { // does not exist yet Transaction tx = graphDb.beginTx(); try { person = graphDb.createNode(); person.setProperty("TYPE", "PERSON"); person.setProperty("uri", uri); idx.add(person, "uri", uri); if (name != null) { person.setProperty("name", name); } tx.success(); } finally { tx.finish(); } } return person; } /** * 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_BASE, prefix, time); } }