Spring data многое делает, чтобы помочь вам сосредоточиться на написании ваших шифровальных запросов, в то время как он обрабатывает отображение результатов для вас. Однако, когда ваши запросы становятся более сложными, он начинает испытывать трудности. Именно здесь вам нужно вмешаться и явно сопоставить результаты запроса с объектами вашего домена.
В этом посте мы рассмотрим, как вы можете запросить путь и сопоставить результаты с помощью Spring Data Neo4j.
Простые запросы отображаются для вас
Для простых запросов вам может сойти с рук только определение соответствующих классов сущностей и отношений, Spring сделает все остальное.
Давайте используем Функция findAll (возвращает все узлы):
@Query("MATCH (n:City) RETURN n LIMIT 25")
Iterable findAll();
Объект 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 Set connections = 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 List path;
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”