使用py2neo从Cypher查询返回节点 [英] Use py2neo to return nodes from Cypher query

查看:880
本文介绍了使用py2neo从Cypher查询返回节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用py2neo和cypher调用从Neo4j数据库中对节点进行基本检索,但是我找不到py2neo v4.1的工作代码示例,并且该文档没有真正的描述,示例,有用的信息或教程的链接。关于SE的类似问题利用不再起作用的代码。



我有以下密码查询:

  getCityNodeQuery ='''MATCH(州)在哪里n.state_name = $ sttnm 
可选匹配(n:州)<-[:: PARTOF]-(县:县)在哪里县。 county_name包含$ ctynm
可选匹配(n:State)<-[:PARTOF]-(city:City)WHERE city.city_name包含$ ctynm
可选匹配(n:State)<-[ :PARTOF]-(county:County)<-[:PARTOF]-(citycounty:Ward)WHERE citycounty.city_name包含$ ctynm
返回县,市,citycounty'''

当我在Neo4j桌面浏览器中运行此程序时,将其值放入 sttnm ctynm 字段,我总是可以得到我所要查找的内容:代表那个城市的单个节点(例如,输入佛罗里达和盖恩斯维尔来显示该节点盖恩斯维尔市)。



所以Cypher部分本身似乎正确,因此问题可能出在我从py2neo调用它的方式上:

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

返回 None



所以有一种想法是我没有正确调用查询,因此它没有返回从Neo4j浏览器调用时执行的节点。但是,当我只运行第一行并时,我确实获得了正确的County节点,因此我使用了 graph.evaluate()和我传递参数的方式似乎都是正确的。



我还可以重新排列查询,以使县成为匹配城市的条件,并且THAT起作用并且避免了可选匹配。我以几种方式重新编制了查询,并以一种方式获得了县,但没有县内的城市。不幸的是,我实际上有三个要匹配的不同条件。因此,最直接的问题是:


Py2neo在Cypher中不支持 OPTIONAL MATCH 查询?


所以更普遍的问题是:


如何在py2neo中使用cypher从Neo4j数据库返回所需的节点?


Or


在Neo4j Browser和py2neo中使用Cypher查询有什么区别?



解决方案

此问题的解决方案与py2neo v 4.1.0中各种命令返回的对象有关。我试图将问题/解决方案推广到我的特定用例之外。



在neo4j浏览器中运行Cypher命令会返回 RECORD 对象以及所有节点和边缘(在浏览器中,即使您不要求这些边缘,似乎在找到的所有节点中也存在所有边缘)。浏览器将向您显示该记录中的所有项目,而您无需执行任何特殊操作(尽管您可以使用 LIMIT 作为数字和<$ c $来限制浏览器返回的内容c>在哪里用于过滤标签和属性)。



Py2neo有多种从Cypher查询返回对象的选项,但没有一个很好有文档记录,没有一个有用的例子或对差异的充分解释。但是,经过大量的尝试和失败,我设法弄清了一些东西并使它起作用。我将分享我学到的东西,以便希望其他尝试使用此软件包的人不会因为文档不佳而浪费时间。



请考虑以下方法

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

theCypherQuery ='''MATCH(n:Label1)WHERE n.label1_name = $ para1
可选匹配(n:Label1)<-[:REL1]-(n2:Label2)WHERE n2.label2_name = $ para2
可选匹配(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})
返回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):
##返回的节点默认为None
thisNode = None
##从查询中检索记录对象,替换为参数值。
## .data()部分返回一个包含单个字典的列表。
##因此,我只需提取项[0]即可提取字典。
thisRecord = graph.run(theCypherQuery,parameters = {'para1':parameter1,'para2':parameter2})。data()[0]
##现在,我创建一个非无列表字典中使用列表理解的值。
theseNodes = [用于键的val,此记录中的val .items(),如果val!=无]
##也许什么也没找到,但是如果找到至少一项...
如果len(theseNodes)> 0:
##然后返回找到的第一个对象(在我的情况下是唯一的匹配节点)
##请注意,将列表转换为节点对象也是必需的。
thisNode = theseNodes [0]
返回thisNode

这里没有什么特别奇怪的如果您已经精通Python,但是没有适当的文档说明,则可能很难理解数据结构以及检索和操作它们的必要条件。在这种情况下,检索到的节点对象与py2neo命令兼容,例如以下命令,它们可以根据两个找到的节点的名称(从另一个数据源)在它们之间创建链接。

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

请注意,我没有尝试返回并操纵关系对象,但希望它与获取和使用节点对象没有太大区别。并且希望您可以修改此代码以检索满足您需求的节点和关系。


I am trying to do a basic retrieval of a node from my Neo4j database using py2neo and a cypher call, but I can't find any examples of working code for py2neo v4.1 and the documentation has no real descriptions, examples, helpful information, or links to tutorials. Similar questions on SE utilize code that no longer works.

I have the following cypher query:

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'''

When I run this in the Neo4j desktop browser with values put in for the sttnm and ctynm fields I always get exactly what I'm looking for: a single node representing that city (for example inputting "Florida" and "Gainesville" brings up the node for the city of Gainesville).

So the Cypher part itself seems correct, and the problem is therefore probably with how I'm calling it from py2neo:

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

which returns None.

So one thought is that I'm not calling the query correctly, and so it's not returning the node that it does when called from Neo4j browser. However, when I run just the first line and RETURN n I do get the correct County node, so my use of graph.evaluate() and my way of passing parameters both seem correct.

I can also rearrange the query to make the County a condition on matching the City and THAT works and avoids the optional match. I reformulated the query a few ways and in one way I got the Counties, but not Cities within Counties. Unfortunately I actually have three different conditions that I want to match optionally. So the most immediate question is:

Does py2neo not support OPTIONAL MATCH within Cypher queries?

So the more general question is:

How do I return the desired node from my Neo4j database using cypher within py2neo?

Or

What are the differences between using Cypher queries in Neo4j Browser vs py2neo?

解决方案

The solution to this problem has to do with the objects returned by the various commands in py2neo v 4.1.0. I've tried to generalize the problem/solution to be helpful beyond my particular use-case.

Running the Cypher command in neo4j browser returns a RECORD object and all the nodes and edges (and in the Browser it seems all edges among all the nodes found even if you didn't ask for those edges). The browser will show you all the items in that record without you having to do anything special (though you can limit what the browser returns using LIMIT for the number and WHERE for filtering labels and properties).

Py2neo has a variety of options for returning objects from Cypher queries, none of which are well documented and none of which have useful examples or sufficient explanations of differences. But after lots of trying and failing I managed to figure out a few things and get it to work. I'm going to share what I learned so that hopefully somebody else trying to use this package doesn't lose hours due to poor documentation.

Consider the following method you might use to retrieve a node from your database.

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)

If theCypherQuery always returns exactly one node, then graph.evaluate will work because it actually returns the first object in the record generated by the query.

However, if you have a more complicated query and/or database that potentially returns multiple items (even if all but one item is None) then you need to use graph.run instead of graph.evaluate. But graph.run returns a record object that is not something you can deal with in Python easily, so there are options:

  1. graph.run(theCypherQuery).data() returns the result as a list of one dictionary reporting all the nodes returned in the record.

  2. graph.run(theCypherQuery).table() returns that dictionary as a table of keys and values, which seems mostly useful for printing to the console.

  3. graph.run(theCypherQuery).evaluate() is equivalent to graph.evaluate(theCypherQuery) above and returns the first object.

In my real case I wanted to match a name across nodes with different kinds of labels that are children of another node with a particular label. My Cypher query always returned the correct node, but there were five None objects returned as well (for the other node labels) that were simply ignored in the Browser but were breaking my Python code. Maybe I could change my Cypher query to only return that one node regardless of its label type, but regardless I figured it's a good idea to learn how to deal with these record objects.

Here is an example of manipulating the returned record object in Python to extract nodes and eliminate the None responses.

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 

Nothing here is especially strange of difficult Python if you are already proficient, but without proper documentation it may be difficult to understand the data structures and what is necessary to retrieve and manipulate them. In this case, the retrieved node objects are compatible with py2neo commands such as the following to create a link between two found nodes based on their names (from another data source).

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

Note that I haven't tried to return and manipulate a relationship object yet, but hopefully it won't be too different from getting and using the node objects. And hopefully you can modify this code to retrieve nodes and relationships to fit your needs.

这篇关于使用py2neo从Cypher查询返回节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆