neo4j是当下比较流行的图数据库,为什么流行呢?因为用起来真的很方便,笔者之前分别将知识存储在mysql、RDF、mongodb中,现在尝试neo4j,从初步尝试来看,在构建图谱、查询、路径搜索上,neo4j用起来比前3者都更简洁省心。neo4j分为两种版本,社区版和企业版,企业版支持分布式布局,可支持高并发,但是需要付费;社区版免费,但是只能单机部署,当图中结点和关系达到一定量后,查询速度会比较慢。所以,像社交这样的图谱,社区版neo4j估计是够呛,然而,笔者建立的是专业领域的知识图谱,结点量在百万左右,关系量在千万以下,查询并发小,社区版足矣。

neo4j的主要存储结构为Node和Relationship,Node包含label和Property, label相当于节点的类别,Property是Node包含的属性,是个key-value结构,一个结点可包含多个属性。 Node 和 Relatsionship 的属性是用一个双向列表来保存的.

neo4j自带查询语言——Cypher,笔者觉得这种语言的查询方式有点像sql,在关系指向上又有点类scala。Cypher的语法很简单,沉下心来半个小时足够基本应用了。除了在neo4j自身的界面上使用,还可以将Cypher嵌入到python中使用,这就是py2neo。py2neo自身的源程序很简单,支持的类和函数也不多,笔者大概看了一下源码,对外的接口大概是这样的:

 

接口分为4部分,其中meta就是一些登录和安全相关的处理,基本和查询无关。data是对接到neo4j下面的一些数据结构,可以通过这些数据结构来构建和查询图数据库。

Data中,Node为节点类,可存储结点,并支持一些基本操作,Relationship是关系类,关系类主要由入节点、关系属性、出节点构成。Subgraph是一些节点和关系的集合,其中至少包含一个节点一条边。Walkable是Subgraph外加一些额外的信息,感觉就是关系和节点能练成一条路的Subgraph,在笔者新安装的版本中,Subgraph貌似要通过Path才能可视化。Path就是多个节点,多条关系,并且关系和节点能连成一条完整的路径,Path可以由多个Relationship相加得到。PropertyDict是属性词典。

dataset中,目前只使用了graph,最好用的是run方法,将Cypher命令写成字符串,然后丢到run总直接执行,这样,就相当于在python中直接执行Cypher语言。毕竟py2neo的各种查询,貌似都是转换成Cypher来完成的,python在查询时就相当于Cypher自动组装器一般(这点在NodeMatch和Relationshipmatch中都可以看到对应的组装源代码),并且Cypher语言中有一些图搜索方法是py2neo中没有的。

matching功能很单一,就是Node和Relationship的查询接口。

查询实例:

1.graph链接

graph = Graph("http://your_neo4j_ip:7474/db/data/", username="your_username", password="your_password")

2. graph.run的使用——查询并写入DataFrame

match_str = "match (z:`z的label`)<-[`z和d的关系`]-(d:`d的label`)-[`z和s的关系`]->(s:`s的label`) \
             where s.name='需要查找的实体的name属性值' \
             return z"
tmp_df = pd.DataFrame(graph.run(match_str))

3. graph.run的使用——调用Cypher内部方法

match_str = "match p=shortestPath((m:m的label{name:'m的name属性值'})-[r*1..4]-(n:n的label{name:'name属性值'})) return p"
p1 = graph.run(match_str)
for p in p1:
    print(p)

4. 查询节点和关系

matcher = NodeMatcher(graph)
rmatcher = RelationshipMatcher(graph)
a = matcher.match("疾病", name="脑中风").first()   #a Node
b = matcher.match("药品", name="阿托伐他汀钙").first()
r1 = rmatcher.match({b,a}).first()   # a Relationship
print(r1) 

这里刚开始使用时很容易掉进坑里,例如,我们知道两个节点,但不知道他们什么关系,想查询他们的关系,反正笔者刚开始的做法是自定义两个Node,这两个Node和图谱中的某两个Node的label和属性完全一样,然后查询这两个Node之间的关系,如下面这样

a = Node("疾病", name="脑中风")
b = Node("药品", name="阿托伐他汀钙")
r1 = rmatcher.match({b,a}).first()

运行时总是报节点不在图谱中,后来才发现,新定义的结点,即便是图谱中原来有的,但id是不一样的,肯定会报没有,只能靠查询图谱中的结点来获取关系的入节点和出节点。

5. 获取子图

c = matcher.match("疾病", name="高胆固醇血症").first()
r2 = rmatcher.match({b,c}).first()
s = r1 | r2
print(c)
for n in s.nodes:
    print(n)
for r in s.relationships:
    print(r)

6.  获取路径

d = matcher.match("疾病", name="糖尿病").first()
e = matcher.match("药品", name="那格列奈").first()
f = matcher.match("症状", name="血糖高").first()
print(e)
r3 = rmatcher.match({d,e}).first()
r4 = rmatcher.match({e,f}).first()
w = r3 + r4
print(Path(w))

总的来说,neo4j的查询还是很好用的。目前就到这儿啦,后面持续学习和补充啦。

Logo

更多推荐