Рубрики
Без рубрики

Отображение пути в Spring Data Neo4j

Spring data многое делает, чтобы помочь вам сосредоточиться на написании ваших шифровальных запросов, в то время как он обрабатывает отображение… С тегами java, neo4j, spring.

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”