Используйте py2neo для возврата узлов из запроса Cypher - PullRequest
0 голосов
/ 05 октября 2018

Я пытаюсь выполнить базовый поиск узла из моей базы данных Neo4j, используя py2neo и вызов cypher, но я не могу найти никаких примеров рабочего кода для py2neo v4.1, и в документации нет реальных описаний, примеров, полезная информация или ссылки на учебники.Подобные вопросы о SE используют код, который больше не работает.

У меня следующий запрос шифрования:

getCityNodeQuery= '''MATCH (State) WHERE n.state_name=$sttnm
OPTIONAL MATCH (n:State)<-[:PARTOF]-(county:County) WHERE county.county_name CONTAINS $ctynm
OPTIONAL MATCH (n:State)<-[:PARTOF]-(city:City) WHERE city.city_name CONTAINS $ctynm
OPTIONAL MATCH (n:State)<-[:PARTOF]-(county:County)<-[:PARTOF]-(citycounty:Ward) WHERE citycounty.city_name CONTAINS $ctynm
RETURN county, city, citycounty'''

Когда я запускаю его в браузере настольного компьютера Neo4j со значениями, введенными для * 1006Поля * и ctynm Я всегда получаю именно то, что ищу: один узел, представляющий этот город (например, ввод "Флорида" и "Гейнсвилл" вызывает узел для города Гейнсвилл).

Таким образом, сама часть Cypher кажется правильной, и поэтому проблема, вероятно, заключается в том, как я ее называю из py2neo:

def getCityWardNode(prefecture_name, city_name):
    thisCityNode = graph.evaluate(getCityNodeQuery, parameters = {'sttnm':state_name, 'ctynm':city_name})
    print(thisCityNode)

, которая возвращает None.

Таким образом, одна мысль состоит в том, что я неправильно вызываю запрос, и поэтому он не возвращает узел, который он делает при вызове из браузера Neo4j.Однако, когда я запускаю только первую строку и RETURN n, я получаю правильный узел County, поэтому мое использование graph.evaluate() и способ передачи параметров кажутся правильными.

Я также могу изменить порядок запроса, чтобы в графстве было выполнено условие для сопоставления города, и это работает и позволяет избежать необязательного сопоставления.Я переформулировал запрос несколькими способами и одним способом получил округа, но не города внутри округов.К сожалению, на самом деле у меня есть три разных условия, которые я хочу выполнить по желанию.Итак, самый неотложный вопрос:

Не поддерживает ли py2neo OPTIONAL MATCH в запросах Cypher?

Итак, более общий вопрос:

Как мне вернуть нужный узел из моей базы данных Neo4j, используя cypher в py2neo?

Или

В чем разница между использованием запросов Cypher в браузере Neo4j и py2neo?

1 Ответ

0 голосов
/ 10 октября 2018

Решение этой проблемы связано с объектами, возвращаемыми различными командами в py2neo v 4.1.0.Я попытался обобщить проблему / решение, чтобы она была полезной за пределами моего конкретного варианта использования.

Выполнение команды Cypher в браузере neo4j возвращает объект RECORD и все узлы и ребра (и в браузерекажется, что все ребра среди всех найденных узлов, даже если вы не просили эти ребра).Браузер покажет вам все элементы в этой записи без необходимости делать что-то особенное (хотя вы можете ограничить то, что браузер возвращает, используя LIMIT для числа и WHERE для фильтрации меток и свойств).

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

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

import py2neo as pn
graph = pn.Graph("bolt://localhost:####/", user="neo4j", password="pwd")

theCypherQuery= '''MATCH (n:Label1) WHERE n.label1_name=$para1 
OPTIONAL MATCH (n:Label1)<-[:REL1]-(n2:Label2) WHERE n2.label2_name = $para2
OPTIONAL MATCH (n:Label1)<-[:REL1]-(n3:Label3) WHERE n3.label3_name = $para2
RETURN n2, n3'''

def getNode(thisCypherQuery, parameter1, parameter2):
    cypherResult = graph.evaluate(thisCypherQuery, parameters = {'para1':parameter1, 'para2':parameter2})
    return cypherResult

someNode = getNode(theCypherQuery,firstParameter,secondParameter)

Если theCypherQuery всегда возвращает ровно один узел, то graph.evaluate будет работать, потому что фактически возвращает первый объект в записи, сгенерированной запросом.

Однако, если у вас более сложный запрос и / или база данных, которая потенциально возвращает несколько элементов (даже если все элементы, кроме одного, равны None), вам нужно использовать graph.run вместо graph.evaluate.Но graph.run возвращает объект записи, с которым вы не можете легко справиться в Python, поэтому есть варианты:

  1. graph.run(theCypherQuery).data() возвращает результат в виде списка из одного словаря отчетоввсе узлы вернулись в записи.

  2. graph.run(theCypherQuery).table() возвращает этот словарь в виде таблицы ключей и значений, что, по-видимому, наиболее полезно для печати на консоли.

  3. graph.run(theCypherQuery).evaluate() эквивалентно graph.evaluate(theCypherQuery) выше и возвращает первый объект.

В моем реальном случае я хотел сопоставить имячерез узлы с различными видами меток, которые являются дочерними по отношению к другому узлу с определенной меткой.Мой запрос Cypher всегда возвращал правильный узел, но было также возвращено пять объектов None (для других меток узлов), которые просто игнорировались в браузере, но нарушали мой код Python.Возможно, я мог бы изменить свой запрос Cypher, чтобы он возвращал только этот один узел, независимо от его типа метки, но независимо от того, что я решил, что это хорошая идея, чтобы научиться обращаться с этими объектами записи.

Вот пример манипуляциивозвратил объект записи в Python для извлечения узлов и исключения ответов None.

def getNode(thisCypherQuery, parameter1, parameter2):
## The returned node is None by default
thisNode = None  
## Retrieve the record object from the query, substituting in the parameter values.
## The .data() part returns a list containing a single dictionary.
## So I extract the dictionary by simply pulling item [0].
thisRecord = graph.run(theCypherQuery, parameters = {'para1':parameter1, 'para2':parameter2}).data()[0]
## Now I create a list of non-None values from the dictionary using "list comprehension".
theseNodes = [val for key,val in thisRecord .items() if val != None]
## Perhaps nothing was found, but if at least one item was found...
if len(theseNodes) > 0:  
    ## Then return the first found object (which in my case is the unique matching node)
    ## Note that this is also necessary to convert the list into a node object.
    thisNode = theseNodes[0]
return thisNode 

Нет ничего особенно странного в сложном Python, если вы уже опытны, но без надлежащей документации может быть сложно понять структуры данных и то, что необходимо для их извлечения и манипулирования ими.В этом случае извлеченные объекты узлов совместимы с командами py2neo, такими как приведенные ниже, для создания связи между двумя найденными узлами на основе их имен (из другого источника данных).

firstNode = getNode(theCypherQuery,'UnitedStates','Georgia')
secondNode = getNode(theCypherQuery,'UnitedStates','Jacksonville')
graph.merge(pn.Relationship(firstNode,'BORDERING',secondNode))

Обратите внимание, что у меня нет 'Я еще не пытался возвращать и манипулировать объектом отношений, но, надеюсь, он не будет сильно отличаться от получения и использования объектов узла.И, надеюсь, вы можете изменить этот код, чтобы получать узлы и отношения в соответствии с вашими потребностями.

...