DATAGRAPH-387 : LabelingNodeTypeRepresentationStrategy (phase 3) - updates
This commit is contained in:
committed by
Michael Hunger
parent
7d33c695ac
commit
9895bdacac
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user