Spring data многое делает, чтобы помочь вам сосредоточиться на написании ваших шифровальных запросов, в то время как он обрабатывает отображение результатов для вас. Однако, когда ваши запросы становятся более сложными, он начинает испытывать трудности. Именно здесь вам нужно вмешаться и явно сопоставить результаты запроса с объектами вашего домена.
В этом посте мы рассмотрим, как вы можете запросить путь и сопоставить результаты с помощью Spring Data Neo4j.
Простые запросы отображаются для вас
Для простых запросов вам может сойти с рук только определение соответствующих классов сущностей и отношений, Spring сделает все остальное.
Давайте используем Функция findAll
(возвращает все узлы):
@Query("MATCH (n:City) RETURN n LIMIT 25") IterablefindAll();
Объект City
должен быть определен как таковой:
@NodeEntity(value = "City") public class City { @Id @GeneratedValue private Long id; private String name; private double longitude; private double latitude; @Relationship(type = "NEXT") private Setconnections = Collections.emptySet(); public City( Long id, String name, double longitude, double latitude, Set connections ) { this.id = id; this.name = name; this.longitude = longitude; this.latitude = latitude; this.connections = connections; } public City() { } // getters + setters }
City
также имеет отношение, так что это также необходимо определить:
@RelationshipEntity("NEXT") public class Connection { @Id @GeneratedValue private long id; @StartNode private City start; @EndNode private City end; public Connection(long id, City start, City end) { this.id = id; this.start = start; this.end = end; } public Connection() { } // getters + setters }
Вызывая Функция findAll
правильно соберет города из базы данных и нанесет их на карту для вас.
На данный момент все хорошо.
Другой пример настройки класса сущностей Neo4j можно найти в документации Spring Data Neo4j – начало работы/| .
Извлечение путей требует вашей помощи
Приведенный выше код не подходит для запроса cypher, который возвращает путь.
Для этого раздела будет использоваться следующий запрос (взят из документации плагина Apoc ):
MATCH (a:City {name:'bruges'}), (b:City {name:'dresden'}) MATCH p=(a)-[*]->(b) WITH collect(p) as paths CALL apoc.spatial.sortByDistance(paths) YIELD path, distance RETURN path, distance
Этот запрос возвращает путь между двумя городами и общее расстояние этого пути.
К сожалению, нет способа представить эту информацию с использованием классов сущностей и отношений, определенных ранее. Кроме того, в настоящее время нет способа сделать это с помощью Spring Data Neo4j full stop. Лично мне потребовалось много времени, чтобы осознать этот факт, пока я не наткнулся на этот отрывок в их документации .
Пользовательские запросы не поддерживают пользовательскую глубину. Кроме того, @Query не поддерживает сопоставление пути с объектами домена, поэтому путь не должен быть возвращен из запроса Cypher. Вместо этого верните узлы и связи, чтобы они были сопоставлены с объектами домена.
Каким-то образом мне удавалось постоянно просматривать эту информацию, когда я просматривал документы…
Это означает, что вы не можете сделать что-то подобное:
public interface PathRepository extends Neo4jRepository{ @Query("MATCH (a:City {name:$departure}), (b:City {name:$arrival})\n" + "MATCH p=(a)-[*]->(b)\n" + "WITH collect(p) as paths\n" + "CALL apoc.spatial.sortByDistance(paths) YIELD path, distance\n" + "RETURN path, distance") List getAllPaths(String departure, String arrival); }
Где Путь
содержит следующее:
@QueryResult public class Path { public Listpath; public double distance; public Path(List path, double distance) { this.path = path; this.distance = distance; } }
Аннотация @QueryResult
позволяет вам определить пользовательский объект, содержащий результаты вашего запроса cypher, подробнее можно найти в Spring docs
Если вы сделаете это, он все равно будет выполнен, но не будет никакой информации о заполненных городах.
Как вручную отобразить путь
Чтобы иметь возможность сопоставить путь, показанный в предыдущем разделе, вам необходимо вручную присвоить результаты запроса cypher объектам.
Это можно сделать с помощью следующего кода (входящий большой фрагмент кода):
@Repository // Class instead of interface // Extend [SimpleNeo4jRepository] instead of [Neo4jRepository] public class PathRepository extends SimpleNeo4jRepository{ private static final String GET_ALL_PATHS_QUERY = "MATCH (a:City {name:$departure}), (b:City {name:$arrival})\n" + "MATCH p=(a)-[*]->(b)\n" + "WITH collect(p) as paths\n" + "CALL apoc.spatial.sortByDistance(paths) YIELD path, distance\n" + "RETURN path, distance"; // Needed to be able to query the database private final Session session; // Inject in the session // No need to create the session yourself, Spring has already created it public PathRepository(Session session) { super(City.class, session); this.session = session; } public List getAllPaths(String departure, String arrival) { Map parameters = Map.of( "departure", departure, "arrival", arrival ); // Execute the query and retrieve the result Result rows = session.query(GET_ALL_PATHS_QUERY, parameters); List results = new ArrayList<>(); for (Map row : rows) { results.add(convertRow(row)); } return results; } private Path convertRow(Map row) { InternalPath.SelfContainedSegment[] connections = (InternalPath.SelfContainedSegment[]) row.get("path"); List cities = new ArrayList<>(); // Iterate through the segments in the path for (InternalPath.SelfContainedSegment connection : connections) { cities.add(convert(connection)); } double distance = (Double) row.get("distance"); return new Path(cities, distance); } private City convert(InternalPath.SelfContainedSegment connection) { // Extract the information about the [City] from the path segment // Information about the start node and the relationship could also be accessed return new City( connection.end().id(), connection.end().get("name").asString(), connection.end().get("latitude").asDouble(), connection.end().get("longitude").asDouble() ); } }
Эта итерация Path Repository
расширяет Простой репозиторий Neo4j
для наследования некоторых наиболее распространенных запросов, не требуя от вас их самостоятельной реализации. Вам не нужно расширять этот класс, но я предлагаю вам это сделать.
Я не думаю, что есть какая-либо необходимость объяснять остальную часть примера, я привел в порядок код, насколько мог, и добавил комментарии для ясности. Надеюсь, их будет достаточно!
Вывод
Как я в конце концов выяснил и теперь упомянул вам, на момент написания этого поста Spring Data Neo4j не поддерживает автоматическое сопоставление запросов, содержащих пути. Поэтому вам нужно будет взять свою судьбу в свои руки и самостоятельно извлечь данные. Код в предпоследнем разделе (длинный фрагмент кода) показывает вам, как это сделать. Используя подобную структуру, вы должны быть в состоянии адаптировать ее для собственного использования для решения ваших собственных проблем. Я надеюсь, что это сэкономит вам некоторое время, так как мне потребовалось некоторое время, чтобы понять, что это был единственный способ получить путь при использовании Spring Data Neoj4 в любом случае…
Если вам понравился этот пост или вы нашли его полезным (или и то, и другое), пожалуйста, не стесняйтесь подписываться на меня в Твиттере по адресу @LankyDanDev и не забудьте поделиться со всеми, кто может найти это полезным!
Оригинал: “https://dev.to/lankydandev/mapping-a-path-in-spring-data-neo4j-14la”