comparison src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 16:794077e6288c

CLOSED - # 252: Tags for Annotations https://it-dev.mpiwg-berlin.mpg.de/tracs/mpdl-project-software/ticket/252
author casties
date Tue, 04 Sep 2012 20:02:59 +0200
parents 58357a4b86de
children aafa3884b2c4
comparison
equal deleted inserted replaced
15:58357a4b86de 16:794077e6288c
3 */ 3 */
4 package de.mpiwg.itgroup.annotations.neo4j; 4 package de.mpiwg.itgroup.annotations.neo4j;
5 5
6 import java.util.ArrayList; 6 import java.util.ArrayList;
7 import java.util.Calendar; 7 import java.util.Calendar;
8 import java.util.HashSet;
8 import java.util.List; 9 import java.util.List;
10 import java.util.Set;
9 11
10 import org.apache.log4j.Logger; 12 import org.apache.log4j.Logger;
11 import org.neo4j.graphdb.Direction; 13 import org.neo4j.graphdb.Direction;
12 import org.neo4j.graphdb.GraphDatabaseService; 14 import org.neo4j.graphdb.GraphDatabaseService;
13 import org.neo4j.graphdb.Node; 15 import org.neo4j.graphdb.Node;
32 protected static Logger logger = Logger.getLogger(AnnotationStore.class); 34 protected static Logger logger = Logger.getLogger(AnnotationStore.class);
33 35
34 protected GraphDatabaseService graphDb; 36 protected GraphDatabaseService graphDb;
35 37
36 public static enum NodeTypes { 38 public static enum NodeTypes {
37 ANNOTATION, PERSON, TARGET, GROUP 39 ANNOTATION, PERSON, TARGET, GROUP, TAG
38 } 40 }
39 41
40 protected List<Index<Node>> nodeIndexes; 42 protected List<Index<Node>> nodeIndexes;
41 43
42 public static enum RelationTypes implements RelationshipType { 44 public static enum RelationTypes implements RelationshipType {
43 ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF 45 ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF, HAS_TAG
44 } 46 }
45 47
46 public static String ANNOTATION_URI_BASE = "http://entities.mpiwg-berlin.mpg.de/annotations/"; 48 public static String ANNOTATION_URI_BASE = "http://entities.mpiwg-berlin.mpg.de/annotations/";
47 49
48 public AnnotationStore(GraphDatabaseService graphDb) { 50 public AnnotationStore(GraphDatabaseService graphDb) {
49 super(); 51 super();
50 this.graphDb = graphDb; 52 this.graphDb = graphDb;
51 nodeIndexes = new ArrayList<Index<Node>>(3); 53 nodeIndexes = new ArrayList<Index<Node>>(5);
52 // List.set(enum.ordinal(), val) seems not to work. 54 // List.set(enum.ordinal(), val) seems not to work.
53 nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations")); 55 nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations"));
54 nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons")); 56 nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons"));
55 nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets")); 57 nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets"));
56 nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups")); 58 nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups"));
59 nodeIndexes.add(NodeTypes.TAG.ordinal(), graphDb.index().forNodes("tags"));
57 } 60 }
58 61
59 protected Index<Node> getNodeIndex(NodeTypes type) { 62 protected Index<Node> getNodeIndex(NodeTypes type) {
60 return nodeIndexes.get(type.ordinal()); 63 return nodeIndexes.get(type.ordinal());
61 } 64 }
68 if (userUri == null) return null; 71 if (userUri == null) return null;
69 Node person = getNodeIndex(NodeTypes.PERSON).get("uri", userUri).getSingle(); 72 Node person = getNodeIndex(NodeTypes.PERSON).get("uri", userUri).getSingle();
70 return person; 73 return person;
71 } 74 }
72 75
73
74 /** 76 /**
75 * Returns List of Groups the person is member of. 77 * Returns List of Groups the person is member of.
76 * 78 *
77 * @param person 79 * @param person
78 * @return 80 * @return
83 for (Relationship rel : rels) { 85 for (Relationship rel : rels) {
84 Node groupNode = rel.getEndNode(); 86 Node groupNode = rel.getEndNode();
85 Actor group = createActorFromNode(groupNode); 87 Actor group = createActorFromNode(groupNode);
86 // make sure we're getting a group 88 // make sure we're getting a group
87 if (!(group instanceof Group)) { 89 if (!(group instanceof Group)) {
88 logger.error("target of MEMBER_OF is not GROUP! rel="+rel); 90 logger.error("target of MEMBER_OF is not GROUP! rel=" + rel);
89 continue; 91 continue;
90 } 92 }
91 groups.add((Group) group); 93 groups.add((Group) group);
92 } 94 }
93 return groups; 95 return groups;
94 } 96 }
95 97
96 /** 98 /**
97 * Returns if person with uri is in Group group. 99 * Returns if person with uri is in Group group.
98 * 100 *
99 * @param person 101 * @param person
100 * @param group 102 * @param group
101 * @return 103 * @return
102 */ 104 */
103 public boolean isPersonInGroup(Person person, Group group) { 105 public boolean isPersonInGroup(Person person, Group group) {
104 Node pn = getPersonNodeByUri(person.getUriString()); 106 Node pn = getPersonNodeByUri(person.getUriString());
105 if (pn == null) return false; 107 if (pn == null) return false;
106 // optimised version of getGroupsForPersonNode 108 // optimized version of getGroupsForPersonNode
107 Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF); 109 Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF);
108 for (Relationship rel : rels) { 110 for (Relationship rel : rels) {
109 Node gn = rel.getEndNode(); 111 Node gn = rel.getEndNode();
110 if (gn.getProperty("uri", "").equals(group.getUriString()) || gn.getProperty("id", "").equals(group.getId())) { 112 if (gn.getProperty("uri", "").equals(group.getUriString()) || gn.getProperty("id", "").equals(group.getId())) {
111 return true; 113 return true;
112 } 114 }
113 } 115 }
114 return false; 116 return false;
115 } 117 }
116 118
117 /** 119 /**
118 * Returns the Annotation with the given id. 120 * Returns the Annotation with the given id.
119 * 121 *
120 * @param id 122 * @param id
121 * @return 123 * @return
135 public Annotation createAnnotationFromNode(Node annotNode) { 137 public Annotation createAnnotationFromNode(Node annotNode) {
136 Annotation annot = new Annotation(); 138 Annotation annot = new Annotation();
137 annot.setUri((String) annotNode.getProperty("id", null)); 139 annot.setUri((String) annotNode.getProperty("id", null));
138 annot.setBodyText((String) annotNode.getProperty("bodyText", null)); 140 annot.setBodyText((String) annotNode.getProperty("bodyText", null));
139 annot.setBodyUri((String) annotNode.getProperty("bodyUri", null)); 141 annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
140 // get annotation target from relation 142 /*
143 * get annotation target from relation
144 */
141 Relationship targetRel = getRelation(annotNode, RelationTypes.ANNOTATES, null); 145 Relationship targetRel = getRelation(annotNode, RelationTypes.ANNOTATES, null);
142 if (targetRel != null) { 146 if (targetRel != null) {
143 Node target = targetRel.getEndNode(); 147 Node target = targetRel.getEndNode();
144 annot.setTargetBaseUri((String) target.getProperty("uri", null)); 148 annot.setTargetBaseUri((String) target.getProperty("uri", null));
145 } else { 149 } else {
148 annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null)); 152 annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null));
149 String ft = (String) annotNode.getProperty("fragmentType", null); 153 String ft = (String) annotNode.getProperty("fragmentType", null);
150 if (ft != null) { 154 if (ft != null) {
151 annot.setFragmentType(FragmentTypes.valueOf(ft)); 155 annot.setFragmentType(FragmentTypes.valueOf(ft));
152 } 156 }
153 // get creator from relation 157 /*
158 * get creator from relation
159 */
154 Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null); 160 Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null);
155 if (creatorRel != null) { 161 if (creatorRel != null) {
156 Node creatorNode = creatorRel.getStartNode(); 162 Node creatorNode = creatorRel.getStartNode();
157 Actor creator = createActorFromNode(creatorNode); 163 Actor creator = createActorFromNode(creatorNode);
158 annot.setCreator(creator); 164 annot.setCreator(creator);
159 } else { 165 } else {
160 logger.error("annotation " + annotNode + " has no creator node!"); 166 logger.error("annotation " + annotNode + " has no creator node!");
161 } 167 }
162 // get creation date 168 /*
169 * get creation date
170 */
163 annot.setCreated((String) annotNode.getProperty("created", null)); 171 annot.setCreated((String) annotNode.getProperty("created", null));
164 // get permissions 172 /*
173 * get permissions
174 */
165 Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null); 175 Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null);
166 if (adminRel != null) { 176 if (adminRel != null) {
167 Node adminNode = adminRel.getEndNode(); 177 Node adminNode = adminRel.getEndNode();
168 Actor admin = createActorFromNode(adminNode); 178 Actor admin = createActorFromNode(adminNode);
169 annot.setAdminPermission(admin); 179 annot.setAdminPermission(admin);
184 if (readRel != null) { 194 if (readRel != null) {
185 Node readNode = readRel.getEndNode(); 195 Node readNode = readRel.getEndNode();
186 Actor read = createActorFromNode(readNode); 196 Actor read = createActorFromNode(readNode);
187 annot.setReadPermission(read); 197 annot.setReadPermission(read);
188 } 198 }
199 /*
200 * get tags
201 */
202 Set<String> tags = new HashSet<String>();
203 for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
204 String tag = (String) rel.getEndNode().getProperty("name", null);
205 if (tag != null) {
206 tags.add(tag);
207 }
208 }
209 annot.setTags(tags);
189 210
190 return annot; 211 return annot;
191 } 212 }
192 213
193 /** 214 /**
283 setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission()); 304 setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission());
284 setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission()); 305 setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission());
285 setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission()); 306 setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission());
286 setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission()); 307 setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
287 308
309 /*
310 * Tags on this annotation.
311 */
312 Set<String> newTags = annot.getTags();
313 // we ignore existing tags if tags == null
314 if (newTags != null) {
315 List<Relationship> oldHasTags = new ArrayList<Relationship>();
316 for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
317 oldHasTags.add(rel);
318 }
319 // adjust to new tags
320 if (newTags.isEmpty()) {
321 // remove old tags
322 if (!oldHasTags.isEmpty()) {
323 for (Relationship rel : oldHasTags) {
324 rel.delete();
325 // TODO: should we delete orphan nodes too?
326 }
327 }
328 } else {
329 if (!oldHasTags.isEmpty()) {
330 // adjust old tags
331 for (Relationship rel : oldHasTags) {
332 String oldTag = (String) rel.getEndNode().getProperty("name", null);
333 if (newTags.contains(oldTag)) {
334 // tag exists
335 newTags.remove(oldTag);
336 } else {
337 // tag exists no longer
338 rel.delete();
339 // TODO: should we delete orphan nodes too?
340 }
341 }
342 }
343 if (!newTags.isEmpty()) {
344 // still tags to add
345 for (String tag : newTags) {
346 // create new tag
347 Node tagNode = getOrCreateTagNode(tag);
348 getOrCreateRelation(annotNode, RelationTypes.HAS_TAG, tagNode);
349 }
350 }
351
352 }
353 }
288 tx.success(); 354 tx.success();
289 } finally { 355 } finally {
290 tx.finish(); 356 tx.finish();
291 } 357 }
292 358
368 logger.error("CREATED relation does not end with ANNOTATION: " + ann); 434 logger.error("CREATED relation does not end with ANNOTATION: " + ann);
369 } 435 }
370 } 436 }
371 } 437 }
372 } 438 }
439 // TODO: if both uri and user are given we should intersect
373 return annotations; 440 return annotations;
374 } 441 }
375 442
443 /**
444 * Returns Relationship of type from Node start to Node end. Creates one if
445 * it doesn't exist.
446 *
447 * @param start
448 * @param type
449 * @param end
450 * @return
451 */
376 protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) { 452 protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
377 if (start.hasRelationship()) { 453 if (start.hasRelationship()) {
378 // there are relations 454 // there are relations
379 Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING); 455 Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING);
380 for (Relationship rel : rels) { 456 for (Relationship rel : rels) {
455 try { 531 try {
456 person = graphDb.createNode(); 532 person = graphDb.createNode();
457 if (actor.isGroup()) { 533 if (actor.isGroup()) {
458 person.setProperty("TYPE", NodeTypes.GROUP.name()); 534 person.setProperty("TYPE", NodeTypes.GROUP.name());
459 } else { 535 } else {
460 person.setProperty("TYPE", NodeTypes.PERSON.name()); 536 person.setProperty("TYPE", NodeTypes.PERSON.name());
461 } 537 }
462 person.setProperty("uri", uri); 538 person.setProperty("uri", uri);
463 idx.add(person, "uri", uri); 539 idx.add(person, "uri", uri);
464 if (name != null) { 540 if (name != null) {
465 person.setProperty("name", name); 541 person.setProperty("name", name);
471 } finally { 547 } finally {
472 tx.finish(); 548 tx.finish();
473 } 549 }
474 } 550 }
475 return person; 551 return person;
552 }
553
554 protected Node getOrCreateTagNode(String tagname) {
555 Index<Node> idx = getNodeIndex(NodeTypes.TAG);
556 IndexHits<Node> tags = idx.get("name", tagname);
557 Node tag = tags.getSingle();
558 if (tag == null) {
559 // does not exist yet
560 Transaction tx = graphDb.beginTx();
561 try {
562 tag = graphDb.createNode();
563 tag.setProperty("TYPE", NodeTypes.TAG.name());
564 tag.setProperty("name", tagname);
565 idx.add(tag, "name", tagname);
566 tx.success();
567 } finally {
568 tx.finish();
569 }
570 }
571 return tag;
476 } 572 }
477 573
478 /** 574 /**
479 * Create or update permissions relations for an annotation. 575 * Create or update permissions relations for an annotation.
480 * 576 *
530 } finally { 626 } finally {
531 tx.finish(); 627 tx.finish();
532 } 628 }
533 } 629 }
534 630
535 /** returns the (first) Relationship of RelationTypes type from Node start. 631 /**
536 * 632 * returns the (first) Relationship of RelationTypes type from Node start.
633 *
537 * @param start 634 * @param start
538 * @param type 635 * @param type
539 * @param direction 636 * @param direction
540 * @return 637 * @return
541 */ 638 */
546 rels = start.getRelationships(type); 643 rels = start.getRelationships(type);
547 } else { 644 } else {
548 rels = start.getRelationships(type, direction); 645 rels = start.getRelationships(type, direction);
549 } 646 }
550 for (Relationship rel : rels) { 647 for (Relationship rel : rels) {
648 // just the first one
551 return rel; 649 return rel;
552 } 650 }
553 return null; 651 return null;
554 } 652 }
555 653