View Javadoc

1   /*
2    * Copyright 2015 Data Archiving and Networked Services (an institute of
3    * Koninklijke Nederlandse Akademie van Wetenschappen), King's College London,
4    * Georg-August-Universitaet Goettingen Stiftung Oeffentlichen Rechts
5    *
6    * Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
7    * the European Commission - subsequent versions of the EUPL (the "Licence");
8    * You may not use this work except in compliance with the Licence.
9    * You may obtain a copy of the Licence at:
10   *
11   * https://joinup.ec.europa.eu/software/page/eupl
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the Licence is distributed on an "AS IS" basis,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the Licence for the specific language governing
17   * permissions and limitations under the Licence.
18   */
19  
20  package eu.ehri.project.core.impl;
21  
22  import com.google.common.base.Preconditions;
23  import com.google.common.collect.Sets;
24  import com.tinkerpop.blueprints.CloseableIterable;
25  import com.tinkerpop.blueprints.Vertex;
26  import com.tinkerpop.frames.FramedGraph;
27  import eu.ehri.project.core.impl.neo4j.Neo4j2Graph;
28  import eu.ehri.project.core.impl.neo4j.Neo4j2Vertex;
29  import eu.ehri.project.exceptions.IntegrityError;
30  import eu.ehri.project.exceptions.ItemNotFound;
31  import eu.ehri.project.models.EntityClass;
32  import eu.ehri.project.models.annotations.EntityType;
33  import eu.ehri.project.models.utils.ClassUtils;
34  import org.neo4j.graphdb.GraphDatabaseService;
35  import org.neo4j.graphdb.Label;
36  import org.neo4j.graphdb.schema.ConstraintDefinition;
37  import org.neo4j.graphdb.schema.IndexDefinition;
38  import org.neo4j.graphdb.schema.Schema;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  import java.util.Collection;
43  import java.util.Map;
44  import java.util.NoSuchElementException;
45  import java.util.Set;
46  
47  /**
48   * Implementation of GraphManager that uses a single index to manage all nodes,
49   * with Neo4j Lucene query optimisations.
50   */
51  public final class Neo4jGraphManager<T extends Neo4j2Graph> extends BlueprintsGraphManager<T> {
52  
53      private final static Logger logger = LoggerFactory.getLogger(Neo4jGraphManager.class);
54  
55      public Neo4jGraphManager(FramedGraph<T> graph) {
56          super(graph);
57      }
58  
59      public static final String BASE_LABEL = "_Entity";
60  
61      @Override
62      public boolean exists(String id) {
63          Preconditions.checkNotNull(id,
64                  "attempt determine existence of a vertex with a null id");
65          try (CloseableIterable<Vertex> q = graph.getBaseGraph()
66                  .getVerticesByLabelKeyValue(BASE_LABEL, EntityType.ID_KEY, id)) {
67              return q.iterator().hasNext();
68          }
69      }
70  
71      @Override
72      public Vertex getVertex(String id) throws ItemNotFound {
73          Preconditions
74                  .checkNotNull(id, "attempt to fetch vertex with a null id");
75          try (CloseableIterable<Vertex> q = graph.getBaseGraph()
76                  .getVerticesByLabelKeyValue(BASE_LABEL, EntityType.ID_KEY, id)) {
77              return q.iterator().next();
78          } catch (NoSuchElementException e) {
79              throw new ItemNotFound(id);
80          }
81      }
82  
83      @Override
84      public CloseableIterable<Vertex> getVertices(String key, Object value,
85              EntityClass type) {
86          return graph.getBaseGraph().getVerticesByLabelKeyValue(type.getName(),
87                  key, value);
88      }
89  
90      @Override
91      public Vertex createVertex(String id, EntityClass type,
92              Map<String, ?> data) throws IntegrityError {
93          return setLabels(super.createVertex(id, type, data));
94      }
95  
96      @Override
97      public Vertex updateVertex(String id, EntityClass type,
98              Map<String, ?> data) throws ItemNotFound {
99          return setLabels(super.updateVertex(id, type, data));
100     }
101 
102     @Override
103     public void initialize() {
104         createIndicesAndConstraints(graph.getBaseGraph().getRawGraph());
105     }
106 
107     @Override
108     public CloseableIterable<Vertex> getVertices(EntityClass type) {
109         return graph.getBaseGraph().getVerticesByLabel(type.getName());
110     }
111 
112     /**
113      * Set labels on a Neo4j-based vertex.
114      *
115      * @param vertex the vertex
116      * @return a vertex with labels set
117      */
118     public Vertex setLabels(Vertex vertex) {
119         Neo4j2Vertex node = (Neo4j2Vertex) vertex;
120         for (String label : node.getLabels()) {
121             node.removeLabel(label);
122         }
123         node.addLabel(BASE_LABEL);
124         String type = getType(vertex);
125         node.addLabel(type);
126         return node;
127     }
128 
129     /**
130      * Create
131      */
132     public static void createIndicesAndConstraints(GraphDatabaseService graph) {
133         Schema schema = graph.schema();
134         for (ConstraintDefinition constraintDefinition : schema.getConstraints()) {
135             constraintDefinition.drop();
136         }
137         for (IndexDefinition indexDefinition : schema.getIndexes()) {
138             indexDefinition.drop();
139         }
140 
141         schema.constraintFor(Label.label(BASE_LABEL))
142                 .assertPropertyIsUnique(EntityType.ID_KEY)
143                 .create();
144         schema.indexFor(Label.label(BASE_LABEL))
145                 .on(EntityType.TYPE_KEY)
146                 .create();
147 
148         // Create an index on each mandatory or indexed property and
149         // a unique constraint on unique properties.
150         for (EntityClass cls : EntityClass.values()) {
151             Set<String> propertyKeys = Sets.newHashSet();
152             propertyKeys.addAll(ClassUtils.getIndexedPropertyKeys(cls.getJavaClass()));
153             propertyKeys.addAll(ClassUtils.getMandatoryPropertyKeys(cls.getJavaClass()));
154             for (String prop : propertyKeys) {
155                 logger.trace("Creating index on property: {} -> {}", cls.getName(), prop);
156                 schema.indexFor(Label.label(cls.getName()))
157                         .on(prop)
158                         .create();
159             }
160 
161             Collection<String> uniquePropertyKeys = ClassUtils.getUniquePropertyKeys(cls.getJavaClass());
162             for (String unique : uniquePropertyKeys) {
163                 logger.trace("Creating constraint on unique property: {} -> {}",
164                         cls.getName(), unique);
165                 schema.constraintFor(Label.label(cls.getName()))
166                         .assertPropertyIsUnique(unique)
167                         .create();
168             }
169         }
170     }
171 }