DATAGRAPH-169 Backquoting all variable parts of derived finder queries to accommodate for non-identifier names.

This commit is contained in:
Michael Hunger
2012-01-10 23:51:06 +01:00
parent 08a105cd59
commit 788998b95f
7 changed files with 59 additions and 47 deletions

View File

@@ -32,7 +32,7 @@ import java.util.Set;
@NodeEntity
public class Person {
public static final String NAME_INDEX = "name_index";
public static final String NAME_INDEX = "name-index";
@GraphId
private Long graphId;

View File

@@ -91,7 +91,7 @@ public class QueryEngineTest extends EntityTestBase {
@Test
public void testQueryListOfTypeNode() throws Exception {
final String queryString = "start person=node:name_index(name={name}) match (person) <-[:boss]- (boss) return boss";
final String queryString = "start person=node:`name-index`(name={name}) match (person) <-[:boss]- (boss) return boss";
final Result<Map<String,Object>> queryResult = queryEngine.query(queryString, michaelsName());
final Collection<Node> result = IteratorUtil.asCollection(queryResult.to(Node.class));
@@ -100,7 +100,7 @@ public class QueryEngineTest extends EntityTestBase {
@SuppressWarnings("unchecked")
@Test
public void testQueryListOfTypePerson() throws Exception {
final String queryString = "start person=node:name_index(name={name}) match (person) <-[:boss]- (boss) return boss";
final String queryString = "start person=node:`name-index`(name={name}) match (person) <-[:boss]- (boss) return boss";
final Collection<Person> result = IteratorUtil.asCollection(queryEngine.query(queryString, michaelsName()).to(Person.class, new EntityResultConverter(conversionService, template)));
assertEquals(asList(testTeam.emil),result);
@@ -112,7 +112,7 @@ public class QueryEngineTest extends EntityTestBase {
@Test
public void testQuerySingleOfTypePerson() throws Exception {
final String queryString = "start person=node:name_index(name={name}) match (person) <-[:boss]- (boss) return boss";
final String queryString = "start person=node:`name-index`(name={name}) match (person) <-[:boss]- (boss) return boss";
final Person result = queryEngine.query(queryString, michaelsName()).to(Person.class, new EntityResultConverter<Map<String,Object>,Person>(conversionService, template)).single();
assertEquals(testTeam.emil,result);
@@ -195,7 +195,7 @@ public class QueryEngineTest extends EntityTestBase {
}
@Test
public void testQueryListWithCustomConverter() throws Exception {
final String queryString = "start person=node:name_index(name={name}) match (person) <-[:boss]- (boss) return boss";
final String queryString = "start person=node:`name-index`(name={name}) match (person) <-[:boss]- (boss) return boss";
final Collection<String> result = IteratorUtil.asCollection(queryEngine.query(queryString, michaelsName()).to(String.class, new ResultConverter.ResultConverterAdapter<Map<String, Object>, String>() {
@Override
public String convert(Map<String, Object> row, Class<String> target) {
@@ -208,14 +208,14 @@ public class QueryEngineTest extends EntityTestBase {
@Test
public void testQueryForObjectAsString() throws Exception {
final String queryString = "start person=node:name_index(name={name}) match (person) <-[:persons]- (team) return team.name";
final String queryString = "start person=node:`name-index`(name={name}) match (person) <-[:persons]- (team) return team.name";
final String result = queryEngine.query(queryString, michaelsName()).to(String.class).single();
assertEquals(testTeam.sdg.getName(),result);
}
@Test
public void testQueryForObjectAsEnum() throws Exception {
final String queryString = "start person=node:name_index(name={name}) return person.personality";
final String queryString = "start person=node:`name-index`(name={name}) return person.personality";
final Personality result = queryEngine.query(queryString, michaelsName()).to(Personality.class).single();
assertEquals(michael.getPersonality(),result);

View File

@@ -15,11 +15,6 @@
*/
package org.springframework.data.neo4j.repository.query;
import static org.springframework.util.StringUtils.*;
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.context.MappingContext;
@@ -29,6 +24,11 @@ import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.List;
import static org.springframework.util.StringUtils.*;
/**
* Value object to create Cypher queries.
*
@@ -133,7 +133,7 @@ class CypherQueryBuilder implements CypherQueryDefinition {
builder.append(" where ").append(whereClauses);
}
builder.append(" return ").append(variableContext.getVariableFor(entity));
builder.append(" return ").append(String.format(QueryTemplates.VARIABLE, variableContext.getVariableFor(entity)));
return builder.toString();
}

View File

@@ -65,11 +65,20 @@ class MatchClause {
private String matchPattern(VariableContext variableContext, PersistentPropertyPath<Neo4jPersistentProperty> relPath) {
if (relPath.getLength() == 1) {
final Neo4jPersistentProperty property = relPath.getBaseProperty();
return variableContext.getVariableFor(property.getOwner()) + QueryTemplates.getArrow(property.getRelationshipInfo())
+ variableContext.getVariableFor(relPath);
return formatMatch(variableContext.getVariableFor(property.getOwner()),
QueryTemplates.getArrow(property.getRelationshipInfo()),
variableContext.getVariableFor(relPath));
}
final RelationshipInfo info = relPath.getLeafProperty().getRelationshipInfo();
return matchPattern(variableContext, relPath.getParentPath()) + QueryTemplates.getArrow(info)
+ variableContext.getVariableFor(relPath);
return formatMatch2(matchPattern(variableContext, relPath.getParentPath()),
QueryTemplates.getArrow(info),
variableContext.getVariableFor(relPath));
}
private String formatMatch(String first, String arrow, String second) {
return String.format(QueryTemplates.MATCH_CLAUSE, first, arrow, second);
}
private String formatMatch2(String first, String arrow, String second) {
return String.format(QueryTemplates.MATCH_CLAUSE2, first, arrow, second);
}
}

View File

@@ -27,18 +27,21 @@ public abstract class QueryTemplates {
public static final String PARAMETER = "%d";
public static final String PARAMETER_INDEX_QUERY = "%s:%s";
public static final String VARIABLE = "`%s`";
public static final String PLACEHOLDER = String.format("{%s}", PARAMETER);
private static final String DIRECTION_INCOMING = "<-[:%s]-";
private static final String DIRECTION_OUTGOING = "-[:%s]->";
private static final String DIRECTION_BOTH = "-[:%s]-";
private static final String DIRECTION_INCOMING = "<-[:`%s`]-";
private static final String DIRECTION_OUTGOING = "-[:`%s`]->";
private static final String DIRECTION_BOTH = "-[:`%s`]-";
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_START_CLAUSE = "`%s`=node:__types__(className=\"%s\")";
static final String SKIP_LIMIT = " skip %d limit %d";
static final String START_CLAUSE = "%s=node:%s(%s=" + PLACEHOLDER + ")";
static final String START_CLAUSE_FULLTEXT = "%s=node:%s(" + PLACEHOLDER + ")";
static final String WHERE_CLAUSE_1 = "%s.%s %s " + PLACEHOLDER;
static final String WHERE_CLAUSE_0 = "%s.%s %s ";
static final String START_CLAUSE = "`%s`=node:`%s`(`%s`=" + PLACEHOLDER + ")";
static final String START_CLAUSE_FULLTEXT = "`%s`=node:`%s`(" + PLACEHOLDER + ")";
static final String WHERE_CLAUSE_1 = "`%s`.`%s` %s " + PLACEHOLDER;
static final String WHERE_CLAUSE_0 = "`%s`.`%s` %s ";
static final String SORT_CLAUSE = "%s %s";
static final String ORDER_BY_CLAUSE = " order by %s";

View File

@@ -32,7 +32,7 @@ import java.util.Set;
@NodeEntity
public class Person {
public static final String NAME_INDEX = "name_index";
public static final String NAME_INDEX = "name-index";
public static final org.neo4j.graphdb.RelationshipType KNOWS = DynamicRelationshipType.withName("knows");
@GraphId
private Long graphId;

View File

@@ -15,9 +15,6 @@
*/
package org.springframework.data.neo4j.repository.query;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
@@ -26,6 +23,9 @@ import org.springframework.data.domain.Sort;
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}.
*
@@ -35,7 +35,7 @@ public class CypherQueryBuilderUnitTests {
CypherQueryBuilder query;
private final String CLASS_NAME = Person.class.getName();
private String DEFAULT_START_CLAUSE = "start person=node:__types__(className=\"" + CLASS_NAME + "\")";
private String DEFAULT_START_CLAUSE = "start `person`=node:__types__(className=\"" + CLASS_NAME + "\")";
@Before
public void setUp() {
@@ -49,7 +49,7 @@ public class CypherQueryBuilderUnitTests {
Part part = new Part("name", Person.class);
query.addRestriction(part);
assertThat(query.toString(), is("start person=node:Person(name={0}) return person"));
assertThat(query.toString(), is("start `person`=node:`Person`(`name`={0}) return `person`"));
}
@Test
@@ -58,7 +58,7 @@ public class CypherQueryBuilderUnitTests {
Part part = new Part("titleLike", Person.class);
query.addRestriction(part);
assertThat(query.toString(), is("start person=node:title({0}) return person"));
assertThat(query.toString(), is("start `person`=node:`title`({0}) return `person`"));
}
@Test
public void createsQueryForLikeProperty() {
@@ -66,7 +66,7 @@ public class CypherQueryBuilderUnitTests {
Part part = new Part("infoLike", Person.class);
query.addRestriction(part);
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" where person.info =~ {0} return person"));
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" where `person`.`info` =~ {0} return `person`"));
}
@Test
public void createsQueryForGreaterThanPropertyReference() {
@@ -74,7 +74,7 @@ public class CypherQueryBuilderUnitTests {
Part part = new Part("ageGreaterThan", Person.class);
query.addRestriction(part);
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" where person.age > {0} return person"));
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" where `person`.`age` > {0} return `person`"));
}
@Test
public void createsQueryForTwoPropertyExpressions() {
@@ -82,7 +82,7 @@ public class CypherQueryBuilderUnitTests {
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"));
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" where `person`.`age` > {0} and `person`.`info` = {1} return `person`"));
}
@Test
@@ -91,7 +91,7 @@ public class CypherQueryBuilderUnitTests {
Part part = new Part("ageIsNull", Person.class);
query.addRestriction(part);
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" where person.age is null return person"));
assertThat(query.toString(), is(DEFAULT_START_CLAUSE+" where `person`.`age` is null return `person`"));
}
@Test
@@ -100,7 +100,7 @@ public class CypherQueryBuilderUnitTests {
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"));
assertThat(query.toString(), is("start `person_group`=node:`Group`(`name`={0}) match `person`<-[:`members`]-`person_group` return `person`"));
}
@Test
@@ -110,7 +110,7 @@ public class CypherQueryBuilderUnitTests {
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"));
is("start `person`=node:`Person`(`name`={0}), `person_group`=node:`Group`(`name`={1}) match `person`<-[:`members`]-`person_group` return `person`"));
}
@Test
@@ -119,12 +119,12 @@ public class CypherQueryBuilderUnitTests {
query.addRestriction(new Part("age", Person.class));
final String className = Person.class.getName();
assertThat(query.toString(), is(DEFAULT_START_CLAUSE +" where person.age = {0} return person"));
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(DEFAULT_START_CLAUSE + " match person<-[:members]-person_group return person"));
assertThat(query.toString(), is(DEFAULT_START_CLAUSE + " match `person`<-[:`members`]-`person_group` return `person`"));
}
@@ -138,29 +138,29 @@ public class CypherQueryBuilderUnitTests {
System.out.println(query.toString());
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"
"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.toString(new Sort("person.name")), is("start person=node:Person(name={0}) return person order by person.name ASC"));
assertThat(query.toString(new Sort("person.name")), 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.toString(sort), is("start person=node:Person(name={0}) return person order by person.name ASC,person.age DESC"));
assertThat(query.toString(sort), 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.toString(pageable), is("start person=node:Person(name={0}) return person order by person.name ASC skip 30 limit 10"));
assertThat(query.toString(pageable), is("start `person`=node:`Person`(`name`={0}) return `person` order by person.name ASC skip 30 limit 10"));
}
}