DATAGRAPH-436 Use case for @Labels (first stab at this)

This commit is contained in:
Michael Hunger
2014-03-13 13:44:44 +01:00
parent 482294473f
commit 9728b7d188
13 changed files with 147 additions and 14 deletions

View File

@@ -108,6 +108,8 @@ public class NodeEntityTests extends EntityTestBase {
public void testLabels() {
String[] labelNames = {"Person", "Developer", "Father","_Person"};
Person p = new Person("Michael",39).persist();
// assertThat(p.getLabels(), hasItems(labelNames[0],labelNames[3]));
p = neo4jTemplate.findOne(p.getId(), Person.class);
assertThat(p.getLabels(), hasItems(labelNames[0],labelNames[3]));
p.addLabel(labelNames[1]);
p.addLabel(labelNames[2]);

View File

@@ -22,7 +22,6 @@ import org.neo4j.rest.graphdb.RestAPIFacade;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.index.RestIndex;
import org.neo4j.rest.graphdb.index.RestIndexManager;
import org.neo4j.rest.graphdb.query.RestCypherQueryEngine;
import org.neo4j.rest.graphdb.transaction.NullTransaction;
import org.neo4j.rest.graphdb.transaction.NullTransactionManager;
import org.neo4j.rest.graphdb.util.Config;
@@ -91,10 +90,12 @@ public class SpringRestGraphDatabase extends org.neo4j.rest.graphdb.RestGraphDat
}
@Override
public Node getOrCreateNode(String indexName, String key, Object value, final Map<String,Object> properties) {
public Node getOrCreateNode(String indexName, String key, Object value, final Map<String, Object> properties, Collection<String> labels) {
if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
final RestIndex<Node> nodeIndex = index().forNodes(indexName);
return getRestAPI().getOrCreateNode(nodeIndex, key, value, properties);
RestNode node = getRestAPI().getOrCreateNode(nodeIndex, key, value, properties);
getRestAPI().addLabels(node,toLabels(labels));
return node;
}
@Override

View File

@@ -217,6 +217,12 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -52,7 +52,7 @@ public interface GraphDatabase {
* creates the node uniquely or returns an existing node with the same index-key-value combination.
* properties are used to initialize the node.
*/
Node getOrCreateNode(String indexName, String key, Object value, final Map<String,Object> properties);
Node getOrCreateNode(String indexName, String key, Object value, final Map<String, Object> properties, Collection<String> labels);
/**
* @param id relationship id

View File

@@ -0,0 +1,108 @@
/**
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.neo4j.fieldaccess;
import org.neo4j.graphdb.DynamicLabel;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.neo4j.annotation.Labels;
import org.springframework.data.neo4j.mapping.MappingPolicy;
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import java.util.Set;
import java.util.TreeSet;
import static org.springframework.data.neo4j.support.DoReturn.doReturn;
public class LabelFieldAccessorFactory implements FieldAccessorFactory {
private final Neo4jTemplate template;
public LabelFieldAccessorFactory(Neo4jTemplate template) {
this.template = template;
}
@Override
public boolean accept(final Neo4jPersistentProperty property) {
return property.isAnnotationPresent(Labels.class);
}
@Override
public FieldAccessor forField(final Neo4jPersistentProperty property) {
return new LabelFieldAccessor(property, template);
}
public static class LabelFieldAccessor implements FieldAccessor {
protected final Neo4jPersistentProperty property;
private final Neo4jTemplate template;
public LabelFieldAccessor(final Neo4jPersistentProperty property, Neo4jTemplate template) {
this.property = property;
this.template = template;
}
@Override
public boolean isWriteable(Object entity) {
return true;
}
@Override
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
if (entity==null) return entity;
final PropertyContainer state = template.getPersistentState(entity);
if (state instanceof Node) {
Node node = (Node) state;
Set<String> oldLabels = getLabels(node);
for (String newLabel : (Iterable<String>) newVal) {
if (oldLabels.remove(newLabel)) continue;
node.addLabel(DynamicLabel.label(newLabel));
}
for (String removedLabels : oldLabels) {
node.removeLabel(DynamicLabel.label(removedLabels));
}
return doReturn(newVal);
}
throw new MappingException("Error setting labels on "+entity);
}
@Override
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
final PropertyContainer state = template.getPersistentState(entity);
if (state instanceof Node) {
return doReturn(getLabels((Node) state));
}
throw new MappingException("Error retrieving labels from "+entity);
}
private Set<String> getLabels(Node state) {
Set<String> labels = new TreeSet<>();
for (Label label : state.getLabels()) {
labels.add(label.name());
}
return labels;
}
@Override
public Object getDefaultValue() {
return null;
}
}
}

View File

@@ -287,17 +287,26 @@ public class DelegatingGraphDatabase implements GraphDatabase {
return schemaIndexProvider.merge(labelName,key,value,nodeProperties,labels);
}
public Node getOrCreateNode(String indexName, String key, Object value, final Map<String,Object> nodeProperties) {
public Node getOrCreateNode(String indexName, String key, Object value, final Map<String, Object> nodeProperties, final Collection<String> labels) {
if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
if (value instanceof Number) value= ValueContext.numeric((Number)value);
UniqueFactory.UniqueNodeFactory factory = new UniqueFactory.UniqueNodeFactory(delegate, indexName) {
protected void initialize(Node node, Map<String, Object> _) {
setProperties(node,nodeProperties);
setLabels(node,labels);
}
};
return factory.getOrCreate(key, value);
}
private Node setLabels(Node node, Collection<String> labels) {
if (labels==null || labels.isEmpty()) return node;
for (String label : labels) {
node.addLabel(DynamicLabel.label(label));
}
return node;
}
@Override
public Relationship getOrCreateRelationship(String indexName, String key, Object value, final Node startNode, final Node endNode, final String type, final Map<String, Object> properties) {
if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");

View File

@@ -296,8 +296,8 @@ public class Neo4jTemplate implements Neo4jOperations, ApplicationContextAware {
* properties are used to initialize the node.
*/
@Override
public Node getOrCreateNode(String index, String key, Object value, final Map<String, Object> properties) {
return getGraphDatabase().getOrCreateNode(index, key, value, properties);
public Node getOrCreateNode(String index, String key, Object value, final Map<String, Object> properties, Collection<String> labels) {
return getGraphDatabase().getOrCreateNode(index, key, value, properties, labels);
}
/**
@@ -739,7 +739,7 @@ public class Neo4jTemplate implements Neo4jOperations, ApplicationContextAware {
return getGraphDatabase().merge(indexInfo.getIndexName(),indexInfo.getIndexKey(),value, Collections.<String,Object>emptyMap(), persistentEntity.getAllLabels());
} else {
if (value instanceof Number && indexInfo.isNumeric()) value = ValueContext.numeric((Number) value);
return getGraphDatabase().getOrCreateNode(indexInfo.getIndexName(), indexInfo.getIndexKey(), value, Collections.<String, Object>emptyMap());
return getGraphDatabase().getOrCreateNode(indexInfo.getIndexName(), indexInfo.getIndexKey(), value, Collections.<String, Object>emptyMap(), persistentEntity.getAllLabels());
}
}

View File

@@ -153,7 +153,7 @@ public class EntityStateHandler {
if (indexInfo.isLabelBased()) {
return graphDatabase.merge(indexInfo.getIndexName(), indexInfo.getIndexKey(), value, Collections.<String,Object>emptyMap(), persistentEntity.getAllLabels());
} else {
return graphDatabase.getOrCreateNode(indexInfo.getIndexName(), indexInfo.getIndexKey(), value, Collections.<String,Object>emptyMap());
return graphDatabase.getOrCreateNode(indexInfo.getIndexName(), indexInfo.getIndexKey(), value, Collections.<String,Object>emptyMap(),persistentEntity.getAllLabels());
}
}

View File

@@ -109,7 +109,7 @@ public class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersistentE
@Override
public void initialize() {
super.initialize();
//TODO re-enable after SDC update setStrict(true);
setStrict(true);
}
private boolean isRelationshipEntityType(Class<?> type) {

View File

@@ -31,6 +31,7 @@ import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.neo4j.mapping.*;
import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy;
import org.springframework.data.util.TypeInformation;
/**
@@ -252,6 +253,8 @@ public class Neo4jPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4j
final Set<String> labels = collectSuperTypeLabels(storedType, new LinkedHashSet<String>());
labels.addAll(computeIndexBasedLabels(this));
labels.add(alias);
// TODO workaround check if this MC is label based from the TRS
// labels.add(LabelBasedNodeTypeRepresentationStrategy.LABELSTRATEGY_PREFIX+alias);
return labels;
}

View File

@@ -72,11 +72,15 @@ public class LabelBasedNodeTypeRepresentationStrategy implements NodeTypeReprese
* as the primary SDN marker Label.
*/
private void addLabelsForEntityHierarchy(Node state, StoredEntityType type) {
cypherHelper.setLabelsOnNode(state.getId(), getAllHierarchyLabelsForType(type));
}
private Set<String> getAllHierarchyLabelsForType(StoredEntityType type) {
String alias = type.getAlias().toString();
Set<String> labels = collectSuperTypeLabels(type, new LinkedHashSet<String>());
labels.add(alias);
labels.add(LABELSTRATEGY_PREFIX + alias);
cypherHelper.setLabelsOnNode(state.getId(), labels);
return labels;
}
private Set<String> collectSuperTypeLabels(StoredEntityType type, Set<String> labels) {

View File

@@ -81,7 +81,7 @@ public interface Neo4jOperations {
* creates the node uniquely or returns an existing node with the same index-key-value combination.
* properties are used to initialize the node.
*/
Node getOrCreateNode(String index, String key, Object value, Map<String, Object> properties);
Node getOrCreateNode(String index, String key, Object value, Map<String, Object> properties, Collection<String> labels);
/**
* creates the node uniquely or returns an existing node with the same label-key-value combination.

View File

@@ -51,8 +51,8 @@ public class DelegatingGraphDatabaseTests {
@Test
public void testGetOrCreateNode() throws Exception {
try (Transaction tx = graphDatabase.beginTx()) {
final Node node = graphDatabase.getOrCreateNode("user", "name", "David", map("name", "David"));
final Node node2 = graphDatabase.getOrCreateNode("user", "name", "David", map("name", "David"));
final Node node = graphDatabase.getOrCreateNode("user", "name", "David", map("name", "David"), null);
final Node node2 = graphDatabase.getOrCreateNode("user", "name", "David", map("name", "David"), null);
assertEquals("David",node.getProperty("name"));
assertEquals(node,node2);
assertEquals(node,gdb.index().forNodes("user").get("name","David").getSingle());