DATAGRAPH-389 : Derived Queries for Labels - part 2
This commit is contained in:
committed by
Michael Hunger
parent
4c1be9925a
commit
a605e2c415
@@ -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 {
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user