@@ -490,13 +490,18 @@ public enum CypherGenerator {
|
||||
if (LOOKS_LIKE_A_FUNCTION.matcher(property).matches()) {
|
||||
expression = Cypher.raw(property);
|
||||
} else if (property.contains(".")) {
|
||||
String[] path = property.split("\\.");
|
||||
if (path.length != 2) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot handle order property `%s`, it must be a simple property or one-hop path.",
|
||||
property));
|
||||
int firstDot = property.indexOf('.');
|
||||
String tail = property.substring(firstDot + 1);
|
||||
if (tail.isEmpty() || property.lastIndexOf(".") != firstDot) {
|
||||
if (tail.trim().matches("`.+`")) {
|
||||
tail = tail.replaceFirst("`(.+)`", "$1");
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot handle order property `%s`, it must be a simple property or one-hop path.",
|
||||
property));
|
||||
}
|
||||
}
|
||||
expression = Cypher.property(path[0], path[1]);
|
||||
expression = Cypher.property(property.substring(0, firstDot), tail);
|
||||
} else {
|
||||
expression = Cypher.name(property);
|
||||
}
|
||||
|
||||
@@ -176,6 +176,13 @@ class CypherGeneratorTest {
|
||||
.withMessageMatching("Cannot handle order property `.*`, it must be a simple property or one-hop path\\.");
|
||||
}
|
||||
|
||||
@Test // GH-2474
|
||||
void shouldNotFailOnMultipleEscapedHops() {
|
||||
|
||||
Optional<String> fragment = Optional.ofNullable(CypherGenerator.INSTANCE.createOrderByFragment(Sort.by("n.`a.b.c`")));
|
||||
assertThat(fragment).hasValue("ORDER BY n.`a.b.c` ASC");
|
||||
}
|
||||
|
||||
@CsvSource(delimiterString = "|", value = {
|
||||
"apoc.text.clean(department.name) |false| ORDER BY apoc.text.clean(department.name) ASC",
|
||||
"apoc.text.clean(department.name) |true | ORDER BY apoc.text.clean(department.name) DESC",
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2011-2022 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
|
||||
*
|
||||
* https://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.integration.issues.gh2474;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.GeneratedValue;
|
||||
import org.springframework.data.neo4j.core.schema.Id;
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
import org.springframework.data.neo4j.core.schema.Property;
|
||||
import org.springframework.data.neo4j.core.schema.Relationship;
|
||||
|
||||
/**
|
||||
* @author Stephen Jackson
|
||||
*/
|
||||
@Node
|
||||
@Data
|
||||
public class CityModel {
|
||||
@Id
|
||||
@GeneratedValue(generatorClass = GeneratedValue.UUIDGenerator.class)
|
||||
private UUID cityId;
|
||||
|
||||
@Relationship(value = "MAYOR")
|
||||
private PersonModel mayor;
|
||||
|
||||
@Relationship(value = "CITIZEN")
|
||||
private List<PersonModel> citizens = new ArrayList<>();
|
||||
|
||||
@Relationship(value = "EMPLOYEE")
|
||||
private List<JobRelationship> cityEmployees = new ArrayList<>();
|
||||
|
||||
private String name;
|
||||
|
||||
@Property("exotic.property")
|
||||
private String exoticProperty;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2011-2022 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
|
||||
*
|
||||
* https://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.integration.issues.gh2474;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.repository.Neo4jRepository;
|
||||
import org.springframework.data.neo4j.repository.query.Query;
|
||||
|
||||
/**
|
||||
* @author Stephen Jackson
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
public interface CityModelRepository extends Neo4jRepository<CityModel, UUID> {
|
||||
|
||||
@Query(""
|
||||
+ "MATCH (n:CityModel)"
|
||||
+ "RETURN n :#{orderBy(#sort)}")
|
||||
List<CityModel> customQuery(Sort sort);
|
||||
|
||||
long deleteAllByExoticProperty(String property);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2011-2022 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
|
||||
*
|
||||
* https://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.integration.issues.gh2474;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.neo4j.config.AbstractNeo4jConfig;
|
||||
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
|
||||
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
|
||||
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
|
||||
import org.springframework.data.neo4j.test.BookmarkCapture;
|
||||
import org.springframework.data.neo4j.test.Neo4jExtension;
|
||||
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* @author Stephen Jackson
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Neo4jIntegrationTest
|
||||
public class GH2474IT {
|
||||
|
||||
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
|
||||
|
||||
@Autowired
|
||||
Driver driver;
|
||||
|
||||
@Autowired
|
||||
BookmarkCapture bookmarkCapture;
|
||||
|
||||
@Autowired
|
||||
CityModelRepository cityModelRepository;
|
||||
|
||||
@BeforeEach
|
||||
void setupData() {
|
||||
|
||||
cityModelRepository.deleteAll();
|
||||
|
||||
CityModel aachen = new CityModel();
|
||||
aachen.setName("Aachen");
|
||||
aachen.setExoticProperty("Cars");
|
||||
|
||||
CityModel utrecht = new CityModel();
|
||||
utrecht.setName("Utrecht");
|
||||
utrecht.setExoticProperty("Bikes");
|
||||
|
||||
cityModelRepository.saveAll(Arrays.asList(aachen, utrecht));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreExoticProperty() {
|
||||
|
||||
CityModel cityModel = new CityModel();
|
||||
cityModel.setName("The Jungle");
|
||||
cityModel.setExoticProperty("lions");
|
||||
cityModel = cityModelRepository.save(cityModel);
|
||||
|
||||
CityModel reloaded = cityModelRepository.findById(cityModel.getCityId())
|
||||
.orElseThrow(RuntimeException::new);
|
||||
assertThat(reloaded.getExoticProperty()).isEqualTo("lions");
|
||||
|
||||
long cnt = cityModelRepository.deleteAllByExoticProperty("lions");
|
||||
assertThat(cnt).isOne();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortOnExoticProperty() {
|
||||
|
||||
Sort sort = Sort.by(Sort.Order.asc("exoticProperty"));
|
||||
List<CityModel> cityModels = cityModelRepository.findAll(sort);
|
||||
|
||||
assertThat(cityModels).extracting(CityModel::getExoticProperty).containsExactly("Bikes", "Cars");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortOnExoticPropertyCustomQuery_MakeSureIUnderstand() {
|
||||
|
||||
Sort sort = Sort.by(Sort.Order.asc("n.name"));
|
||||
List<CityModel> cityModels = cityModelRepository.customQuery(sort);
|
||||
|
||||
assertThat(cityModels).extracting(CityModel::getExoticProperty).containsExactly("Cars", "Bikes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortOnExoticPropertyCustomQuery() {
|
||||
Sort sort = Sort.by(Sort.Order.asc("n.`exotic.property`"));
|
||||
List<CityModel> cityModels = cityModelRepository.customQuery(sort);
|
||||
|
||||
assertThat(cityModels).extracting(CityModel::getExoticProperty).containsExactly("Bikes", "Cars");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
@EnableNeo4jRepositories
|
||||
static class Config extends AbstractNeo4jConfig {
|
||||
|
||||
@Bean
|
||||
public BookmarkCapture bookmarkCapture() {
|
||||
return new BookmarkCapture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformTransactionManager transactionManager(
|
||||
Driver driver, DatabaseSelectionProvider databaseNameProvider) {
|
||||
|
||||
BookmarkCapture bookmarkCapture = bookmarkCapture();
|
||||
return new Neo4jTransactionManager(driver, databaseNameProvider,
|
||||
Neo4jBookmarkManager.create(bookmarkCapture));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Driver driver() {
|
||||
|
||||
return neo4jConnectionSupport.getDriver();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2011-2022 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
|
||||
*
|
||||
* https://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.integration.issues.gh2474;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.neo4j.core.schema.GeneratedValue;
|
||||
import org.springframework.data.neo4j.core.schema.Id;
|
||||
import org.springframework.data.neo4j.core.schema.RelationshipProperties;
|
||||
import org.springframework.data.neo4j.core.schema.TargetNode;
|
||||
|
||||
/**
|
||||
* @author Stephen Jackson
|
||||
*/
|
||||
@RelationshipProperties
|
||||
@Data
|
||||
public class JobRelationship {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@TargetNode
|
||||
private PersonModel person;
|
||||
|
||||
private String jobTitle;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2011-2022 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
|
||||
*
|
||||
* https://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.integration.issues.gh2474;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.neo4j.core.schema.GeneratedValue;
|
||||
import org.springframework.data.neo4j.core.schema.Id;
|
||||
import org.springframework.data.neo4j.core.schema.Node;
|
||||
|
||||
/**
|
||||
* @author Stephen Jackson
|
||||
*/
|
||||
@Node
|
||||
@Data
|
||||
public class PersonModel {
|
||||
@Id
|
||||
@GeneratedValue(generatorClass = GeneratedValue.UUIDGenerator.class)
|
||||
private UUID personId;
|
||||
|
||||
private String address;
|
||||
private String name;
|
||||
private String favoriteFood;
|
||||
}
|
||||
Reference in New Issue
Block a user