当前位置: 首页 > 后端技术 > Python

使用Py2neo查询Neo4j中的节点、关系和路径

时间:2023-03-26 16:53:22 Python

Neo4j是一个开源的图数据库,Py2neo提供了使用Python语言访问Neo4j的接口。本文介绍使用Py2neo的NodeMatcher和RelationshipMatcher查询图中的节点和关系,以及如何通过执行Cypher语句进行查询。本文使用的Py2neo是2021.1之后的版本。请点击此处获取手册:Py2neo手册连接到Neo4j数据库。本文将使用各种数据类型。importnumpyasnpimportpandasaspdfrompy2neoimportNode,Relationship,Graph,Path,Subgraphfrompy2neoimportNodeMatcher,RelationshipMatcher配置Neo4j数据库的访问地址,用户名和密码neo4j_url='http://localhost:7474/'user='neo4j'pwd='admin'2021.1之前访问数据库的方式为:graph=Graph(neo4j_url,username=user,password=pwd)2021.1之后的版本访问数据库的方式为:graph=Graph(neo4j_url,auth=(user,pwd))下图是一个例子:图中包含一些Person节点,每个Person节点都有name、age、work等属性;其中,“赵赵”节点为多标签节点。除了Person标签,它还有一个Teacher标签;Person与Person节点之间有同事、邻居、学生、老师等。关系;图中有一些Location节点,它们之间存在包含关系;Person节点和Location节点之间存在“访问”关系,“访问”关系有两个属性:date和stay_hours。1、使用graph.schema查询图中的节点类型和关系。要检查节点类型,请使用graph.schema.node_labels,并检查关系类型,请使用graph.schema.relationship_types。它们的返回值类型是frozenset,不能增删元素。收藏。>>>graph.schema.node_labelsfrozenset({'Location','Person','Teacher'})>>>graph.schema.relationship_typesfrozenset({'visit','include','colleague','student','teacher','neighbor'})2.使用NodeMatcher查询节点首先创建一个NodeMatcher对象,使用match表示匹配哪个标签节点,使用where表示过滤条件(有两种方法)。需要注意的是,匹配成功返回的对象是NodeMatcher对象。要将其转换为Node对象,可以使用first提取第一个满足条件的节点,或者转换为节点列表。>>>node_matcher=NodeMatcher(graph)>>>node=node_matcher.match("Person").where(age=20).first()>>>nodeNode('Person',age=20,name='李li',work='宇宙电子厂')>>>nodes=list(node_matcher.match("Person").where(age=35))>>>nodes[Node('Person',age=35,name='旺旺',work='宇宙电子厂')]where条件有两种写法,一种是将要匹配的属性和值写成key=value的形式,比如where(age=20)上面,这种写法只能根据值是否完全一致进行匹配,不能根据值的大小进行过滤。如果这样写,会报错:node=node_matcher.match("Person").where(age>20).first()#error如果要根据值的大小进行过滤还是做一些字符串的模糊匹配,可以把条件表达式写成一个字符串,整体放在where语句中。在此字符串中,您可以使用_来引用匹配的节点。下面两个例子,一个是匹配工作属性为“MoonXX”模式的Person节点,一个是匹配age大于20的Person节点。>>>node=node_matcher.match("Person").where("_.work=~'Moon.*'").first()>>>nodeNode('Person','Teacher',age=45,name='赵赵',work='月亮中学')>>>nodes=list(node_matcher.match("Person").where("_.age>20"))>>>nodes[Node('Person',age=35,name='汪汪',work='宇宙电子厂'),Node('Person',age=30,name='张张',work='宇宙电子厂'),Node('Person','Teacher',age=45,name='赵赵',work='月亮中学')]将NodeMatcher返回的结果转换成Node数据类型或Node列表后,访问属性就是很容易简单,如上面最后一个例子的结果,访问第一个节点的name属性:>>>nodes[0]['name']'王王'3.使用RelationshipMatcher查询关系RelationshipMatcher有三个或更多matchmethods参数:第一个参数是节点的序列或集合,可以是None,表示任意节点;第二个参数是关系的类型,可以是None,表示任何类型的关系;第三个第一个参数是要匹配的属性,写成key=value的形式。match方法的返回值是RelationshipMatcher类型,需要先通过转换成Relationship数据结构,或者转换成列表。举个例子,比如你想查询“李李”这个节点的所有关系。先查询节点,再查询节点之间的关系。r_type=None表示可以接受任何类型的关系。返回的关系包括访问、同事。>>>node1=node_matcher.match("Person").where(name='李李').first()>>>relationship=list(relationship_matcher.match([node1],r_type=None))>>>relationship[visit(Node('人',age=20,name='李丽',work='万能电子厂'),Node('地点',name='禄口机场'),date='2021/7/16',stay_hours=1),colleague(Node('人',age=20,name='李丽',work='万能电子厂'),Node('人',age=30,name='张张',work='万能电子厂')),colleague(Node('人',age=20,name='李丽',work='万能电子厂'),Node('人',age=35,name='WangWang',work='宇宙电子厂'))]例2,查询“李李”和“张张”的关系,两个节点的顺序表示关系的方向是匹配。因此,整个图中“李李”和“张张”节点之间的同事关系是双向的,但查询结果只给出了“张张”节点到“李李”节点的关系。>>>node1=node_matcher.match("人").where(name='李李').first()>>>node2=node_matcher.match("人").where(name='张张').first()>>>relationship=list(relationship_matcher.match((node2,node1),r_type=None))>>>relationship[colleague(Node('人',age=30,name='张张',work='万能电子厂'),Node('人',age=20,name='李丽',work='万能电子厂'))]例3,查询图中某类关系,则第一个参数为None,第二个参数r_type指定关系类型。这里查询图中所有的同事关系。>>>relationship=list(relationship_matcher.match(None,r_type='colleague'))>>>relationship[colleague(Node('人',age=20,name='李李',work='万能电子厂'),Node('人',age=30,name='张张',work='万能电子厂')),colleagues(Node('人',age=20,name='李李',work='宇宙电子厂'),Node('人',age=35,name='旺旺',work='宇宙电子厂')),colleagues(Node('人',age=35,name='汪汪',work='宇宙电子厂'),Node('人',age=20,name='丽丽',work='宇宙电子厂')),colleagues(Node('人',age=30,name='张张',work='万向电子厂'),Node('人',age=20,name='李丽',work='万向电子厂'))]例4,在query根据属性的值过滤关系,属性可以写成key=value的形式作为match方法的第三个参数。这里查询图中的visit关系,stay_hours属性为1>>>relationship=list(relationship_matcher.match(None,r_type='visit',stay_hours=1))>>>relationship[visit(Node('人',age=20,name='李李',work='万向电子厂'),Node('Location',name='禄口机场'),date='2021/7/16',stay_hours=1)]虽然Py2neo手册上没有写,其实RelationshipMatcher也可以连接where方法根据属性的值过滤关系。上面的例子也可以写成下面的形式,效果是一样的。relationship=list(relationship_matcher.match(None,r_type='Visit').where(stay_hours=1))同理,where方法中也可以写一个字符串表达式,按值过滤关系。比如要过滤掉所有stay_hours>=1的访问关系,可以这样写:>>>relationship=list(relationship_matcher.match(None,r_type='visit').where("_.stay_hours>=1"))>>>relationship[Visit(Node('人物',age=20,name='李丽',work='宇宙电子厂'),Node('地点',name='禄口机场'),date='2021/7/16',stay_hours=1),visit(Node('人',age=20,name='刘刘',work='地球电商公司'),Node('位置',name='禄口机场'),date='2021/7/19',stay_hours=4)]如何访问返回结果中的每个属性,Relationship实际上包含一对起始节点和结束节点:start_node和end_node包含关系的类型,关系的属性以字典的形式存在,可以使用get方法获取属性的值。获取关系的起始节点和结束节点:>>>print(relationship[0].start_node['name'])>>>print(relationship[0].end_node['name'])TextString>>>print(relationship[0])>>>print(type(relationship[0]).__name__)(李李)-[:visit{date:'2021/7/16',stay_hours:1}]->(禄口机场)访问获取关系中的属性和值>>>print(relationship[0].keys())>>>print(relationship[0].values())>>>print(relationship[0].get('date'))dict_keys(['date','stay_hours'])dict_values(['2021/7/16',1])2021/7/164。通过执行Cypher语句查询NodeMatcher和RelationshipMatcher所能表达的匹配条件比较简单,更复杂的查询还是需要用Cypher语句来表达。Py2neo本身支持Cypher语句的执行。复杂的查询可以写成Cypher语句。通过graph.run方法查询,返回结果可以转化为pandas.DataFrame或pandas.Series对象,与其他数据分析工具无缝对接。例如查询Person节点,满足工作属性为“万能电子厂”。在Cypher语句中,可以用WHERE连接条件表达式,用AS重命名返回的属性。返回多个属性时,使用xxxASx,yyyASy。在graph.run方法之后,to_data_frame()可以将返回的数据变成一个pandasDataFrame对象,用AS改变的属性名就是DataFrame中的列名。cypher_="MATCH(n:Person)\WHEREn.work='宇宙电子厂'\RETURNn.nameASname,n.ageASage"df=graph.run(cypher_).to_data_frame()#pd.DataFrame示例2,查询其他哪些节点与已知节点相关,它们之间有什么样的关系。Cypher语言在查询关系时使用<或>来指示方向。这里需要返回type(r)。如果直接返回r,结果将是空值。>>>cypher_="MATCH(n:Person)-[r]->(m:Person)\WHEREn.name='李李'\RETURNtype(r)AStype,m.nameASname">>>df=graph.run(cypher_).to_data_frame()#pd.DataFrame例子3、Cypher语言也可以查询路径,因为返回的路径数不确定,所以最好将结果转成pandas.Series首先,然后遍历每个路径的访问节点和关系。这里查询的是“赵赵”节点与“旺旺”节点的关系路径。关系规定为同事或邻居,关系不超过4层。>>>cypher_="MATCHpath=(m:Person)-[:colleague|neighbor*1..4]->(n:Person)\WHEREm.name='赵赵'ANDn.name='王王'\RETURN路径">>>s=graph.run(cypher_).to_series()>>>print(len(s))>>>s[0]1Path(Node('Person','Teacher',age=45,name='赵赵',work='月亮中学'),neighbor(Node('人','老师',age=45,name='赵赵',work='月亮中学'),Node('人',age=30,name='张张',work='宇宙电子厂')),colleagues(Node('人',age=30,name='张张',work='万向电子厂'),Node('人',age=20,name='李丽',work='万向电子厂')),colleagues(Node('人',age=20,name='李Li',work='万能电子厂'),Node('人',age=35,name='旺旺',work='万能电子厂')))这里只查询到1条关系路径。从上图的结果也可以看出Path是一个比较复杂的结构。Path中的节点和关系分别用节点和关系表示,按照节点和关系在路径上的顺序存储。这里有一个示例代码,直接打印路径数据结构,并为每个路径组织路径文本。forpathins:#直接打印路径print(path)#获取路径中的节点和关系nodes=path.nodesrelationships=path.relationships#自己组织路径文本path_text=""forn,rinzip(nodes,relationshipshis):#添加一个节点和一个关系typepath_text+="{}-{}-".format(n['name'],type(r).__name__)#不要忘记最后一个节点path_text+=nodes[-1]['name']+'\n'print(path_text)这段代码运行结果如下,上面一行是直接打印路径的结果,下面一行是自己整理课文。(赵赵)-[:邻居{}]->(张张)-[:同事{}]->(李丽)-[:同事{}]->(旺旺)赵赵-邻居-张张-同事-LiLi-同事-WangWang总结在Neo4j中使用Py2neo查询节点、关系和路径时,可以通过NodeMatcher和RelationshipMatcher实现条件简单的查询。更复杂的查询可以写成Cypher语句来查询,查询结果可以转换成pandasDataFrame或Series数据类型,结合其他数据分析工具。我的Python版本>>>importsys>>>print(sys.version)3.7.6(默认,2020年1月8日,13:42:34)[Clang4.0.1(tags/RELEASE_401/final)]