使用py2neo从Cypher查询返回节点

时间:2018-10-05 07:55:06

标签: neo4j cypher py2neo

我正在尝试使用py2neo和cypher调用从Neo4j数据库中对节点进行基本检索,但是我找不到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桌面浏览器中使用sttnmctynm字段的值来运行它时,我总是可以得到我想要的东西:一个代表那个城市的节点(例如输入“佛罗里达”和“盖恩斯维尔”将显示盖恩斯维尔市的节点)。

所以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()的使用和传递参数的方式似乎都是正确的。

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

  

在Cypher查询中py2neo不支持OPTIONAL MATCH吗?

因此,更普遍的问题是:

  

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

  

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

1 个答案:

答案 0 :(得分:2)

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

在neo4j浏览器中运行Cypher命令将返回一个RECORD对象以及所有节点和边缘(在浏览器中,即使您不要求这些边缘,似乎在找到的所有节点中也存在所有边缘)。浏览器将向您显示该记录中的所有项目,而无需执行任何特殊操作(尽管您可以使用LIMIT作为数字,WHERE作为过滤标签和属性来限制浏览器返回的内容)。 / p> 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而不是{{1 }}。但是graph.evaluate返回的记录对象不是您可以在python中轻松处理的,因此有一些选择:

(1)graph.run将结果作为一个字典列表返回,该字典报告记录中返回的所有节点。
(2)graph.run(theCypherQuery).data()将该字典作为键和值的表返回,这似乎对于打印到控制台最有用。
(3)graph.run(theCypherQuery).table()与上面的graph.run(theCypherQuery).evaluate()等效,并返回第一个对象。

在我的真实情况下,我想在具有不同标签种类的节点之间匹配名称,这些节点是具有特定标签的另一个节点的子节点。我的Cypher查询始终返回正确的节点,但是还返回了五个graph.evaluate(theCypherQuery)对象(用于其他节点标签),这些对象在浏览器中被忽略,但破坏了我的python代码。也许我可以将Cypher查询更改为仅返回一个节点,而不管其标签类型如何,但是不管我觉得了解如何处理这些记录对象都是一个好主意。

这是在python中操作返回的记录对象以提取节点并消除None响应的示例。

None

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

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 

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