DATAGRAPH-387 : LabelingNodeTypeRepresentationStrategy (phase 3) - updates

This commit is contained in:
Nicki Watt
2013-09-25 22:40:43 +01:00
committed by Michael Hunger
parent 7d33c695ac
commit 9895bdacac
4 changed files with 77 additions and 79 deletions

View File

@@ -30,8 +30,7 @@ import org.springframework.data.neo4j.aspects.support.EntityTestBase;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.support.mapping.StoredEntityType;
import org.springframework.data.neo4j.support.typerepresentation.CoreAPIBasedLabelingNodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.typerepresentation.CypherBasedLabelingNodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.typerepresentation.LabelingNodeTypeRepresentationStrategy;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@@ -44,8 +43,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.HashSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
@@ -54,7 +52,7 @@ import static org.junit.Assert.assertNull;
public class LabelingNodeTypeRepresentationStrategyTests extends EntityTestBase {
@Autowired
private CypherBasedLabelingNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
private LabelingNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
@Autowired
Neo4jTemplate neo4jTemplate;
@@ -103,10 +101,22 @@ public class LabelingNodeTypeRepresentationStrategyTests extends EntityTestBase
@Test
@Transactional
public void testCount() throws Exception {
assertEquals(2, nodeTypeRepresentationStrategy.count(thingType));
public void testCountOfSuperTypeIncludesSubTypes() throws Exception {
final int EXPECTED_NUM_THINGS = 1;
final int EXPECTED_NUM_SUBTHINGS = 1;
final int TOTAL_EXPECTED = EXPECTED_NUM_THINGS + EXPECTED_NUM_SUBTHINGS;
assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(thingType));
}
@Test
@Transactional
public void testCountOfSubTypeExcludesConcreteParents() throws Exception {
final int EXPECTED_NUM_THINGS = 1;
final int EXPECTED_NUM_SUBTHINGS = 1;
final int TOTAL_EXPECTED = EXPECTED_NUM_SUBTHINGS;
assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(subThingType));
}
@Test
@Transactional
public void testGetJavaType() throws Exception {

View File

@@ -30,10 +30,12 @@ import org.springframework.data.neo4j.support.mapping.StoredEntityType;
/**
* Provides a Node Type Representation Strategy which makes use of Labels, and specifically
* uses the Core API as the mechanism for dealing with this. (This is inline with how
* the original Node Type Representation Strategies used to work - moving forward a
* Cypher based one will be used, however this exists for comparison purposes at this
* the original Node Type Representation Strategies used to work - moving forward only
* the Cypher based one will be used, however this exists for comparison purposes at this
* point in time in the development)
*
* TODO - Delete me!
*
* @author Nicki Watt
* @since 24-09-2013
*/

View File

@@ -16,9 +16,9 @@
package org.springframework.data.neo4j.support.typerepresentation;
import org.neo4j.graphdb.DynamicLabel;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.helpers.collection.ClosableIterable;
import org.springframework.data.neo4j.annotation.QueryType;
import org.springframework.data.neo4j.core.GraphDatabase;
@@ -39,70 +39,75 @@ import java.util.Map;
* @author Nicki Watt
* @since 24-09-2013
*/
public class CypherBasedLabelingNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy {
public class LabelingNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy {
public static final Label SDN_LABEL_STRATEGY = DynamicLabel.label("SDN_LABEL_STRATEGY");
public static final String LABELSTRATEGY_PREFIX = "__TYPE__";
public static final long REFERENCE_NODE_ID = 0L;
public static final String TYPE_PROPERTY_NAME = "__type__";
protected GraphDatabase graphDb;
protected final Class<Node> clazz;
protected QueryEngine<CypherQuery> queryEngine;
private boolean sdnLabelStrategyPresent;
public CypherBasedLabelingNodeTypeRepresentationStrategy(GraphDatabase graphDb) {
public LabelingNodeTypeRepresentationStrategy(GraphDatabase graphDb) {
this.graphDb = graphDb;
this.clazz = Node.class;
this.queryEngine = graphDb.queryEngineFor(QueryType.Cypher);
this.sdnLabelStrategyPresent = false;
}
@Override
public void writeTypeTo(Node state, StoredEntityType type) {
if (type == null || !type.isNodeEntity()) return;
ResourceIterable<Label> labels = state.getLabels();
if (labels.iterator().hasNext()) {
Label sdnLabel = DynamicLabel.label(LABELSTRATEGY_PREFIX + type.getAlias());
if (state.hasLabel(sdnLabel)) {
return; // already there
}
addLabel(state,type,true);
for (StoredEntityType superType : type.getSuperTypes()) {
addLabel(state, superType, false);
}
markSDNLabelStrategyInUseIfNotExists();
addLabelsForEntityHierarchy(state,type);
}
private void addLabel(Node state, StoredEntityType type, boolean isPrimary) {
String alias = type.getAlias().toString();
/*
CYPHER DOES NOT LIKE THIS ... though it would be nice :)
String addLabelStatement = "start n=node({nodeId}) set n:{alias}";
*/
String addLabelStatement = buildQuery("start n=node({nodeId}) set n:" , alias);
/**
* For each level in the entity hierarchy, this method will assign a
* label (the label name is based on the alias associated with the
* entity type at each level). Additionally, a special label is added
* as the primary SDN marker Label.
*/
private void addLabelsForEntityHierarchy(Node state, StoredEntityType type) {
String addLabelStatement = String.format("start n=node({nodeId}) set n:`%s`:`%s`" , LABELSTRATEGY_PREFIX + type.getAlias(),type.getAlias());
for (StoredEntityType superType : type.getSuperTypes()) {
addLabelStatement += String.format(":`%s`", superType.getAlias());
}
Map<String,Object> params = new HashMap<String,Object>();
params.put("nodeId",state.getId());
//params.put("alias",alias);
queryEngine.query( addLabelStatement, params);
}
if (isPrimary) {
String setPropertyStatement =
buildQuery("start n=node(", String.valueOf(state.getId()),")",
" set n.", TYPE_PROPERTY_NAME ,"='",alias,"'");
queryEngine.query( setPropertyStatement, Collections.EMPTY_MAP);
/**
* Checks if a special label (SDN_LABEL_STRATEGY) exists against the reference node, and
* if it does not, it is added. This label serves as an indicator that the Labeling strategy
* has/is being used on this data set.
*/
private void markSDNLabelStrategyInUseIfNotExists() {
if (!sdnLabelStrategyPresent) {
String query = String.format("start n=node(%d) match n:`%s` return count(*) ", REFERENCE_NODE_ID, SDN_LABEL_STRATEGY.name());
Long labelCount = queryEngine.query(query, Collections.EMPTY_MAP).to(Long.class).single();
/*
CYPHER DOES NOT LIKE THIS ...
String setPropertyStatement = "start n=node({node_id}) set n.{property_name}={property_value}";
params = new HashMap<String,Object>();
params.put("node_id",state.getId());
params.put("property_name",TYPE_PROPERTY_NAME);
params.put("property_value",alias);
queryEngine.query( setPropertyStatement, params);
*/
if (labelCount == 0) {
String update = String.format("start n=node(%d) set n:`%s` ", REFERENCE_NODE_ID, SDN_LABEL_STRATEGY.name());
queryEngine.query(update, Collections.EMPTY_MAP);
}
sdnLabelStrategyPresent = true;
}
}
@Override
public <U> ClosableIterable<Node> findAll(StoredEntityType type) {
String query = buildQuery( "match n:`" , type.getAlias().toString() , "` return n");
String query = String.format("match n:`%s` return n", type.getAlias().toString());
Iterable<Node> rin = queryEngine.query(query, Collections.EMPTY_MAP).to(Node.class);
return new WrappedIterableClosableIterable<Node>(rin);
@@ -110,7 +115,7 @@ public class CypherBasedLabelingNodeTypeRepresentationStrategy implements NodeTy
@Override
public long count(StoredEntityType type) {
String query = buildQuery("match n:`" , type.getAlias().toString() , "` return count(*)");
String query = String.format("match n:`%s` return count(*)", type.getAlias().toString());
return queryEngine.query(query, Collections.EMPTY_MAP).to(Long.class).single();
}
@@ -118,7 +123,18 @@ public class CypherBasedLabelingNodeTypeRepresentationStrategy implements NodeTy
public Object readAliasFrom(Node state) {
if (state == null)
throw new IllegalArgumentException("Node is null");
return state.getProperty(TYPE_PROPERTY_NAME);
String query = String.format("start n=node(%d) return labels(n) as labels", state.getId());
Map queryResult = queryEngine.query(query, Collections.EMPTY_MAP).to(Map.class).single();
Iterable<String> labels = (Iterable)queryResult.get("labels");
for (String label: labels) {
if (label.startsWith(LABELSTRATEGY_PREFIX)) {
return label.substring(LABELSTRATEGY_PREFIX.length());
}
}
throw new IllegalStateException("No primary SDN label exists .. (i.e one with starting with " + LABELSTRATEGY_PREFIX + ") ");
}
@Override
@@ -126,11 +142,4 @@ public class CypherBasedLabelingNodeTypeRepresentationStrategy implements NodeTy
// don't think we need to do anything here!
}
private String buildQuery(String... strings) {
StringBuffer sb = new StringBuffer();
for (String s:strings) {
sb.append(s);
}
return sb.toString();
}
}

View File

@@ -18,9 +18,7 @@ package org.springframework.data.neo4j.support.typerepresentation;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.springframework.data.neo4j.annotation.QueryType;
import org.springframework.data.neo4j.core.GraphDatabase;
import org.springframework.data.neo4j.core.GraphDatabaseGlobalOperations;
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.core.RelationshipTypeRepresentationStrategy;
import org.springframework.data.neo4j.repository.query.CypherQuery;
@@ -28,8 +26,6 @@ import org.springframework.data.neo4j.support.index.IndexProvider;
import org.springframework.data.neo4j.support.index.NoSuchIndexException;
import org.springframework.data.neo4j.support.query.QueryEngine;
import java.util.Collections;
public class TypeRepresentationStrategyFactory {
private final GraphDatabase graphDatabaseService;
private final Strategy strategy;
@@ -68,27 +64,8 @@ public class TypeRepresentationStrategyFactory {
}
private static boolean isAlreadyLabeled(GraphDatabase graphDatabaseService) {
/*
I don't think this is a very efficient query - find if there is a better way to
do this in Cypher, also it seems to break everything else simply by creating the
query engine in this manner. Sticking with GlobalGraphOps for now
QueryEngine<CypherQuery> queryEngine = graphDatabaseService.queryEngineFor(QueryType.Cypher);
Long numLabels = queryEngine.query("start n=node(*) return count( labels(n) ) ", Collections.EMPTY_MAP).to(Long.class).single();
return numLabels > 0;
*/
GraphDatabaseGlobalOperations globalOps = graphDatabaseService.getGlobalGraphOperations();
try {
return globalOps.getAllLabels().iterator().hasNext();
} catch (UnsupportedOperationException e) {
// Currently the REST DB does not support global ops
// TODO : Look to change REST project to support it
return false;
}
return graphDatabaseService.getReferenceNode().hasLabel(
LabelingNodeTypeRepresentationStrategy.SDN_LABEL_STRATEGY);
}
private static boolean isAlreadyIndexed(GraphDatabase graphDatabaseService) {
@@ -140,7 +117,7 @@ public class TypeRepresentationStrategyFactory {
Labeled {
@Override
public NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy(GraphDatabase graphDatabaseService, IndexProvider indexProvider) {
return new CypherBasedLabelingNodeTypeRepresentationStrategy(graphDatabaseService);
return new LabelingNodeTypeRepresentationStrategy(graphDatabaseService);
}
@Override