Можно ли добавить временное свойство к neo4j node только для возврата?
Я использовал cypher и neo4j какое-то время, и я снова и снова сталкиваюсь с подобной проблемой.
Я хотел бы иметь возможность добавить временное свойство к node, которое существует только в возвращаемых данных и не сохраняется в базе данных Neo4j.
Например, текущая проблема, над которой я работаю, дается N-арная древовидная структура в базе данных графа, я хотел бы вернуть детей определенного node, и информационное логическое значение, дающее мне знать, что у этих детей есть дети.
Запрос на получение этой информации достаточно прост:
MATCH (parent:Item { guid: "Identifier here" })-[:HASCHILD]->(child:Item)
OPTIONAL MATCH (child)-[:HASCHILD]->(grandchild:Item)
WITH parent, child, LENGTH(COLLECT(grandchild)) > 0 AS has_children
Теперь у меня есть родительский, дочерний и логический, дающий мне знать, что есть еще дети.
То, что я хотел бы сделать дальше, - установить has_children boolean как свойство на дочерних узлах, но только для возврата запроса мне не нужно, чтобы свойство сохранялось.
Я знаю, что есть некоторые решения, чтобы получить то, что я ищу, но ни один из них не является тем, что я ищу.
1) Я мог бы создать временный node: (Продолжение из предыдущего запроса)
...
WITH parent, child, LENGTH(COLLECT(grandchild)) > 0 AS haschildren,
{
childkey1: child.childkey1,
childkey2: child.childkey2,
...
has_children: haschildren
} AS tempnode
RETURN parent, tempnode
Это решение не очень велико, вам нужно знать точный тип node и все свойства node, с которыми вы имеете дело, он не будет работать в общем случае.
2) Я мог бы УСТАНОВИТЬ и УДАЛИТЬ логическое, временно сохраняя его в базе данных (как описано в Neo4J создать временную переменную в Cypher).
Это не очень удобно, так как это приведет к многократной записи db (для добавления и удаления).
Кроме того, он не так динамичен, как вариант 1, поскольку он сможет обрабатывать только простые типы. (Вариант 1 может обрабатывать более сложные случаи, такие как добавление коллекции узлов вместо простого логического).
Насколько я знаю, это единственные варианты, когда речь заходит о временных данных возврата. Мне интересно, я что-то упустил? Можно ли сделать это более простым или динамичным?
Что-то вдоль линий
WITH parent, child, LENGTH(COLLECT(grandchild)) > 0 AS haschildren,
TEMPSET child.has_children = haschildren
Обратите внимание, что я немного упростил приведенные примеры.
Я столкнулся с этой проблемой несколько раз в разных контекстах в neo4j. В большинстве случаев я либо иду с вариантом 1, либо строю правильную структуру после обработки на стороне приложения после возвращения запроса.
Есть ли лучший способ?
Ответы
Ответ 1
Вы можете попробовать вернуть карту. Взгляните в этом примере:
// Creating a sample node
CREATE (:Person {name:'Jon', id: 1})
Возврат проекции с дополнительным булевым:
MATCH (p:Person {id:1})
WITH p, true AS has_children
RETURN p{.*, has_children:has_children}
Результат:
╒═════════════════════════════════════════╕
│"p" │
╞═════════════════════════════════════════╡
│{"id":1,"name":"Jon","has_children":true}│
└─────────────────────────────────────────┘
Вышеприведенный запрос возвращает все свойства p
node с дополнительным булевым.
Ответ 2
Виртуальные узлы, предоставляемые библиотекой APOC, также могут быть полезны для этого, особенно если вам нужно сохранить (или иным образом отобразить) отношения узла, к которому вы добавляете свойство.
Смотрите https://neo4j.com/docs/labs/apoc/current/virtual/, а также https://community.neo4j.com/t/virtual-nodes-and-relationships-use-case/110. См. https://neo4j.com/docs/labs/apoc/current/introduction/ для установки и т.д. Apoc.
По сути, вы создаете временные (виртуальные) копии узлов, к которым вы хотите добавить свойства, а также временные (виртуальные) отношения, связывающие их с существующими или другими виртуальными узлами.
Например. чтобы в user-> групповых отношениях мы могли добавить новое свойство для пользователей и счетчик для таких отношений (протестировано с Neo4j v3.5):
MATCH (user:User)-[r:MEMBER_OF]-(team:Team)
WITH user, r, team, count(*) AS count
CALL apoc.create.vNode(labels(user), user{.*, something:'new'})
YIELD node AS vUser
CALL apoc.create.vRelationship(vUser, type(r), r{.*,count}, team)
YIELD rel AS vr
RETURN vUser, team, vr
Или, например, что-то вроде этого (не проверено):
MATCH (parent:Item { guid: "Identifier here" })-[r:HASCHILD]->(child:Item)
OPTIONAL MATCH (child)-[:HASCHILD]->(grandchild:Item)
WITH parent, child, LENGTH(COLLECT(grandchild)) > 0 AS has_children
CALL apoc.create.vNode(labels(child), child{.*, has_children:has_children})
YIELD node AS vChild
CALL apoc.create.vRelationship(parent, type(r), r{.*}, vChild)
YIELD rel AS vr
RETURN parent, vChild, vr