DATAGRAPH-387 : Minimal changes required to get LabelNodeTypeRepresentationStrategy working across the board (including REST)
This commit is contained in:
committed by
Michael Hunger
parent
b40ea309ae
commit
5e90f6b5b3
@@ -33,8 +33,6 @@ import org.springframework.data.neo4j.annotation.QueryType;
|
||||
import org.springframework.data.neo4j.conversion.DefaultConverter;
|
||||
import org.springframework.data.neo4j.conversion.ResultConverter;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.core.GraphDatabaseGlobalOperations;
|
||||
import org.springframework.data.neo4j.support.DelegatingGraphDatabaseGlobalOperations;
|
||||
import org.springframework.data.neo4j.support.index.NoSuchIndexException;
|
||||
import org.springframework.data.neo4j.support.query.ConversionServiceQueryResultConverter;
|
||||
import org.springframework.data.neo4j.support.query.QueryEngine;
|
||||
@@ -48,7 +46,6 @@ public class SpringRestGraphDatabase extends org.neo4j.rest.graphdb.RestGraphDat
|
||||
}
|
||||
private ConversionService conversionService;
|
||||
private ResultConverter resultConverter;
|
||||
private GraphDatabaseGlobalOperations globalOperations;
|
||||
|
||||
public SpringRestGraphDatabase( RestAPI api){
|
||||
super(api);
|
||||
@@ -62,14 +59,6 @@ public class SpringRestGraphDatabase extends org.neo4j.rest.graphdb.RestGraphDat
|
||||
this(new RestAPIFacade( uri, user, password ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphDatabaseGlobalOperations getGlobalGraphOperations() {
|
||||
if (this.globalOperations == null) {
|
||||
this.globalOperations = new DelegatingGraphDatabaseGlobalOperations(this);
|
||||
}
|
||||
return globalOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createNode(Map<String, Object> props) {
|
||||
return super.getRestAPI().createNode(props);
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase" scope="singleton">
|
||||
<constructor-arg index="0" value="http://localhost:7470/db/data" />
|
||||
</bean>
|
||||
<alias name="graphDatabaseService" alias="graphDatabase"/>
|
||||
</beans>
|
||||
|
||||
@@ -30,13 +30,6 @@ import java.util.Map;
|
||||
|
||||
|
||||
public interface GraphDatabase {
|
||||
|
||||
/**
|
||||
* @return an object which is able to provide global graph
|
||||
* type operations
|
||||
*/
|
||||
GraphDatabaseGlobalOperations getGlobalGraphOperations();
|
||||
|
||||
/**
|
||||
* @return the reference node of the underlying graph database
|
||||
*/
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashSet;
|
||||
@@ -75,8 +73,13 @@ public class RelationshipHelper {
|
||||
for ( Relationship relationship : node.getRelationships( type, direction ) ) {
|
||||
if ( !targetNodes.remove( relationship.getOtherNode( node ) ) ) {
|
||||
if ( targetType != null ) {
|
||||
Object actualTargetType = relationship.getOtherNode( node ).getProperty( "__type__" );
|
||||
|
||||
Object actualTargetType = tryDetermineTypeAssumingIndexBasedStrategy(relationship, node);
|
||||
if (actualTargetType == null) {
|
||||
actualTargetType = tryDetermineTypeAssumingLabelBasedStrategy(relationship, node);
|
||||
}
|
||||
if (actualTargetType == null) {
|
||||
throw new RuntimeException("Neither a property or Label could be found to work out what the type of the node is at the other end of the relationship ");
|
||||
}
|
||||
try {
|
||||
if (! targetType.isAssignableFrom(Class.forName((String) actualTargetType))) {
|
||||
continue;
|
||||
@@ -91,6 +94,28 @@ public class RelationshipHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private Object tryDetermineTypeAssumingLabelBasedStrategy(Relationship relationship,Node node) {
|
||||
|
||||
ResourceIterable<Label> labels = relationship.getOtherNode(node).getLabels();
|
||||
ResourceIterator<Label> iterator = labels.iterator();
|
||||
try {
|
||||
while (iterator.hasNext()) {
|
||||
Label l = iterator.next();
|
||||
if (l.name().startsWith(LabelBasedNodeTypeRepresentationStrategy.LABELSTRATEGY_PREFIX)) {
|
||||
return l.name().substring(LabelBasedNodeTypeRepresentationStrategy.LABELSTRATEGY_PREFIX.length());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
iterator.close();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private Object tryDetermineTypeAssumingIndexBasedStrategy(Relationship relationship,Node node) {
|
||||
return relationship.getOtherNode( node ).getProperty( "__type__" , null);
|
||||
}
|
||||
|
||||
protected void createAddedRelationships(Node node, Set<Node> targetNodes) {
|
||||
for (Node targetNode : targetNodes) {
|
||||
createSingleRelationship(node, targetNode);
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.index.NoSuchIndexException;
|
||||
import org.springframework.data.neo4j.support.index.NullReadableIndex;
|
||||
import org.springframework.data.neo4j.support.query.QueryEngine;
|
||||
import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -347,7 +348,9 @@ public abstract class AbstractGraphRepository<S extends PropertyContainer, T> im
|
||||
|
||||
@Override
|
||||
public EndResult<T> findAll(Sort sort) {
|
||||
CypherQuery cq = new CypherQuery(template.getEntityType(clazz).getEntity(),template);
|
||||
// TODO : Do nicer mechanism for working out if labels are in play
|
||||
boolean useLabels = template.getInfrastructure().getNodeTypeRepresentationStrategy() instanceof LabelBasedNodeTypeRepresentationStrategy;
|
||||
CypherQuery cq = new CypherQuery(template.getEntityType(clazz).getEntity(),template,useLabels);
|
||||
return query(cq.toQueryString(sort), Collections.EMPTY_MAP);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,19 +40,28 @@ public class CypherQuery implements CypherQueryDefinition {
|
||||
private final Neo4jPersistentEntity<?> entity;
|
||||
private final Neo4jTemplate template;
|
||||
private boolean isCountQuery = false;
|
||||
private boolean useLabels = false;
|
||||
|
||||
public CypherQuery(final Neo4jPersistentEntity<?> entity, Neo4jTemplate template) {
|
||||
public CypherQuery(final Neo4jPersistentEntity<?> entity, Neo4jTemplate template, boolean useLabels) {
|
||||
this.entity = entity;
|
||||
this.template = template;
|
||||
this.useLabels = useLabels;
|
||||
}
|
||||
|
||||
private String getEntityName(Neo4jPersistentEntity<?> entity) {
|
||||
return variableContext.getVariableFor(entity);
|
||||
}
|
||||
|
||||
private String defaultStartClause(Neo4jPersistentEntity<?> entity) {
|
||||
return String.format(QueryTemplates.DEFAULT_START_CLAUSE, getEntityName(entity), entity
|
||||
.getEntityType().getAlias());
|
||||
private String defaultLegacyStartClause(Neo4jPersistentEntity<?> entity) {
|
||||
return String.format(QueryTemplates.DEFAULT_INDEXBASED_START_CLAUSE,
|
||||
getEntityName(entity),
|
||||
entity.getEntityType().getAlias());
|
||||
}
|
||||
|
||||
private String defaultMatchBasedStartClause(Neo4jPersistentEntity<?> entity) {
|
||||
return String.format(QueryTemplates.DEFAULT_LABELBASED_MATCH_START_CLAUSE,
|
||||
getEntityName(entity),
|
||||
entity.getEntityType().getAlias());
|
||||
}
|
||||
|
||||
public void addPart(Part part, PersistentPropertyPath<Neo4jPersistentProperty> path) {
|
||||
@@ -69,10 +78,18 @@ public class CypherQuery implements CypherQueryDefinition {
|
||||
}
|
||||
} else if (leafProperty.isRelationship()) {
|
||||
startClauses.add(new NodeEntityMatchingStartClause(partInfo));
|
||||
whereClauses.add(new TypeRestrictingWhereClause(new PartInfo(path, variableContext.getVariableFor(entity), part, -1), entity, template));
|
||||
if (useLabels) {
|
||||
whereClauses.add(new LabelBasedTypeRestrictingWhereClause(new PartInfo(path, variableContext.getVariableFor(entity), part, -1), entity, template));
|
||||
} else {
|
||||
whereClauses.add(new IndexBasedTypeRestrictingWhereClause(new PartInfo(path, variableContext.getVariableFor(entity), part, -1), entity, template));
|
||||
}
|
||||
} else if (leafProperty.isIdProperty()) {
|
||||
startClauses.add(new NodeEntityMatchingStartClause(partInfo));
|
||||
whereClauses.add(new TypeRestrictingWhereClause(new PartInfo(path, variableContext.getVariableFor(entity), part, -1), entity, template));
|
||||
if (useLabels) {
|
||||
whereClauses.add(new LabelBasedTypeRestrictingWhereClause(new PartInfo(path, variableContext.getVariableFor(entity), part, -1), entity, template));
|
||||
} else {
|
||||
whereClauses.add(new IndexBasedTypeRestrictingWhereClause(new PartInfo(path, variableContext.getVariableFor(entity), part, -1), entity, template));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Error "+part+" points neither to a primitive nor a entity property of "+entity);
|
||||
}
|
||||
@@ -154,20 +171,21 @@ public class CypherQuery implements CypherQueryDefinition {
|
||||
}
|
||||
|
||||
private String render() {
|
||||
String startClauses = collectionToDelimitedString(this.startClauses, ", ");
|
||||
String legacyStartClauses = collectionToDelimitedString(this.startClauses, ", ");
|
||||
String matchClauses = toQueryString(this.matchClauses);
|
||||
String whereClauses = collectionToDelimitedString(this.whereClauses, " AND ");
|
||||
|
||||
StringBuilder builder = new StringBuilder("START ");
|
||||
StringBuilder builder = new StringBuilder("");
|
||||
|
||||
if (hasText(startClauses)) {
|
||||
builder.append(startClauses);
|
||||
} else {
|
||||
builder.append(defaultStartClause(entity));
|
||||
boolean matchKeyWordUsed = false;
|
||||
boolean legacyStartClauseUsed = buildInLegacyStartClauses(builder,legacyStartClauses);
|
||||
if (!legacyStartClauseUsed && useLabels) {
|
||||
matchKeyWordUsed = true;
|
||||
builder.append(" MATCH ").append(defaultMatchBasedStartClause(entity));
|
||||
}
|
||||
|
||||
if (hasText(matchClauses)) {
|
||||
builder.append(" MATCH ").append(matchClauses);
|
||||
builder.append(matchKeyWordUsed ? " , " : " MATCH ");
|
||||
builder.append(matchClauses);
|
||||
}
|
||||
|
||||
if (hasText(whereClauses)) {
|
||||
@@ -183,6 +201,22 @@ public class CypherQuery implements CypherQueryDefinition {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This will change to get rid of the start clauses completely but
|
||||
* for now we just get it to work!
|
||||
*/
|
||||
private boolean buildInLegacyStartClauses(StringBuilder builder, String legacyStartClauses) {
|
||||
if (hasText(legacyStartClauses)) {
|
||||
builder.append("START ").append(legacyStartClauses);
|
||||
return true;
|
||||
} else if (!useLabels) {
|
||||
// TODO: Need to change index based stuff to also not use START
|
||||
builder.append("START ").append(defaultLegacyStartClause(entity));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private String addSorts(Sort sort) {
|
||||
final List<String> sorts = formatSorts(sort);
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
/**
|
||||
@@ -35,7 +36,9 @@ class CypherQueryBuilder {
|
||||
public CypherQueryBuilder(MappingContext<? extends Neo4jPersistentEntity<?>, Neo4jPersistentProperty> context, Class<?> type, Neo4jTemplate template) {
|
||||
this.context = context;
|
||||
Neo4jPersistentEntity<?> entity = context.getPersistentEntity(type);
|
||||
this.query = new CypherQuery(entity, template);
|
||||
// TODO : Do nicer mechanism for working out if labels are in play
|
||||
boolean useLabels = template.getInfrastructure().getNodeTypeRepresentationStrategy() instanceof LabelBasedNodeTypeRepresentationStrategy;
|
||||
this.query = new CypherQuery(entity, template, useLabels);
|
||||
}
|
||||
|
||||
public CypherQueryBuilder asCountQuery() {
|
||||
|
||||
@@ -22,20 +22,29 @@ import org.springframework.data.neo4j.support.mapping.StoredEntityType;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.data.neo4j.repository.query.QueryTemplates.WHERE_TYPE_CHECK;
|
||||
import static org.springframework.data.neo4j.repository.query.QueryTemplates.INDEXBASED_WHERE_TYPE_CHECK;
|
||||
import static org.springframework.util.StringUtils.collectionToCommaDelimitedString;
|
||||
|
||||
public class TypeRestrictingWhereClause extends WhereClause {
|
||||
/**
|
||||
* Representation of a Cypher {@literal where} clause specifically for
|
||||
* use to narrow the results based on particular entity types, where
|
||||
* those entities can be identified via the special __type__ property
|
||||
* present on all nodes when the Index Based Type Representation
|
||||
* Strategy is in use
|
||||
*
|
||||
* @author Nicki Watt
|
||||
*/
|
||||
public class IndexBasedTypeRestrictingWhereClause extends WhereClause {
|
||||
private String aliases;
|
||||
|
||||
public TypeRestrictingWhereClause(PartInfo partInfo, Neo4jPersistentEntity entity, Neo4jTemplate template) {
|
||||
public IndexBasedTypeRestrictingWhereClause(PartInfo partInfo, Neo4jPersistentEntity entity, Neo4jTemplate template) {
|
||||
super(partInfo, template);
|
||||
aliases = collectionToCommaDelimitedString(collectAliases(entity.getEntityType()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(WHERE_TYPE_CHECK, partInfo.getIdentifier(), aliases);
|
||||
return String.format(INDEXBASED_WHERE_TYPE_CHECK, partInfo.getIdentifier(), aliases);
|
||||
}
|
||||
|
||||
private Set<String> collectAliases(StoredEntityType entityType) {
|
||||
@@ -25,6 +25,6 @@ public class IndexRestrictingStartClause extends StartClause {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(QueryTemplates.DEFAULT_START_CLAUSE, getPartInfo().getIdentifier(), className);
|
||||
return String.format(QueryTemplates.DEFAULT_INDEXBASED_START_CLAUSE, getPartInfo().getIdentifier(), className);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.repository.query;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.mapping.StoredEntityType;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.springframework.data.neo4j.repository.query.QueryTemplates.INDEXBASED_WHERE_TYPE_CHECK;
|
||||
import static org.springframework.data.neo4j.repository.query.QueryTemplates.LABELBASED_WHERE_TYPE_CHECK;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
/**
|
||||
* Representation of a Cypher {@literal where} clause specifically for
|
||||
* use to narrow the results based on particular entity types, where
|
||||
* those entities can be identified via specific Labels (as per the
|
||||
* Label Based Type Representation Strategy)
|
||||
*
|
||||
* @author Nicki Watt
|
||||
*/
|
||||
public class LabelBasedTypeRestrictingWhereClause extends WhereClause {
|
||||
private String aliases;
|
||||
|
||||
public LabelBasedTypeRestrictingWhereClause(PartInfo partInfo, Neo4jPersistentEntity entity, Neo4jTemplate template) {
|
||||
super(partInfo, template);
|
||||
Object[] aliasAsArray = collectAliases(entity.getEntityType()).toArray();
|
||||
aliases = arrayToDelimitedString(aliasAsArray, ":");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(LABELBASED_WHERE_TYPE_CHECK, partInfo.getIdentifier(), aliases);
|
||||
}
|
||||
|
||||
private Set<String> collectAliases(StoredEntityType entityType) {
|
||||
Set<String> aliases = new HashSet<String>();
|
||||
aliases.add("`"+entityType.getAlias().toString()+"`");
|
||||
for (StoredEntityType superType : entityType.getSuperTypes()) {
|
||||
aliases.addAll(collectAliases(superType));
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
}
|
||||
@@ -45,13 +45,16 @@ public abstract class QueryTemplates {
|
||||
static final String MATCH_CLAUSE = "`%s`%s`%s`";
|
||||
static final String MATCH_CLAUSE2 = "%s%s`%s`";
|
||||
|
||||
static final String DEFAULT_START_CLAUSE = "`%s`=node:__types__(className=\"%s\")";
|
||||
static final String DEFAULT_INDEXBASED_START_CLAUSE = "`%s`=node:__types__(className=\"%s\")";
|
||||
static final String DEFAULT_LABELBASED_MATCH_START_CLAUSE = "`%s`:`%s`";
|
||||
|
||||
public static final String START_NODE_LOOKUP = "`%s`=node({%d})";
|
||||
static final String SKIP_LIMIT = " SKIP %d LIMIT %d";
|
||||
static final String START_CLAUSE_INDEX_LOOKUP = "`%s`=node:`%s`(`%s`=" + PLACEHOLDER + ")";
|
||||
static final String START_CLAUSE_INDEX_QUERY = "`%s`=node:`%s`(" + PLACEHOLDER + ")";
|
||||
static final String WHERE_CLAUSE_1 = "`%1$s`.`%2$s` %3$s {%4$d}";
|
||||
static final String WHERE_TYPE_CHECK = "`%1$s`.__type__ IN [%2$s]";
|
||||
static final String INDEXBASED_WHERE_TYPE_CHECK = "`%1$s`.__type__ IN [%2$s]";
|
||||
static final String LABELBASED_WHERE_TYPE_CHECK = "`%1$s`:%2$s";
|
||||
static final String WHERE_CLAUSE_0 = "`%1$s`.`%2$s` %3$s ";
|
||||
static final String SORT_CLAUSE = "%s %s";
|
||||
static final String ORDER_BY_CLAUSE = " ORDER BY %s";
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.springframework.data.neo4j.conversion.DefaultConverter;
|
||||
import org.springframework.data.neo4j.conversion.Result;
|
||||
import org.springframework.data.neo4j.conversion.ResultConverter;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.core.GraphDatabaseGlobalOperations;
|
||||
import org.springframework.data.neo4j.support.index.IndexType;
|
||||
import org.springframework.data.neo4j.support.index.NoSuchIndexException;
|
||||
import org.springframework.data.neo4j.support.query.ConversionServiceQueryResultConverter;
|
||||
@@ -56,7 +55,6 @@ public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DelegatingGraphDatabase.class);
|
||||
|
||||
protected GraphDatabaseGlobalOperations globalOperations;
|
||||
protected GraphDatabaseService delegate;
|
||||
private ConversionService conversionService;
|
||||
private ResultConverter resultConverter;
|
||||
@@ -69,21 +67,30 @@ public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
public DelegatingGraphDatabase(final GraphDatabaseService delegate, ResultConverter resultConverter) {
|
||||
this.delegate = delegate;
|
||||
this.resultConverter = resultConverter;
|
||||
this.globalOperations = new DelegatingGraphDatabaseGlobalOperations(delegate);
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphDatabaseGlobalOperations getGlobalGraphOperations() {
|
||||
return globalOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResultConverter(ResultConverter resultConverter) {
|
||||
this.resultConverter = resultConverter;
|
||||
|
||||
// At present, the current config may result in the scenario where
|
||||
// the query engine was requested very early on in the lifecycle
|
||||
// (for Type Representation Strategy) and at that stage, only the
|
||||
// default ResultConverter was available, and thus used to create
|
||||
// the query engines. (TODO - Try change ordering if possible)
|
||||
//
|
||||
// In this case we re-initialise it to ensure it uses this latest
|
||||
// result Converter (as it is currently cached)
|
||||
reinitQueryEngines();
|
||||
}
|
||||
|
||||
private void reinitQueryEngines() {
|
||||
if (cypherQueryEngine != null) this.cypherQueryEngine = queryEngineFor(QueryType.Cypher, resultConverter, true);
|
||||
if (gremlinQueryEngine != null) this.gremlinQueryEngine = queryEngineFor(QueryType.Gremlin, resultConverter, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -195,19 +202,19 @@ public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> QueryEngine<T> queryEngineFor(QueryType type,ResultConverter resultConverter) {
|
||||
private <T> QueryEngine<T> queryEngineFor(QueryType type,ResultConverter resultConverter,boolean reinit) {
|
||||
switch (type) {
|
||||
case Cypher: {
|
||||
if (cypherQueryEngine==null)
|
||||
if (reinit || cypherQueryEngine==null)
|
||||
synchronized (this) {
|
||||
if (cypherQueryEngine==null) cypherQueryEngine = createCypherQueryEngine(resultConverter);
|
||||
if (reinit || cypherQueryEngine==null) cypherQueryEngine = createCypherQueryEngine(resultConverter);
|
||||
}
|
||||
return (QueryEngine<T>) cypherQueryEngine;
|
||||
}
|
||||
case Gremlin: {
|
||||
if (gremlinQueryEngine==null) {
|
||||
if (reinit || gremlinQueryEngine==null) {
|
||||
synchronized (this) {
|
||||
if (gremlinQueryEngine==null) gremlinQueryEngine=createGremlinQueryEngine(resultConverter);
|
||||
if (reinit || gremlinQueryEngine==null) gremlinQueryEngine=createGremlinQueryEngine(resultConverter);
|
||||
}
|
||||
}
|
||||
return (QueryEngine<T>) gremlinQueryEngine;
|
||||
@@ -216,6 +223,11 @@ public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
throw new IllegalArgumentException("Unknown Query Engine Type "+type);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> QueryEngine<T> queryEngineFor(QueryType type,ResultConverter resultConverter) {
|
||||
return queryEngineFor(type,resultConverter,false);
|
||||
}
|
||||
|
||||
private <T> QueryEngine<T> createGremlinQueryEngine(ResultConverter resultConverter) {
|
||||
if (!ClassUtils.isPresent("com.tinkerpop.blueprints.pgm.impls.neo4j.Neo4jGraph", getClass().getClassLoader())) {
|
||||
return new FailingQueryEngine<T>("Gremlin");
|
||||
|
||||
@@ -42,6 +42,10 @@ public class CypherQueryEngine implements QueryEngine<Map<String,Object>> {
|
||||
this.executionEngine = new ExecutionEngine(graphDatabaseService);
|
||||
}
|
||||
|
||||
public ResultConverter getResultConverter() {
|
||||
return resultConverter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Result<Map<String, Object>> query(String statement, Map<String, Object> params) {
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* 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.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.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.core.GraphDatabaseGlobalOperations;
|
||||
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.neo4j.support.mapping.ResourceIterableClosableIterable;
|
||||
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 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
|
||||
*/
|
||||
public class CoreAPIBasedLabelingNodeTypeRepresentationStrategy implements NodeTypeRepresentationStrategy {
|
||||
|
||||
// option A : store type in entity
|
||||
// public static final String TYPE_PROPERTY_NAME = "__type__";
|
||||
// option B : add special type label
|
||||
public static final String TYPE_LABEL_PREFIX = "__TYPE__";
|
||||
protected GraphDatabase graphDb;
|
||||
protected final Class<Node> clazz;
|
||||
|
||||
public CoreAPIBasedLabelingNodeTypeRepresentationStrategy(GraphDatabase graphDb) {
|
||||
this.graphDb = graphDb;
|
||||
this.clazz = Node.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTypeTo(Node state, StoredEntityType type) {
|
||||
if (type == null || !type.isNodeEntity()) return;
|
||||
|
||||
ResourceIterable<Label> labels = state.getLabels();
|
||||
if (labels.iterator().hasNext()) {
|
||||
return; // already there
|
||||
}
|
||||
|
||||
addLabel(state,type,true);
|
||||
for (StoredEntityType superType : type.getSuperTypes()) {
|
||||
addLabel(state, superType, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void addLabel(Node state, StoredEntityType type, boolean isPrimary) {
|
||||
String alias = type.getAlias().toString();
|
||||
state.addLabel(DynamicLabel.label(alias));
|
||||
if (isPrimary) {
|
||||
// option A : store type in entity
|
||||
// state.setProperty(TYPE_PROPERTY_NAME,alias);
|
||||
|
||||
// option B : add special type label
|
||||
state.addLabel(DynamicLabel.label(TYPE_LABEL_PREFIX + alias));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> ClosableIterable<Node> findAll(StoredEntityType type) {
|
||||
GraphDatabaseGlobalOperations globalOps = graphDb.getGlobalGraphOperations();
|
||||
final ResourceIterable<Node> rin = globalOps.getAllNodesWithLabel(DynamicLabel.label(type.getAlias().toString()));
|
||||
return new ResourceIterableClosableIterable(rin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(StoredEntityType type) {
|
||||
long count = 0;
|
||||
GraphDatabaseGlobalOperations globalOps = graphDb.getGlobalGraphOperations();
|
||||
for (Node n : globalOps.getAllNodesWithLabel(DynamicLabel.label(type.getAlias().toString()))) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readAliasFrom(Node state) {
|
||||
if (state == null)
|
||||
throw new IllegalArgumentException("Node is null");
|
||||
|
||||
// Option A: Derive alias from property
|
||||
// return state.getProperty(TYPE_PROPERTY_NAME);
|
||||
|
||||
// Option B: Derive alias from special Label
|
||||
for (Label label: state.getLabels()) {
|
||||
if (label.name().startsWith(TYPE_LABEL_PREFIX)) {
|
||||
return label.name().substring(TYPE_LABEL_PREFIX.length());
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No primary SDN label exists .. (i.e one with SDN_) ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preEntityRemoval(Node state) {
|
||||
// don't think we need to do anything here!
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.springframework.data.neo4j.support.typerepresentation;
|
||||
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.springframework.data.neo4j.conversion.Result;
|
||||
import org.springframework.data.neo4j.repository.query.CypherQuery;
|
||||
import org.springframework.data.neo4j.support.query.QueryEngine;
|
||||
|
||||
@@ -45,7 +46,8 @@ public class LabelBasedStrategyCypherHelper {
|
||||
|
||||
public boolean doesNodeHaveLabel(Long nodeId, String label) {
|
||||
String query = String.format(CYPHER_COUNT_LABELS_ON_NODE, label);
|
||||
long labelCount = queryEngine.query(query, getParamsWithNodeId(nodeId)).to(Long.class).single();
|
||||
Result<CypherQuery> result = queryEngine.query(query, getParamsWithNodeId(nodeId));
|
||||
long labelCount = result.to(Number.class).single().longValue();
|
||||
return labelCount > 0;
|
||||
}
|
||||
|
||||
@@ -72,7 +74,8 @@ public class LabelBasedStrategyCypherHelper {
|
||||
|
||||
public long countNodesWithLabel(String label) {
|
||||
String query = String.format(CYPHER_RETURN_COUNT_OF_NODES_WITH_LABEL, label);
|
||||
return queryEngine.query(query, Collections.EMPTY_MAP).to(Long.class).single();
|
||||
Result<CypherQuery> result = queryEngine.query(query, Collections.EMPTY_MAP);
|
||||
return result.to(Number.class).single().longValue();
|
||||
}
|
||||
|
||||
public Iterable<String> getLabelsForNode(long nodeId) {
|
||||
|
||||
@@ -56,7 +56,7 @@ public class TypeRepresentationStrategyFactory {
|
||||
if (SubReferenceNodeTypeRepresentationStrategy.isStrategyAlreadyInUse(graphDatabaseService)) return Strategy.SubRef;
|
||||
if (LabelBasedNodeTypeRepresentationStrategy.isStrategyAlreadyInUse(graphDatabaseService)) return Strategy.Labeled;
|
||||
tx.success();
|
||||
return Strategy.Indexed;
|
||||
return Strategy.Labeled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class TypeRepresentationStrategyFactory {
|
||||
|
||||
@Override
|
||||
public RelationshipTypeRepresentationStrategy getRelationshipTypeRepresentationStrategy(GraphDatabase graphDatabaseService, IndexProvider indexProvider) {
|
||||
return new NoopRelationshipTypeRepresentationStrategy();
|
||||
return new IndexBasedRelationshipTypeRepresentationStrategy(graphDatabaseService, indexProvider);
|
||||
}
|
||||
},
|
||||
Indexed {
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* 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.repository.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.neo4j.repository.query.CypherQueryBuilder;
|
||||
import org.springframework.data.neo4j.repository.query.Person;
|
||||
import org.springframework.data.neo4j.support.Infrastructure;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.support.typerepresentation.IndexBasedNodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Base Unit test definitions for {@link org.springframework.data.neo4j.repository.query.CypherQueryBuilder}.
|
||||
* Depending on which Type Representation Strategy is in use, the queries will look
|
||||
* different, thus there are currently two subclasses which deal with each case.
|
||||
* TODO - There is some consolidation which can still be done here
|
||||
*
|
||||
* @author Oliver Gierke & Nicki Watt
|
||||
*/
|
||||
public abstract class AbstractCypherQueryBuilderTestBase {
|
||||
|
||||
CypherQueryBuilder query;
|
||||
String queryString;
|
||||
final static String CLASS_NAME = Person.class.getSimpleName();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Neo4jMappingContext context = new Neo4jMappingContext();
|
||||
Neo4jTemplate template = Mockito.mock(Neo4jTemplate.class);
|
||||
Infrastructure inf = Mockito.mock(Infrastructure.class);
|
||||
when (template.getInfrastructure()).thenReturn(inf);
|
||||
when (inf.getNodeTypeRepresentationStrategy()).thenReturn(getNodeTypeRepresentationStrategy());
|
||||
query = new CypherQueryBuilder(context, Person.class, template);
|
||||
queryString = null;
|
||||
}
|
||||
|
||||
abstract NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy();
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForSimplePropertyReference()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForSimplePropertyReference();
|
||||
protected void buildQueryForCreatesQueryForSimplePropertyReference()
|
||||
{
|
||||
Part part = new Part("name", Person.class);
|
||||
query.addRestriction(part);
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForLikePropertyIndex()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForLikePropertyIndex();
|
||||
protected void buildQueryForCreatesQueryForLikePropertyIndex() {
|
||||
Part part = new Part("titleLike", Person.class);
|
||||
query.addRestriction(part);
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForLikeProperty()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForLikeProperty();
|
||||
public void buildQueryForCreatesQueryForLikeProperty() {
|
||||
Part part = new Part("infoLike", Person.class);
|
||||
query.addRestriction(part);
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForGreaterThanPropertyReference()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForGreaterThanPropertyReference();
|
||||
public void buildQueryForCreatesQueryForGreaterThanPropertyReference() {
|
||||
Part part = new Part("ageGreaterThan", Person.class);
|
||||
query.addRestriction(part);
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForTwoPropertyExpressions()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForTwoPropertyExpressions();
|
||||
public void buildQueryForCreatesQueryForTwoPropertyExpressions() {
|
||||
query.addRestriction(new Part("ageGreaterThan", Person.class));
|
||||
query.addRestriction(new Part("info", Person.class));
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForIsNullPropertyReference()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForIsNullPropertyReference();
|
||||
public void buildQueryForCreatesQueryForIsNullPropertyReference() {
|
||||
Part part = new Part("ageIsNull", Person.class);
|
||||
query.addRestriction(part);
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForPropertyOnRelationShipReference()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForPropertyOnRelationShipReference();
|
||||
public void buildQueryForCreatesQueryForPropertyOnRelationShipReference() {
|
||||
Part part = new Part("group.name", Person.class);
|
||||
query.addRestriction(part);
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesQueryForMultipleStartClauses()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsQueryForMultipleStartClauses();
|
||||
public void buildQueryForCreatesQueryForMultipleStartClauses() {
|
||||
query.addRestriction(new Part("name", Person.class));
|
||||
query.addRestriction(new Part("group.name", Person.class));
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesSimpleWhereClauseCorrectly()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsSimpleWhereClauseCorrectly();
|
||||
public void buildQueryForCreatesSimpleWhereClauseCorrectly() {
|
||||
query.addRestriction(new Part("age", Person.class));
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForCreatesSimpleTraversalClauseCorrectly()
|
||||
*/
|
||||
@Test
|
||||
public abstract void createsSimpleTraversalClauseCorrectly();
|
||||
public void buildQueryForCreatesSimpleTraversalClauseCorrectly() {
|
||||
query.addRestriction(new Part("group", Person.class));
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForBuildsComplexQueryCorrectly()
|
||||
*/
|
||||
@Test
|
||||
public abstract void buildsComplexQueryCorrectly();
|
||||
public void buildQueryForBuildsComplexQueryCorrectly() {
|
||||
query.addRestriction(new Part("name", Person.class));
|
||||
query.addRestriction(new Part("groupName", Person.class));
|
||||
query.addRestriction(new Part("ageGreaterThan", Person.class));
|
||||
query.addRestriction(new Part("groupMembersAge", Person.class));
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForBuildsQueryWithSort()
|
||||
*/
|
||||
@Test
|
||||
public abstract void buildsQueryWithSort();
|
||||
public void buildQueryForBuildsQueryWithSort() {
|
||||
query.addRestriction(new Part("name",Person.class));
|
||||
queryString = query.buildQuery(new Sort("person.name")).toQueryString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForBuildsQueryWithSort()
|
||||
*/
|
||||
@Test
|
||||
public abstract void buildsQueryWithTwoSorts();
|
||||
public void buildQueryForBuildsQueryWithTwoSorts() {
|
||||
query.addRestriction(new Part("name",Person.class));
|
||||
Sort sort = new Sort(new Sort.Order("person.name"),new Sort.Order(Sort.Direction.DESC, "person.age"));
|
||||
queryString = query.buildQuery(sort).toQueryString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForBuildsQueryWithSort()
|
||||
*/
|
||||
@Test
|
||||
public abstract void buildsQueryWithPage();
|
||||
public void buildQueryForBuildsQueryWithPage() {
|
||||
query.addRestriction(new Part("name",Person.class));
|
||||
Pageable pageable = new PageRequest(3,10,new Sort("person.name"));
|
||||
queryString = query.buildQuery().toQueryString(pageable);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForShouldFindByNodeEntityForIncomingRelationship()
|
||||
*/
|
||||
@Test
|
||||
public abstract void shouldFindByNodeEntity() throws Exception;
|
||||
public void buildQueryForShouldFindByNodeEntity() throws Exception {
|
||||
query.addRestriction(new Part("pet", Person.class));
|
||||
queryString = query.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be used in conjunction with
|
||||
* buildQueryForShouldFindByNodeEntityForIncomingRelationship()
|
||||
*/
|
||||
@Test
|
||||
public abstract void shouldFindByNodeEntityForIncomingRelationship();
|
||||
public void buildQueryForShouldFindByNodeEntityForIncomingRelationship() {
|
||||
query.addRestriction(new Part("group", Person.class));
|
||||
queryString = query.toString();
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.neo4j.repository.query;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.index.lucene.ValueContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.neo4j.annotation.GraphId;
|
||||
import org.springframework.data.neo4j.annotation.GraphProperty;
|
||||
@@ -47,12 +48,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.neo4j.index.lucene.ValueContext;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class DerivedFinderMethodTests {
|
||||
public abstract class AbstractDerivedFinderMethodTestBase {
|
||||
|
||||
@NodeEntity
|
||||
public static class Thing {
|
||||
@@ -110,11 +106,7 @@ public class DerivedFinderMethodTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryWithEntityGraphId() throws Exception {
|
||||
assertRepositoryQueryMethod(ThingRepository.class, "findByOwnerId",new Object[]{123},
|
||||
"START `thing_owner`=node({0}) MATCH `thing`-[:`owner`]->`thing_owner` WHERE `thing`.__type__ IN ['org.springframework.data.neo4j.repository.query.DerivedFinderMethodTests$Thing'] ",
|
||||
123);
|
||||
}
|
||||
public abstract void testQueryWithEntityGraphId() throws Exception;
|
||||
|
||||
@Test
|
||||
public void testIndexQueryWithTwoParams() throws Exception {
|
||||
@@ -311,7 +303,7 @@ public class DerivedFinderMethodTests {
|
||||
ValueContext.numeric(10));
|
||||
}
|
||||
|
||||
private void assertRepositoryQueryMethod(Class<ThingRepository> repositoryClass, String methodName, Object[] paramValues, String expectedQuery, Object...expectedParam) {
|
||||
protected void assertRepositoryQueryMethod(Class<ThingRepository> repositoryClass, String methodName, Object[] paramValues, String expectedQuery, Object...expectedParam) {
|
||||
Method method = methodFor(repositoryClass, methodName);
|
||||
DerivedCypherRepositoryQuery derivedCypherRepositoryQuery = new DerivedCypherRepositoryQuery(ctx, new GraphQueryMethod(method, new DefaultRepositoryMetadata(repositoryClass), null, ctx), template);
|
||||
Parameters<?, ?> parameters = new DefaultParameters(method);
|
||||
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* 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.repository.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.neo4j.support.typerepresentation.IndexBasedNodeTypeRepresentationStrategy;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.data.neo4j.repository.query.CypherQueryBuilder}
|
||||
* Specifically where the Index Based Type Representation Strategy is being used.
|
||||
*
|
||||
* @author Oliver Gierke & Nicki Watt
|
||||
*/
|
||||
public class CypherQueryBuilderForIndexBasedTRSUnitTests extends AbstractCypherQueryBuilderTestBase {
|
||||
|
||||
private final static String DEFAULT_START_CLAUSE = "START `person`=node:__types__(className=\"" + CLASS_NAME + "\")";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
protected NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy() {
|
||||
return Mockito.mock(IndexBasedNodeTypeRepresentationStrategy.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForSimplePropertyReference() {
|
||||
buildQueryForCreatesQueryForSimplePropertyReference();
|
||||
assertThat(queryString,
|
||||
is("START `person`=node:`Person`(`name`={0}) RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForLikePropertyIndex() {
|
||||
buildQueryForCreatesQueryForLikePropertyIndex();
|
||||
assertThat(queryString, is("START `person`=node:`title`({0}) RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForLikeProperty() {
|
||||
buildQueryForCreatesQueryForLikeProperty();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`info` =~ {0} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForGreaterThanPropertyReference() {
|
||||
buildQueryForCreatesQueryForGreaterThanPropertyReference();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` > {0} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForTwoPropertyExpressions() {
|
||||
buildQueryForCreatesQueryForTwoPropertyExpressions();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` > {0} AND `person`.`info` = {1} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForIsNullPropertyReference() {
|
||||
buildQueryForCreatesQueryForIsNullPropertyReference();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` is null RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForPropertyOnRelationShipReference() {
|
||||
buildQueryForCreatesQueryForPropertyOnRelationShipReference();
|
||||
assertThat(queryString, is("START `person_group`=node:`Group`(`name`={0}) MATCH `person`<-[:`members`]-`person_group` RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForMultipleStartClauses() {
|
||||
buildQueryForCreatesQueryForMultipleStartClauses();
|
||||
assertThat(queryString,
|
||||
is("START `person`=node:`Person`(`name`={0}), `person_group`=node:`Group`(`name`={1}) MATCH `person`<-[:`members`]-`person_group` RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsSimpleWhereClauseCorrectly() {
|
||||
buildQueryForCreatesSimpleWhereClauseCorrectly();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE +" WHERE `person`.`age` = {0} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsSimpleTraversalClauseCorrectly() {
|
||||
buildQueryForCreatesSimpleTraversalClauseCorrectly();
|
||||
assertThat(queryString, is("START `person_group`=node({0}) MATCH `person`<-[:`members`]-`person_group` WHERE `person`.__type__ IN ['Person'] RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsComplexQueryCorrectly() {
|
||||
buildQueryForBuildsComplexQueryCorrectly();
|
||||
assertThat(queryString, is(
|
||||
"START `person`=node:`Person`(`name`={0}), `person_group`=node:`Group`(`name`={1}) " +
|
||||
"MATCH `person`<-[:`members`]-`person_group`, `person`<-[:`members`]-`person_group`-[:`members`]->`person_group_members` " +
|
||||
"WHERE `person`.`age` > {2} AND `person_group_members`.`age` = {3} " +
|
||||
"RETURN `person`"
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsQueryWithSort() {
|
||||
buildQueryForBuildsQueryWithSort();
|
||||
assertThat(queryString, is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsQueryWithTwoSorts() {
|
||||
buildQueryForBuildsQueryWithTwoSorts();
|
||||
assertThat(queryString, is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC,person.age DESC"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsQueryWithPage() {
|
||||
buildQueryForBuildsQueryWithPage();
|
||||
assertThat(queryString, is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC SKIP 30 LIMIT 10"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldFindByNodeEntity() throws Exception {
|
||||
buildQueryForShouldFindByNodeEntity();
|
||||
assertThat(queryString, is("START `person_pet`=node({0}) MATCH `person`-[:`owns`]->`person_pet` WHERE `person`.__type__ IN ['Person'] RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldFindByNodeEntityForIncomingRelationship() {
|
||||
buildQueryForShouldFindByNodeEntityForIncomingRelationship();
|
||||
assertThat(queryString, is("START `person_group`=node({0}) MATCH `person`<-[:`members`]-`person_group` WHERE `person`.__type__ IN ['Person'] RETURN `person`"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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.repository.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.data.neo4j.repository.query.CypherQueryBuilder}
|
||||
* Specifically where the Label Type Representation Strategy is being used.
|
||||
*
|
||||
* @author Oliver Gierke & Nicki Watt
|
||||
*/
|
||||
public class CypherQueryBuilderForLabelBasedTRSUnitTests extends AbstractCypherQueryBuilderTestBase {
|
||||
|
||||
private final static String DEFAULT_START_CLAUSE = " MATCH `person`:`" + CLASS_NAME + "`";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
protected NodeTypeRepresentationStrategy getNodeTypeRepresentationStrategy() {
|
||||
return Mockito.mock(LabelBasedNodeTypeRepresentationStrategy.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForSimplePropertyReference() {
|
||||
buildQueryForCreatesQueryForSimplePropertyReference();
|
||||
assertThat(queryString,
|
||||
is("START `person`=node:`Person`(`name`={0}) RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForLikePropertyIndex() {
|
||||
buildQueryForCreatesQueryForLikePropertyIndex();
|
||||
assertThat(queryString, is("START `person`=node:`title`({0}) RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForLikeProperty() {
|
||||
buildQueryForCreatesQueryForLikeProperty();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`info` =~ {0} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForGreaterThanPropertyReference() {
|
||||
buildQueryForCreatesQueryForGreaterThanPropertyReference();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` > {0} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForTwoPropertyExpressions() {
|
||||
buildQueryForCreatesQueryForTwoPropertyExpressions();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` > {0} AND `person`.`info` = {1} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForIsNullPropertyReference() {
|
||||
buildQueryForCreatesQueryForIsNullPropertyReference();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` is null RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForPropertyOnRelationShipReference() {
|
||||
buildQueryForCreatesQueryForPropertyOnRelationShipReference();
|
||||
assertThat(queryString, is("START `person_group`=node:`Group`(`name`={0}) MATCH `person`<-[:`members`]-`person_group` RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsQueryForMultipleStartClauses() {
|
||||
buildQueryForCreatesQueryForMultipleStartClauses();
|
||||
assertThat(queryString,
|
||||
is("START `person`=node:`Person`(" +
|
||||
"`name`={0}), " +
|
||||
"`person_group`=node:" +
|
||||
"`Group`(`name`={1}) " +
|
||||
"MATCH `person`<-[:`members`]-`person_group` " +
|
||||
"RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsSimpleWhereClauseCorrectly() {
|
||||
buildQueryForCreatesSimpleWhereClauseCorrectly();
|
||||
assertThat(queryString, is(DEFAULT_START_CLAUSE +" WHERE `person`.`age` = {0} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void createsSimpleTraversalClauseCorrectly() {
|
||||
buildQueryForCreatesSimpleTraversalClauseCorrectly();
|
||||
assertThat(queryString, is("START `person_group`=node({0}) MATCH `person`<-[:`members`]-`person_group` WHERE `person`:`Person` RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsComplexQueryCorrectly() {
|
||||
buildQueryForBuildsComplexQueryCorrectly();
|
||||
assertThat(queryString, is(
|
||||
"START `person`=node:`Person`(`name`={0}), `person_group`=node:`Group`(`name`={1}) " +
|
||||
"MATCH `person`<-[:`members`]-`person_group`, `person`<-[:`members`]-`person_group`-[:`members`]->`person_group_members` " +
|
||||
"WHERE `person`.`age` > {2} AND `person_group_members`.`age` = {3} " +
|
||||
"RETURN `person`"
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsQueryWithSort() {
|
||||
buildQueryForBuildsQueryWithSort();
|
||||
assertThat(queryString, is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsQueryWithTwoSorts() {
|
||||
buildQueryForBuildsQueryWithTwoSorts();
|
||||
assertThat(queryString, is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC,person.age DESC"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void buildsQueryWithPage() {
|
||||
buildQueryForBuildsQueryWithPage();
|
||||
assertThat(queryString, is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC SKIP 30 LIMIT 10"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldFindByNodeEntity() throws Exception {
|
||||
buildQueryForShouldFindByNodeEntity();
|
||||
assertThat(queryString, is("START `person_pet`=node({0}) MATCH `person`-[:`owns`]->`person_pet` WHERE `person`:`Person` RETURN `person`"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void shouldFindByNodeEntityForIncomingRelationship() {
|
||||
buildQueryForShouldFindByNodeEntityForIncomingRelationship();
|
||||
assertThat(queryString, is("START `person_group`=node({0}) MATCH `person`<-[:`members`]-`person_group` WHERE `person`:`Person` RETURN `person`"));
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/**
|
||||
* 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.repository.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CypherQueryBuilder}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class CypherQueryBuilderUnitTests {
|
||||
|
||||
CypherQueryBuilder query;
|
||||
private final static String CLASS_NAME = Person.class.getSimpleName();
|
||||
private final static String DEFAULT_START_CLAUSE = "START `person`=node:__types__(className=\"" + CLASS_NAME + "\")";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Neo4jMappingContext context = new Neo4jMappingContext();
|
||||
Neo4jTemplate template = Mockito.mock(Neo4jTemplate.class);
|
||||
query = new CypherQueryBuilder(context, Person.class, template);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryForSimplePropertyReference() {
|
||||
|
||||
Part part = new Part("name", Person.class);
|
||||
query.addRestriction(part);
|
||||
|
||||
assertThat(query.toString(), is("START `person`=node:`Person`(`name`={0}) RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryForLikePropertyIndex() {
|
||||
|
||||
Part part = new Part("titleLike", Person.class);
|
||||
query.addRestriction(part);
|
||||
|
||||
assertThat(query.toString(), is("START `person`=node:`title`({0}) RETURN `person`"));
|
||||
}
|
||||
@Test
|
||||
public void createsQueryForLikeProperty() {
|
||||
|
||||
Part part = new Part("infoLike", Person.class);
|
||||
query.addRestriction(part);
|
||||
|
||||
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" WHERE `person`.`info` =~ {0} RETURN `person`"));
|
||||
}
|
||||
@Test
|
||||
public void createsQueryForGreaterThanPropertyReference() {
|
||||
|
||||
Part part = new Part("ageGreaterThan", Person.class);
|
||||
query.addRestriction(part);
|
||||
|
||||
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` > {0} RETURN `person`"));
|
||||
}
|
||||
@Test
|
||||
public void createsQueryForTwoPropertyExpressions() {
|
||||
|
||||
query.addRestriction(new Part("ageGreaterThan", Person.class));
|
||||
query.addRestriction(new Part("info", Person.class));
|
||||
|
||||
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` > {0} AND `person`.`info` = {1} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryForIsNullPropertyReference() {
|
||||
|
||||
Part part = new Part("ageIsNull", Person.class);
|
||||
query.addRestriction(part);
|
||||
|
||||
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" WHERE `person`.`age` is null RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryForPropertyOnRelationShipReference() {
|
||||
|
||||
Part part = new Part("group.name", Person.class);
|
||||
query.addRestriction(part);
|
||||
|
||||
assertThat(query.toString(), is("START `person_group`=node:`Group`(`name`={0}) MATCH `person`<-[:`members`]-`person_group` RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsQueryForMultipleStartClauses() {
|
||||
|
||||
query.addRestriction(new Part("name", Person.class));
|
||||
query.addRestriction(new Part("group.name", Person.class));
|
||||
|
||||
assertThat(query.toString(),
|
||||
is("START `person`=node:`Person`(`name`={0}), `person_group`=node:`Group`(`name`={1}) MATCH `person`<-[:`members`]-`person_group` RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsSimpleWhereClauseCorrectly() {
|
||||
query.addRestriction(new Part("age", Person.class));
|
||||
assertThat(query.toString(), is(DEFAULT_START_CLAUSE +" WHERE `person`.`age` = {0} RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createsSimpleTraversalClauseCorrectly() {
|
||||
query.addRestriction(new Part("group", Person.class));
|
||||
assertThat(query.toString(), is("START `person_group`=node({0}) MATCH `person`<-[:`members`]-`person_group` WHERE `person`.__type__ IN ['Person'] RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildsComplexQueryCorrectly() {
|
||||
|
||||
query.addRestriction(new Part("name", Person.class));
|
||||
query.addRestriction(new Part("groupName", Person.class));
|
||||
query.addRestriction(new Part("ageGreaterThan", Person.class));
|
||||
query.addRestriction(new Part("groupMembersAge", Person.class));
|
||||
|
||||
assertThat(query.toString(), is(
|
||||
"START `person`=node:`Person`(`name`={0}), `person_group`=node:`Group`(`name`={1}) " +
|
||||
"MATCH `person`<-[:`members`]-`person_group`, `person`<-[:`members`]-`person_group`-[:`members`]->`person_group_members` " +
|
||||
"WHERE `person`.`age` > {2} AND `person_group_members`.`age` = {3} " +
|
||||
"RETURN `person`"
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildsQueryWithSort() {
|
||||
query.addRestriction(new Part("name",Person.class));
|
||||
assertThat(query.buildQuery(new Sort("person.name")).toQueryString(), is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC"));
|
||||
}
|
||||
@Test
|
||||
public void buildsQueryWithTwoSorts() {
|
||||
query.addRestriction(new Part("name",Person.class));
|
||||
Sort sort = new Sort(new Sort.Order("person.name"),new Sort.Order(Sort.Direction.DESC, "person.age"));
|
||||
assertThat(query.buildQuery(sort).toQueryString(), is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC,person.age DESC"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildsQueryWithPage() {
|
||||
query.addRestriction(new Part("name",Person.class));
|
||||
Pageable pageable = new PageRequest(3,10,new Sort("person.name"));
|
||||
assertThat(query.buildQuery().toQueryString(pageable), is("START `person`=node:`Person`(`name`={0}) RETURN `person` ORDER BY person.name ASC SKIP 30 LIMIT 10"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindByNodeEntity() throws Exception {
|
||||
query.addRestriction(new Part("pet", Person.class));
|
||||
|
||||
assertThat(query.toString(), is("START `person_pet`=node({0}) MATCH `person`-[:`owns`]->`person_pet` WHERE `person`.__type__ IN ['Person'] RETURN `person`"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindByNodeEntityForIncomingRelationship() {
|
||||
query.addRestriction(new Part("group", Person.class));
|
||||
|
||||
assertThat(query.toString(), is("START `person_group`=node({0}) MATCH `person`<-[:`members`]-`person_group` WHERE `person`.__type__ IN ['Person'] RETURN `person`"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.repository.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
|
||||
import org.springframework.data.neo4j.support.typerepresentation.IndexBasedNodeTypeRepresentationStrategy;
|
||||
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.TransactionalTestExecutionListener;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
/**
|
||||
* Tests for the various finder method based scenarios
|
||||
* , specifically where the Index Type Representation Strategy is being used.
|
||||
*
|
||||
* @author Oliver Gierke & Nicki Watt
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class DerivedFinderMethodForIndexedBasedTRSTests extends AbstractDerivedFinderMethodTestBase {
|
||||
|
||||
@Autowired
|
||||
NodeTypeRepresentationStrategy strategy;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
assertThat("The tests in this class should be configured to use the Label " +
|
||||
"based Type Representation Strategy, however it is not ... ",
|
||||
strategy, instanceOf(IndexBasedNodeTypeRepresentationStrategy.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testQueryWithEntityGraphId() throws Exception {
|
||||
assertRepositoryQueryMethod(ThingRepository.class, "findByOwnerId",new Object[]{123},
|
||||
"START `thing_owner`=node({0}) MATCH `thing`-[:`owner`]->`thing_owner` WHERE `thing`.__type__ IN ['org.springframework.data.neo4j.repository.query.AbstractDerivedFinderMethodTestBase$Thing'] ",
|
||||
123);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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.repository.query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.neo4j.core.NodeTypeRepresentationStrategy;
|
||||
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.TransactionalTestExecutionListener;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
||||
/**
|
||||
* Tests for the various finder method based scenarios
|
||||
* , specifically where the Label Type Representation Strategy is being used.
|
||||
*
|
||||
* @author Oliver Gierke & Nicki Watt
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class DerivedFinderMethodForLabelBasedTRSTests extends AbstractDerivedFinderMethodTestBase {
|
||||
|
||||
@Autowired
|
||||
NodeTypeRepresentationStrategy strategy;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
assertThat("The tests in this class should be configured to use the Label " +
|
||||
"based Type Representation Strategy, however it is not ... ",
|
||||
strategy, instanceOf(LabelBasedNodeTypeRepresentationStrategy.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testQueryWithEntityGraphId() throws Exception {
|
||||
assertRepositoryQueryMethod(ThingRepository.class, "findByOwnerId",new Object[]{123},
|
||||
"START `thing_owner`=node({0}) MATCH `thing`-[:`owner`]->`thing_owner` WHERE `thing`:`org.springframework.data.neo4j.repository.query.AbstractDerivedFinderMethodTestBase$Thing` ",
|
||||
123);
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,9 @@ import org.springframework.data.neo4j.repository.GraphRepository;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.springframework.data.neo4j.repository.query.DerivedFinderMethodTests.Thing;
|
||||
import static org.springframework.data.neo4j.repository.query.AbstractDerivedFinderMethodTestBase.Thing;
|
||||
|
||||
public interface ThingRepository extends GraphRepository<DerivedFinderMethodTests.Thing> {
|
||||
public interface ThingRepository extends GraphRepository<AbstractDerivedFinderMethodTestBase.Thing> {
|
||||
Thing findByFirstNameAndLastName(String firstName, String lastName);
|
||||
Thing findByFirstName(String firstName);
|
||||
Thing findByDescription(String firstName);
|
||||
|
||||
@@ -26,8 +26,12 @@ import org.springframework.data.neo4j.conversion.ResultConverter;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.test.DocumentingTestBase;
|
||||
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.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
@@ -35,10 +39,16 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.neo4j.helpers.collection.MapUtil.map;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath*:DocumentingTests-context.xml"})
|
||||
@ContextConfiguration(locations = {"classpath:DocumentingTests-context.xml"})
|
||||
@TestExecutionListeners({
|
||||
CleanContextCacheTestExecutionListener.class,
|
||||
DependencyInjectionTestExecutionListener.class,
|
||||
TransactionalTestExecutionListener.class})
|
||||
public class SnippetNeo4jTemplateMethodsTests extends DocumentingTestBase {
|
||||
|
||||
@Autowired
|
||||
private GraphDatabase graphDatabase;
|
||||
private Neo4jTemplate neo;
|
||||
|
||||
private static final RelationshipType WORKS_WITH = DynamicRelationshipType.withName("WORKS_WITH");
|
||||
|
||||
@Test
|
||||
@@ -54,8 +64,8 @@ public class SnippetNeo4jTemplateMethodsTests extends DocumentingTestBase {
|
||||
snippet = "template";
|
||||
|
||||
// SNIPPET template
|
||||
// TODO auto-post-construct !!
|
||||
final Neo4jTemplate neo = new Neo4jTemplate(graphDatabase);
|
||||
|
||||
// Injected Neo4jTemplate to make this test work. Impact for snippet?
|
||||
|
||||
Node mark = neo.createNode(map("name", "Mark"));
|
||||
Node thomas = neo.createNode(map("name", "Thomas"));
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
|
||||
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
|
||||
<context:annotation-config/>
|
||||
|
||||
<bean id="typeRepresentationStrategyFactory" class="org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory">
|
||||
<constructor-arg index="0" ref="graphDatabase"/>
|
||||
<constructor-arg index="1" value="Indexed"/>
|
||||
</bean>
|
||||
|
||||
<neo4j:config graphDatabaseService="graphDatabaseService"/>
|
||||
<neo4j:repositories base-package="org.springframework.data.neo4j.repository.query"/>
|
||||
<bean id="graphDatabaseService" class="org.neo4j.test.ImpermanentGraphDatabase" destroy-method="shutdown"/>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
|
||||
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd">
|
||||
<context:annotation-config/>
|
||||
|
||||
<bean id="typeRepresentationStrategyFactory" class="org.springframework.data.neo4j.support.typerepresentation.TypeRepresentationStrategyFactory">
|
||||
<constructor-arg index="0" ref="graphDatabase"/>
|
||||
<constructor-arg index="1" value="Labeled"/>
|
||||
</bean>
|
||||
|
||||
<neo4j:config graphDatabaseService="graphDatabaseService"/>
|
||||
<neo4j:repositories base-package="org.springframework.data.neo4j.repository.query"/>
|
||||
<bean id="graphDatabaseService" class="org.neo4j.test.ImpermanentGraphDatabase" destroy-method="shutdown"/>
|
||||
</beans>
|
||||
Reference in New Issue
Block a user