Как получить собственный запрос Spring Neo4j Cypher для заполнения массива дочерних отношений - PullRequest
1 голос
/ 03 мая 2019

Встроенные запросы к Spring Data Neo4j (SDN) возвращают объекты, заполненные с глубиной 1 по умолчанию.Это означает, что «дочерние элементы» (связанные узлы) объекта, возвращаемого запросом, заполняются.Это хорошо - в конце ссылок имеются реальные объекты из объектов, возвращаемых этими запросами.

По умолчанию пользовательские запросы имеют глубину 0.Это хлопотно.

В этом ответе описано, как заставить springboot neo4j заполнить связанный элемент с целью пользовательского запроса - для достижения дополнительного уровня глубины.результатов запроса.

У меня проблемы с этим методом, когда связанные элементы находятся в списке:

@NodeEntity
public class BoardPosition {

    @Relationship(type="PARENT", direction = Relationship.INCOMING)
    public List<BoardPosition> children;

У меня есть запрос, возвращающий цель BoardPosition, и мне нужноэто дочерние элементы для заполнения.

@Query("MATCH (target:BoardPosition) <-[c:PARENT]- (child:BoardPosition) 
       WHERE target.play={Play} 
       RETURN target, c, child")
BoardPosition findActiveByPlay(@Param("Play") String play);

Проблема в том, что запрос, похоже, возвращает один отдельный результат для каждого дочернего элемента, и эти результаты не используются для заполнения массива дочерних элементов в целевом объекте.

Вместо того, чтобы Spring Neo собирал дочерние элементы в массив цели, я получаю ошибку «только 1 ожидаемый результат» - как будто запрос возвращает несколько результатов, каждый с одним дочерним элементом, а не один результат с дочерними элементами вit.

org.springframework.dao.IncorrectResultSizeDataAccessException: неверный размер результата: ожидается не более 1

КакМогу ли я иметь пользовательский запрос для заполнения списка детей этой цели?

(Обратите внимание, что встроенный findByPlay(play) делает то, что я хочу - встроенные запросы имеют глубину 1, а не 0, и этовозвращает цель с заполненными детьми - но, конечно, мне нужно сделать запрос немного более сложным, чем просто «by Play» ... вот почему мне нужно решить это)


Версии:

org.springframework.data:spring-data-neo4j:5.1.3.RELEASE

neo4j 3.5.0

Ответы [ 2 ]

2 голосов
/ 08 мая 2019

=== Редактировать ======

Ваша проблема возникает из-за того, что у вас есть собственные отношения (отношения между узлами одной метки)

ЭтоВот как Spring обрабатывает ваш запрос для одного узла:

org.springframework.data.neo4j.repository.query.GraphQueryExecution

@Override
public Object execute(Query query, Class<?> type) {
    Iterable<?> result;
    ....
    Object ret = iterator.next();
    if (iterator.hasNext()) {
        throw new IncorrectResultSizeDataAccessException("Incorrect result size: expected at most 1", 1);
    }
    return ret;
}

Spring передает тип класса вашего узла Class<?> type в neo4j-ogm и считывает ваши данные обратно.

Знаете, сервер neo4j будет возвращать несколько строк для вашего запроса, по одной для каждого соответствующего path:

A <- PARENT - B
A <- PARENT - C
A <- PARENT - D

Если ваши узлы имеют разные метки, то есть имеют другой тип класса, тогда ogmтолько один возвращаемый узел соответствует вашему типу возврата запроса, нет проблем.

Но ваши узлы имеют одинаковые метки, то есть один и тот же тип класса => Neo4j OGM не может различить, который является возвращенным узлом -> Все узлы A,B, C, D возвращено -> Исключение

В связи с этой проблемой, я думаю, вам следует подать отчет об ошибке сейчас.

В качестве обходного пути вы можете изменить запрос, чтобы он возвращал только distinct target.your_identity_property (identity_property - это «первичный ключ» узла, который уникальноидентифицируйте свой узел)

Затем загрузите вызов приложения с этим свойством идентичности:

public interface BoardRepository extends CrudRepository<BoardPos, Long> {

    @Query("MATCH (target:B) <-[c:PARENT]- (child:B) WHERE target.play={Play} RETURN DISTINCT target.your_identity_property")
    Long findActiveByPlay(@Param("Play") String play);

    BoardPos findByYourIdentityProperty(xxxx);
} 

=== OLD ======

Spring docs говорит, что (выделено мной):

Пользовательские запросы не поддерживают настраиваемую глубину.Кроме того, @Query не поддерживает отображение пути к объектам домена, поэтому путь не должен возвращаться из запроса Cypher. Вместо этого возвращайте узлы и отношения, чтобы сопоставить их с сущностями домена.

Итак, ваш вариант использования (заполнение дочерних узлов пользовательским запросом) поддерживается .Spring Framework уже отображает результаты в один узел.(Действительно, мои настройки локальных явок показывают, что операция работает нормально)

Таким образом, ваше исключение может быть вызвано несколькими проблемами:

  1. У вас более одного target:BoardPosition с target.play={play}.Таким образом, исключение относится к более чем одному target:BoardPosition вместо одного BoardPosition с несколькими дочерними результатами

  2. У вас неверное сопоставление сущностей.У вас есть поле сопоставления, помеченное @Relationship с правильным атрибутом direction?Вы можете опубликовать свою сущность здесь.

Вот мои локальные настройки:

@NodeEntity(label = "C")
@Data
public class Child {

    @Id
    @GeneratedValue
    private long id;
    private String name;

    @Relationship(type = "PARENT", direction = "INCOMING")
    private List<Parent> parents;
}

public interface ChildRepository extends CrudRepository<Child, Long> {

    @Query("MATCH (target:C) <-[p:PARENT]- (child:P) " 
        + "WHERE target.name={name} " 
        + "RETURN target, p, child")
    Child findByName(@Param("name") String name);
}

(:C) <-[:PARENT] - (:P)
1 голос
/ 09 мая 2019

Рассмотрим альтернативный запрос

MATCH (target:BoardPosition {play:{Play}})
RETURN target, [ (target)<-[c:PARENT]-(child:BoardPosition) | [c, child] ]

, который использует понимание списка для возврата не только target , но также его отношений и связанных узлов метки BoardPosition в пределаходна строка результатов.Это гарантирует, что результатом будет одна строка (если ваш атрибут play уникален).

Я не пробовал это на вашем примере, но в моем приложении этот подход работаетхорошо.Neo4j OGM увлажняет объекты, как и ожидалось.Важно включить связанные узлы , а также отношения, указывающие на узлы.

Если вы включите журналы neo4j OGM, вы увидите, что встроенные запросы с глубиной 1 используюттот же подход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...