DATAGRAPH-389 : Derived Queries for Labels - part 2

This commit is contained in:
Nicki Watt
2013-10-15 07:06:51 +01:00
committed by Michael Hunger
parent 4c1be9925a
commit a605e2c415
7 changed files with 472 additions and 295 deletions

View File

@@ -0,0 +1,203 @@
/**
* 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.aspects.support.typerepresentation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Transaction;
import org.neo4j.helpers.collection.ClosableIterable;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.aspects.support.EntityTestBase;
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
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.LabelBasedNodeTypeRepresentationStrategy;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.HashSet;
import static org.junit.Assert.assertEquals;
public abstract class AbstractNodeTypeRepresentationStrategyTestBase extends EntityTestBase {
@Autowired
protected NodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
@Autowired
protected Neo4jTemplate neo4jTemplate;
@Autowired
protected Neo4jMappingContext ctx;
protected Thing thing;
protected SubThing subThing;
protected SubThing subSubThing;
protected StoredEntityType thingType;
protected StoredEntityType subThingType;
protected StoredEntityType subSubThingType;
@BeforeTransaction
public void cleanDb() {
super.cleanDb();
}
@Before
public void setUp() throws Exception {
if (thing == null) {
createThingsAndLinks();
}
thingType = typeOf(Thing.class);
subThingType = typeOf(SubThing.class);
subSubThingType = typeOf(SubSubThing.class);
}
@Test
@Transactional
public abstract void testPostEntityCreation() throws Exception;
@Test
@Transactional
public abstract void testPreEntityRemoval() throws Exception;
@Test
@Transactional
public void testFindAll() throws Exception {
ClosableIterable<Node> allThings = nodeTypeRepresentationStrategy.findAll(thingType);
assertEquals("Did not find all things.",
new HashSet<PropertyContainer>(Arrays.asList(neo4jTemplate.getPersistentState(subSubThing), neo4jTemplate.getPersistentState(subThing), neo4jTemplate.getPersistentState(thing))),
IteratorUtil.addToCollection(allThings, new HashSet<Node>()));
}
@Test
@Transactional
public void testCountOfSuperTypeIncludesSubTypes() throws Exception {
final int EXPECTED_NUM_THINGS = 1;
final int EXPECTED_NUM_SUBTHINGS = 1;
final int EXPECTED_NUM_SUBSUBTHINGS = 1;
final int TOTAL_EXPECTED = EXPECTED_NUM_THINGS + EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS;
assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(thingType));
}
@Test
@Transactional
public void testCountOfSubTypeExcludesConcreteParents() throws Exception {
final int EXPECTED_NUM_SUBTHINGS = 1;
final int EXPECTED_NUM_SUBSUBTHINGS = 1;
final int TOTAL_EXPECTED = EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS;
assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(subThingType));
}
@Test
@Transactional
public void testGetJavaType() throws Exception {
assertEquals(thingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(thing)));
assertEquals(subThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subThing)));
assertEquals(subSubThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subSubThing)));
assertEquals(Thing.class, neo4jTemplate.getStoredJavaType(node(thing)));
assertEquals(SubThing.class, neo4jTemplate.getStoredJavaType(node(subThing)));
assertEquals(SubSubThing.class, neo4jTemplate.getStoredJavaType(node(subSubThing)));
}
@Test
@Transactional
public void testCreateEntityAndInferType() throws Exception {
Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing));
assertEquals(thing, newThing);
}
@Test
@Transactional
public void testCreateEntityAndSpecifyType() throws Exception {
Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing));
assertEquals(subThing, newThing);
}
@Test
@Transactional
public void testProjectEntity() throws Exception {
Unrelated other = neo4jTemplate.projectTo(node(thing), Unrelated.class);
assertEquals("thing", other.getName());
}
protected Node node(Thing thing) {
return getNodeState(thing);
}
protected Thing createThingsAndLinks() {
Transaction tx = graphDatabaseService.beginTx();
try {
Node n1 = graphDatabaseService.createNode();
thing = neo4jTemplate.setPersistentState(new Thing(),n1);
nodeTypeRepresentationStrategy.writeTypeTo(n1, neo4jTemplate.getEntityType(Thing.class));
thing.setName("thing");
Node n2 = graphDatabaseService.createNode();
subThing = neo4jTemplate.setPersistentState(new SubThing(),n2);
nodeTypeRepresentationStrategy.writeTypeTo(n2, neo4jTemplate.getEntityType(SubThing.class));
subThing.setName("subThing");
Node n3 = graphDatabaseService.createNode();
subSubThing = neo4jTemplate.setPersistentState(new SubSubThing(),n3);
nodeTypeRepresentationStrategy.writeTypeTo(n3, neo4jTemplate.getEntityType(SubSubThing.class));
subThing.setName("subSubThing");
tx.success();
return thing;
} finally {
tx.finish();
}
}
@NodeEntity
public static class Unrelated {
String name;
public String getName() {
return name;
}
}
@NodeEntity
public static class Thing {
String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class SubThing extends Thing {
}
public static class SubSubThing extends SubThing {
}
}

View File

@@ -32,6 +32,7 @@ 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.IndexBasedNodeTypeRepresentationStrategy;
import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
@@ -44,83 +45,86 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.HashSet;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/**
* Tests to ensure that all scenarios involved in entity creation / reading etc
* behave as expected, specifically where the Index Based Type Representation Strategy
* is being used.
*
* The common scenarios/tests are defined in the superclass and each subclass, which
* represents a specific strategy, needs to ensure that all is when then they
* are used
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
"classpath:org/springframework/data/neo4j/aspects/support/IndexingTypeRepresentationStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class IndexBasedNodeTypeRepresentationStrategyTests extends EntityTestBase {
public class IndexBasedNodeTypeRepresentationStrategyTests extends AbstractNodeTypeRepresentationStrategyTestBase {
@Autowired
private IndexBasedNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
@Autowired
Neo4jTemplate neo4jTemplate;
@Autowired
Neo4jMappingContext ctx;
private Thing thing;
private SubThing subThing;
private StoredEntityType thingType;
private StoredEntityType subThingType;
@BeforeTransaction
public void cleanDb() {
super.cleanDb();
}
@Before
public void setUp() throws Exception {
if (thing == null) {
createThingsAndLinks();
}
thingType = typeOf(Thing.class);
subThingType = typeOf(SubThing.class);
@Before
@Override
public void setUp() throws Exception {
super.setUp();
assertThat("The tests in this class should be configured to use the Index " +
"based Type Representation Strategy, however it is not ... ",
nodeTypeRepresentationStrategy,
instanceOf(IndexBasedNodeTypeRepresentationStrategy.class));
}
@Test
@Transactional
@Override
public void testPostEntityCreation() throws Exception {
Index<Node> typesIndex = graphDatabaseService.index().forNodes(IndexBasedNodeTypeRepresentationStrategy.INDEX_NAME);
IndexHits<Node> thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias());
assertEquals(set(node(thing), node(subThing)), IteratorUtil.addToCollection((Iterable<Node>)thingHits, new HashSet<Node>()));
IndexHits<Node> subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias());
assertEquals(node(subThing), subThingHits.getSingle());
assertEquals(thingType.getAlias(), node(thing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
assertEquals(subThingType.getAlias(), node(subThing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
// Things
IndexHits<Node> thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias());
assertEquals(set(node(thing), node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable<Node>)thingHits, new HashSet<Node>()));
// SubThings
IndexHits<Node> subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias());
assertEquals(set(node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable<Node>)subThingHits, new HashSet<Node>()));
// SubSubThings
IndexHits<Node> subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias());
assertEquals(node(subSubThing), subSubThingHits.getSingle());
// General
assertEquals(thingType.getAlias(), node(thing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
assertEquals(subSubThingType.getAlias(), node(subSubThing).getProperty(IndexBasedNodeTypeRepresentationStrategy.TYPE_PROPERTY_NAME));
thingHits.close();
subThingHits.close();
subSubThingHits.close();
}
@Test
@Override
public void testPreEntityRemoval() throws Exception {
manualCleanDb();
createThingsAndLinks();
Index<Node> typesIndex;
IndexHits<Node> thingHits;
IndexHits<Node> subThingHits;
try (Transaction tx = graphDatabaseService.beginTx()) {
typesIndex = graphDatabaseService.index().forNodes(IndexBasedNodeTypeRepresentationStrategy.INDEX_NAME);
tx.success();
}
try (Transaction tx = graphDatabaseService.beginTx()) {
nodeTypeRepresentationStrategy.preEntityRemoval(node(thing));
tx.success();
}
testPreEntityRemovalOfThing(typesIndex);
testPreEntityRemovalOfSubThing(typesIndex);
testPreEntityRemovalOfSubSubThing(typesIndex);
}
try (Transaction tx = graphDatabaseService.beginTx()) {
thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias());
assertEquals(node(subThing), thingHits.getSingle());
subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias());
assertEquals(node(subThing), subThingHits.getSingle());
tx.success();
}
private void testPreEntityRemovalOfSubSubThing(Index<Node> typesIndex) {
IndexHits<Node> thingHits;
IndexHits<Node> subThingHits;
IndexHits<Node> subSubThingHits;
// 3. Remove SubSubThing
try (Transaction tx = graphDatabaseService.beginTx()) {
nodeTypeRepresentationStrategy.preEntityRemoval(node(subThing));
nodeTypeRepresentationStrategy.preEntityRemoval(node(subSubThing));
tx.success();
}
@@ -129,99 +133,60 @@ public class IndexBasedNodeTypeRepresentationStrategyTests extends EntityTestBas
assertNull(thingHits.getSingle());
subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias());
assertNull(subThingHits.getSingle());
subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias());
assertNull(subSubThingHits.getSingle());
tx.success();
}
}
@Test
@Transactional
public void testFindAll() throws Exception {
assertEquals("Did not find all things.",
new HashSet<PropertyContainer>(Arrays.asList(neo4jTemplate.getPersistentState(subThing), neo4jTemplate.getPersistentState(thing))),
IteratorUtil.addToCollection(nodeTypeRepresentationStrategy.findAll(thingType), new HashSet<Node>()));
}
@Test
@Transactional
public void testCount() throws Exception {
assertEquals(2, nodeTypeRepresentationStrategy.count(thingType));
}
@Test
@Transactional
public void testGetJavaType() throws Exception {
assertEquals(thingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(thing)));
assertEquals(subThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subThing)));
assertEquals(Thing.class, neo4jTemplate.getStoredJavaType(node(thing)));
assertEquals(SubThing.class, neo4jTemplate.getStoredJavaType(node(subThing)));
}
@Test
@Transactional
public void testCreateEntityAndInferType() throws Exception {
Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing));
assertEquals(thing, newThing);
}
private void testPreEntityRemovalOfSubThing(Index<Node> typesIndex) {
IndexHits<Node> thingHits;
IndexHits<Node> subThingHits;
IndexHits<Node> subSubThingHits;
@Test
@Transactional
public void testCreateEntityAndSpecifyType() throws Exception {
Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing));
assertEquals(subThing, newThing);
}
@Test
@Transactional
public void testProjectEntity() throws Exception {
Unrelated other = neo4jTemplate.projectTo(node(thing), Unrelated.class);
assertEquals("thing", other.getName());
}
private Node node(Thing thing) {
return getNodeState(thing);
}
private Thing createThingsAndLinks() {
Transaction tx = graphDatabaseService.beginTx();
try {
Node n1 = graphDatabaseService.createNode();
thing = neo4jTemplate.setPersistentState(new Thing(),n1);
nodeTypeRepresentationStrategy.writeTypeTo(n1, neo4jTemplate.getEntityType(Thing.class));
thing.setName("thing");
Node n2 = graphDatabaseService.createNode();
subThing = neo4jTemplate.setPersistentState(new SubThing(),n2);
nodeTypeRepresentationStrategy.writeTypeTo(n2, neo4jTemplate.getEntityType(SubThing.class));
subThing.setName("subThing");
tx.success();
return thing;
} finally {
tx.finish();
}
}
@NodeEntity
public static class Unrelated {
String name;
public String getName() {
return name;
}
}
@NodeEntity
public static class Thing {
String name;
public void setName(String name) {
this.name = name;
// 1. Remove SubThing
try (Transaction tx = graphDatabaseService.beginTx()) {
nodeTypeRepresentationStrategy.preEntityRemoval(node(subThing));
tx.success();
}
public String getName() {
return name;
try (Transaction tx = graphDatabaseService.beginTx()) {
thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias());
assertEquals(node(subSubThing), thingHits.getSingle());
subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias());
assertEquals(node(subSubThing), subThingHits.getSingle());
subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias());
assertEquals(node(subSubThing), subSubThingHits.getSingle());
tx.success();
}
}
public static class SubThing extends Thing {
private void testPreEntityRemovalOfThing(Index<Node> typesIndex) {
IndexHits<Node> thingHits;
IndexHits<Node> subThingHits;
IndexHits<Node> subSubThingHits;
// 1. Remove Thing
try (Transaction tx = graphDatabaseService.beginTx()) {
nodeTypeRepresentationStrategy.preEntityRemoval(node(thing));
tx.success();
}
try (Transaction tx = graphDatabaseService.beginTx()) {
thingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, thingType.getAlias());
assertEquals(set(node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable<Node>)thingHits, new HashSet<Node>()));
subThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subThingType.getAlias());
assertEquals(set(node(subThing), node(subSubThing)), IteratorUtil.addToCollection((Iterable<Node>)subThingHits, new HashSet<Node>()));
subSubThingHits = typesIndex.get(IndexBasedNodeTypeRepresentationStrategy.INDEX_KEY, subSubThingType.getAlias());
assertEquals(node(subSubThing), subSubThingHits.getSingle());
tx.success();
}
}
}

View File

@@ -19,66 +19,42 @@ package org.springframework.data.neo4j.aspects.support.typerepresentation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Transaction;
import org.neo4j.helpers.collection.ClosableIterable;
import org.neo4j.helpers.collection.IteratorUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.neo4j.annotation.NodeEntity;
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.LabelBasedNodeTypeRepresentationStrategy;
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.BeforeTransaction;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.HashSet;
import static org.junit.Assert.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
/**
* Tests to ensure that all scenarios involved in entity creation / reading etc
* behave as expected, specifically where the Label Type Representation Strategy
* is being used.
*
* The common scenarios/tests are defined in the superclass and each subclass, which
* represents a specific strategy, needs to ensure that all is when then they
* are used
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
"classpath:org/springframework/data/neo4j/aspects/support/LabelingTypeRepresentationStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class LabelBasedNodeTypeRepresentationStrategyTests extends EntityTestBase {
public class LabelBasedNodeTypeRepresentationStrategyTests extends AbstractNodeTypeRepresentationStrategyTestBase {
@Autowired
private LabelBasedNodeTypeRepresentationStrategy nodeTypeRepresentationStrategy;
@Autowired
Neo4jTemplate neo4jTemplate;
@Autowired
Neo4jMappingContext ctx;
private Thing thing;
private SubThing subThing;
private SubThing subSubThing;
private StoredEntityType thingType;
private StoredEntityType subThingType;
private StoredEntityType subSubThingType;
@BeforeTransaction
public void cleanDb() {
super.cleanDb();
}
@Before
public void setUp() throws Exception {
if (thing == null) {
createThingsAndLinks();
}
thingType = typeOf(Thing.class);
subThingType = typeOf(SubThing.class);
subSubThingType = typeOf(SubSubThing.class);
@Before
@Override
public void setUp() throws Exception {
super.setUp();
assertThat("The tests in this class should be configured to use the Label " +
"based Type Representation Strategy, however it is not ... ",
nodeTypeRepresentationStrategy,
instanceOf(LabelBasedNodeTypeRepresentationStrategy.class));
}
@Test
@@ -88,122 +64,10 @@ public class LabelBasedNodeTypeRepresentationStrategyTests extends EntityTestBas
}
@Test
@Transactional
public void testPreEntityRemoval() throws Exception {
// preEntityRemoval is a no op method, so nothing to test here!
}
@Test
@Transactional
public void testFindAll() throws Exception {
ClosableIterable<Node> allThings = nodeTypeRepresentationStrategy.findAll(thingType);
assertEquals("Did not find all things.",
new HashSet<PropertyContainer>(Arrays.asList(neo4jTemplate.getPersistentState(subSubThing), neo4jTemplate.getPersistentState(subThing), neo4jTemplate.getPersistentState(thing))),
IteratorUtil.addToCollection(allThings, new HashSet<Node>()));
}
@Test
@Transactional
public void testCountOfSuperTypeIncludesSubTypes() throws Exception {
final int EXPECTED_NUM_THINGS = 1;
final int EXPECTED_NUM_SUBTHINGS = 1;
final int EXPECTED_NUM_SUBSUBTHINGS = 1;
final int TOTAL_EXPECTED = EXPECTED_NUM_THINGS + EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS;
assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(thingType));
}
@Test
@Transactional
public void testCountOfSubTypeExcludesConcreteParents() throws Exception {
final int EXPECTED_NUM_SUBTHINGS = 1;
final int EXPECTED_NUM_SUBSUBTHINGS = 1;
final int TOTAL_EXPECTED = EXPECTED_NUM_SUBTHINGS + EXPECTED_NUM_SUBSUBTHINGS;
assertEquals(TOTAL_EXPECTED, nodeTypeRepresentationStrategy.count(subThingType));
}
@Test
@Transactional
public void testGetJavaType() throws Exception {
assertEquals(thingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(thing)));
assertEquals(subThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subThing)));
assertEquals(subSubThingType.getAlias(), nodeTypeRepresentationStrategy.readAliasFrom(node(subSubThing)));
assertEquals(Thing.class, neo4jTemplate.getStoredJavaType(node(thing)));
assertEquals(SubThing.class, neo4jTemplate.getStoredJavaType(node(subThing)));
assertEquals(SubSubThing.class, neo4jTemplate.getStoredJavaType(node(subSubThing)));
}
@Test
@Transactional
public void testCreateEntityAndInferType() throws Exception {
Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing));
assertEquals(thing, newThing);
}
@Test
@Transactional
public void testCreateEntityAndSpecifyType() throws Exception {
Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing));
assertEquals(subThing, newThing);
}
@Test
@Transactional
public void testProjectEntity() throws Exception {
Unrelated other = neo4jTemplate.projectTo(node(thing), Unrelated.class);
assertEquals("thing", other.getName());
}
private Node node(Thing thing) {
return getNodeState(thing);
}
private Thing createThingsAndLinks() {
Transaction tx = graphDatabaseService.beginTx();
try {
Node n1 = graphDatabaseService.createNode();
thing = neo4jTemplate.setPersistentState(new Thing(),n1);
nodeTypeRepresentationStrategy.writeTypeTo(n1, neo4jTemplate.getEntityType(Thing.class));
thing.setName("thing");
Node n2 = graphDatabaseService.createNode();
subThing = neo4jTemplate.setPersistentState(new SubThing(),n2);
nodeTypeRepresentationStrategy.writeTypeTo(n2, neo4jTemplate.getEntityType(SubThing.class));
subThing.setName("subThing");
Node n3 = graphDatabaseService.createNode();
subSubThing = neo4jTemplate.setPersistentState(new SubSubThing(),n3);
nodeTypeRepresentationStrategy.writeTypeTo(n3, neo4jTemplate.getEntityType(SubSubThing.class));
subThing.setName("subSubThing");
tx.success();
return thing;
} finally {
tx.finish();
}
}
@NodeEntity
public static class Unrelated {
String name;
public String getName() {
return name;
}
}
@NodeEntity
public static class Thing {
String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static class SubThing extends Thing {
}
public static class SubSubThing extends SubThing {
}
}

View File

@@ -59,6 +59,7 @@ import static org.springframework.data.neo4j.aspects.Person.persistedPerson;
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
"classpath:org/springframework/data/neo4j/aspects/support/SubReferenceTypeRepresentationStrategyOverride-context.xml"})
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
// TODO extend AbstractNodeTypeRepresentationStrategyTestBase
public class SubReferenceNodeTypeRepresentationStrategyTests extends EntityTestBase {
@Autowired

View File

@@ -17,7 +17,7 @@ package org.springframework.data.neo4j.repository.query;
/**
* Abstract class which represents a start clause which makes
* use of an indexof some sort.
* use of an index of some sort.
*
* @author Nicki Watt
*/

View File

@@ -17,6 +17,7 @@ package org.springframework.data.neo4j.repository.query;
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.util.Assert;
import java.util.EnumSet;
import java.util.List;
@@ -30,16 +31,29 @@ import java.util.List;
public class StartClauseFactory {
/**
* At present, Multiple parts are always assumed to result in a
* a FullTextIndexBasedStartClause
* Create a start clause from multiple parts
* @param partInfos The various parts which the start clause
* needs to be created around/for.
* @return A appropriate StartClause
* @return An appropriate StartClause
*/
public static StartClause create(List<PartInfo> partInfos) {
return (partInfos.size() == 1)
? create(partInfos.get(0))
: new FullTextIndexBasedStartClause(partInfos);
Assert.notEmpty(partInfos);
if (partInfos.size() == 1) {
return create(partInfos.get(0));
} else if (areAllIndexedAndHaveSameIdentifiers(partInfos)) {
return new FullTextIndexBasedStartClause(partInfos);
}
throw new IllegalArgumentException("Cannot determine an appropriate Start Clause for multiple partInfos provided");
}
private static boolean areAllIndexedAndHaveSameIdentifiers(List<PartInfo> partInfos) {
// We use the first part to compare the others against
PartInfo firstPart = partInfos.get(0);
for (PartInfo partInfo : partInfos) {
if (!partInfo.isIndexed()) return false;
if (!partInfo.sameIdentifier(firstPart)) return false;
}
return true;
}
/**

View File

@@ -0,0 +1,130 @@
package org.springframework.data.neo4j.repository.query;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.repository.query.parser.Part;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
/**
* Unit Test(s) of the StartClauseFactory
*/
public class StartClauseFactoryTest {
PartInfo simpleIndexedPartInfo1;
PartInfo simpleIndexedPartInfo2;
PartInfo searchableIndexedPartInfo;
PartInfo fullTextIndexedPartInfo;
PartInfo idBasedPartInfo;
PartInfo relBasedPartInfo;
@Before
public void setUp() throws Exception {
// TODO - Perhaps rather use Spring to setup rather than mock
simpleIndexedPartInfo1 = Mockito.mock(PartInfo.class);
when(simpleIndexedPartInfo1.isIndexed()).thenReturn(true);
when(simpleIndexedPartInfo1.isFullText()).thenReturn(false);
simpleIndexedPartInfo2 = Mockito.mock(PartInfo.class);
when(simpleIndexedPartInfo2.isIndexed()).thenReturn(true);
when(simpleIndexedPartInfo2.isFullText()).thenReturn(false);
searchableIndexedPartInfo = Mockito.mock(PartInfo.class);
when(searchableIndexedPartInfo.isIndexed()).thenReturn(true);
when(searchableIndexedPartInfo.isFullText()).thenReturn(false);
when(searchableIndexedPartInfo.getType()).thenReturn(Part.Type.STARTING_WITH);
fullTextIndexedPartInfo = Mockito.mock(PartInfo.class);
when(fullTextIndexedPartInfo.isIndexed()).thenReturn(true);
when(fullTextIndexedPartInfo.isFullText()).thenReturn(true);
Neo4jPersistentProperty leafProperty1 = Mockito.mock(Neo4jPersistentProperty.class);
idBasedPartInfo = Mockito.mock(PartInfo.class);
when(idBasedPartInfo.isIndexed()).thenReturn(false);
when(idBasedPartInfo.getLeafProperty()).thenReturn(leafProperty1);
when(leafProperty1.isRelationship()).thenReturn(false);
when(leafProperty1.isIdProperty()).thenReturn(true);
Neo4jPersistentProperty leafProperty2 = Mockito.mock(Neo4jPersistentProperty.class);
relBasedPartInfo = Mockito.mock(PartInfo.class);
when(relBasedPartInfo.isIndexed()).thenReturn(false);
when(relBasedPartInfo.getLeafProperty()).thenReturn(leafProperty2);
when(leafProperty2.isRelationship()).thenReturn(true);
when(leafProperty2.isIdProperty()).thenReturn(false);
}
@Test
public void testCreateExactIndexBasedStartClause() {
StartClause startClause = StartClauseFactory.create(simpleIndexedPartInfo1);
assertTrue(startClause instanceof ExactIndexBasedStartClause);
}
@Test
public void testCreateFullTextIndexBasedStartClauseWhenFullyIndexed() {
StartClause startClause = StartClauseFactory.create(fullTextIndexedPartInfo);
assertTrue(startClause instanceof FullTextIndexBasedStartClause);
}
@Test
public void testCreateFullTextIndexBasedStartClauseWhenNormallyIndexedButWithSearchLikePart() {
StartClause startClause = StartClauseFactory.create(searchableIndexedPartInfo);
assertTrue(startClause instanceof FullTextIndexBasedStartClause);
}
@Test
public void testCreateGraphIdStartClauseWhenIdPropertyPathInfo() {
StartClause startClause = StartClauseFactory.create(idBasedPartInfo);
assertTrue(startClause instanceof GraphIdStartClause);
}
@Test
public void testCreateGraphIdStartClauseWhenIsRelationship() {
// This is how it was in the original code, not sure why
// relationships should always result in a GraphIdStartClause
// TODO - find out why
StartClause startClause = StartClauseFactory.create(relBasedPartInfo);
assertTrue(startClause instanceof GraphIdStartClause);
}
@Test
public void testCreateFullTextIndexBasedStartClauseWhenMultiplePartsProvidedWhichAreAllIndexedWithSameId() {
List<PartInfo> partInfos = new ArrayList<PartInfo>();
partInfos.add(simpleIndexedPartInfo1);
partInfos.add(simpleIndexedPartInfo2);
when(simpleIndexedPartInfo1.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true);
when(simpleIndexedPartInfo2.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true);
StartClause startClause = StartClauseFactory.create(partInfos);
assertTrue(startClause instanceof FullTextIndexBasedStartClause);
}
@Test(expected = IllegalArgumentException.class)
public void testExceptionThrownWhenProvidingMultiplePartsWhichAreAllIndexedButDiffId() {
List<PartInfo> partInfos = new ArrayList<PartInfo>();
partInfos.add(simpleIndexedPartInfo1);
partInfos.add(simpleIndexedPartInfo2);
when(simpleIndexedPartInfo1.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true);
when(simpleIndexedPartInfo2.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(false);
StartClauseFactory.create(partInfos);
}
@Test(expected = IllegalArgumentException.class)
public void testExceptionThrownWhenProvidingMultiplePartsWhichAreNotAllIndexed() {
List<PartInfo> partInfos = new ArrayList<PartInfo>();
partInfos.add(simpleIndexedPartInfo1);
partInfos.add(idBasedPartInfo);
when(simpleIndexedPartInfo1.sameIdentifier(simpleIndexedPartInfo1)).thenReturn(true);
StartClauseFactory.create(partInfos);
}
}