官网:
Graph Data Platform | Graph Database Management System | Neo4j
入门教程:
Getting Started Guide - Getting Started
Cypher文档:
Introduction - Neo4j Cypher Manual
Cypher参考卡:
Neo4j Cypher Refcard 4.2
Neo4j操作手册:
Introduction - Operations Manual
驱动手册:
Neo4j documentation - Neo4j Documentation
在neo4j的ui界面处也有这些对应链接:
Cypher 是一种声明式图查询语言,它允许对图进行表达性和高效的查询、更新。
CREATE (:Movie {title: 'The Matrix', released: 1997})
Movie是节点label, 后面是节点属性, 节点id是无法指定的,只能自动生成
CREATE (p:Person {name: 'Keanu Reeves', born: 1964})
RETURN p
CREATE (a:Per {name: 'Tom Hanks'})-[r:ACTED_IN {roles: 'Forrest'}]->(m:Movie {title: 'Forrest Gump', released: 1994})
CREATE (a:Person {name: 'Tom Hanks', born: 1956})-[r:ACTED_IN {roles: ['Forrest']}]->(m:Movie {title: 'Forrest Gump', released: 1994})
CREATE (d:Person {name: 'Robert Zemeckis', born: 1951})-[:DIRECTED]->(m)
RETURN a, d, r, m
MATCH (m:Movie {title: 'The Matrix'})
RETURN m
MATCH (m:Movie)
WHERE m.title = 'The Matrix'
RETURN m
MATCH (m:Movie) --> (p)
WHERE m.title = 'The Matrix'
RETURN m,p
MATCH ()-[r:ACTED_IN]->()
WHERE 'Neo' IN r.roles
RETURN r
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE p.name = 'K' OR m.released > 2000 OR 'Neo' IN r.roles
RETURN p, r, m
MATCH (n)
RETURN DISTINCT n.age AS Labels
MATCH (n)-[l]->(v)
WHERE id(n) = 68 and type(l)='belong' and labels(v) = ['per']
return id(n) as id ,type(l) as type ,labels(v) as labels
MATCH (n)-[l]->(v)
WHERE id(n) = 68 and type(l)='belong' and 'per' in labels(v)
return id(n) as id ,type(l) as type ,labels(v) as labels
return:
id | type | labels |
68 | "belong" | ["per"] |
注意:
节点的 label是一个数组, 关系的type是一个字符串
MATCH (n {name: 'Andy'})
SET n.surname = 'Taylor'
RETURN n.name, n.surname
MATCH (n )
WHERE n.name = 'Andy'
SET n.surname = 'Taylor'
RETURN n.name, n.surname
也可以用 set 去删除属性: 将属性的值设为 null。在neo4j中属性值为null属性就消失:
MATCH (n {name: 'Andy'})
SET n.name = null
RETURN n.name, n.age
修改label:
MATCH (n {name: 'Stefan'})
SET n:German
RETURN n.name, labels(n) AS labels
MATCH (n {name: 'George'})
SET n:Swedish:Bossman
RETURN n.name, labels(n) AS labels
MATCH (n:Person {name: 'UNKNOWN'})
DELETE n
注意: 当要删除的节点存在边的时候节点删除不会成功,所以需要同时删除节点的边
MATCH (n:Person {name: 'UNKNOWN'})-[l]-()
DELETE n,l
官方提供一个关键词 DETACH, 可以删除节点的同时先删除该节点有关的边,就不用想上面手动指定:
MATCH (n {name: 'Andy'})
DETACH DELETE n
MATCH (n {name: 'Andy'})-[r:KNOWS]->()
DELETE r
插入点:
UNWIND [{age:9,name:'xiaoming'},{age:10,name:'zhangsan'}] AS row
CREATE (n: person) SET n = row
等于
UNWIND [{age:9,name:'xiaoming'},{age:10,name:'zhangsan'}] AS row
CREATE (n: person) SET n.name = row.name, n.age=row.age
插入边:
UNWIND [{from_name:'xiaoming',to_name: 'zhangsan',prop: {distance: '3000m'} }] AS row
MATCH (from)
where from.name= row.from_name
MATCH (to)
where to.name= row.to_name
CREATE (from)-[rel:KNOW]->(to)
SET rel = row.prop
UNWIND [{age:18,name:'xiaoming'}] AS row
MATCH(n: person)
WHERE n.name = 'xiaoming'
SET n.age= row.age
注意: 元素的id, 节点的label, 关系的type 如果想在参数中指定的话,只能通过 id(n) = row.xxx, labels(n)= [row.xxx], type(l) = row.xxx 等方式指定, 无法直接 (n: row.xxx) 的方式指定
其次,在创建和修改节点的 label和type时, 也是不能使用 labels(n)= [row.xxx], type(l) = row.xxx 的, 查询的时候可以用
Indexes for search performance - Neo4j Cypher Manual
索引只是加快搜索,不具备唯一性, 唯一性实现需要定义约束
创建单属性索引
CREATE INDEX index_name FOR (n:Person) ON (n.surname)
index_name 也就是索引名必须是唯一的
CREATE INDEX index_name IF NOT EXISTS FOR (n:Person) ON (n.surname)
已存在具有相同名称和/或相同架构的索引,则不会创建索引。
创建复合索引
CREATE INDEX index_name FOR (n:Person) ON (n.age, n.country)
DROP INDEX missing_index_name IF EXISTS
SHOW INDEXES
Constraints - Neo4j Cypher Manual
创建约束会自动创建对应索引,再手动创建相应索引会失败;
删除约束时会同时删除对应索引,还需要索引需要手动创建
约束类型有四种:
唯一属性约束可确保属性值对于具有特定标签的所有节点都是唯一的。唯一约束并不意味着所有节点都必须具有唯一的属性值 — 没有属性的节点不受此规则的约束。
节点属性存在约束可确保具有特定标签的所有节点都存在一个属性。尝试创建指定标签的新节点但没有此属性的查询将失败。对于尝试删除必需属性的查询也是如此。
属性存在约束可确保具有特定类型的所有关系都存在属性。尝试创建指定类型的关系但没有此属性的所有查询都将失败。对于尝试删除必需属性的查询也是如此。
节点键约束确保对于给定的标签和属性集:
所有属性都存在于具有该标签的所有节点上。
属性值的组合是唯一的。
不过后面三种约束功能只在商用版neo4j中有,所以只介绍唯一节点属性约束使用
CREATE CONSTRAINT constraint_name ON (book:Book) ASSERT book.isbn IS UNIQUE
CREATE CONSTRAINT constraint_name IF NOT EXISTS ON (book:Book) ASSERT book.isbn IS UNIQUE
DROP CONSTRAINT missing_constraint_name IF EXISTS
SHOW CONSTRAINTS
api: API Documentation — Neo4j Python Driver 4.4
Get started - Neo4j Python Driver Manual
安装:
pip install neo4j
from neo4j import GraphDatabase
driver = GraphDatabase.driver(uri, auth=(user, password))
driver.close()
查询
query_str = "MATCH (n:Person) where n.name = $my_name RETURN n"
params = {"my_name": "lvh"}
# 1
with driver.session() as session:
result = session.read_transaction(lambda tx: list(tx.run(query_str, params)))
print(result)
'''
[
>,
>
]
'''
query_str = "MATCH (n:Person) where n.name = $my_name RETURN n"
params = {"my_name": "lvh"}
def query_func(tx,query_str, params):
return list(tx.run(query_str, params))
with driver.session() as session:
result = session.read_transaction(query_func,query_str,params)
for record in result:
node = record['n']
print(type(node))
print(node)
'''
'''
# 3
query_str = "MATCH (n:Person)-[l]->(v) where n.name = $my_name RETURN n,l, v.name as other_name"
params = {"my_name": "lvh"}
with driver.session() as session:
result = session.read_transaction(lambda tx: list(tx.run(query_str, params)))
for record in result:
node = record["n"]
print(node)
l = record["l"]
print(l)
other_name = record["other_name"]
print(other_name)
'''
, ) type='KNOWS' properties={}>
y
, ) type='KNOWS' properties={}>
d
'''
result中每条记录是 Record, Record类似字典,k是 查询语句中 RETURN 的字段
详细的结构可以看api文档
批量插入:
batches = [
{
"node_name": "比亚迪"
},
{
"node_name": "小米"
},
{
"node_name": "大众"
}]
user_label = "Ner"
node_insert_cql = 'UNWIND $batches as row ' \
'CREATE (n: {_label}) SET n = row'
with driver.session() as session:
session.write_transaction(lambda tx:
tx.run(node_insert_cql.format(_label=user_label), {"batches": batches}))
插入和修改label时不能通过参数指定
简单工具类:
from neo4j import GraphDatabase
from neo4j.graph import Node, Relationship
import threading
import time
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class Neo4jDb:
_instance_lock = threading.Lock()
def __init__(self, uri, user, password):
self.driver = GraphDatabase.driver(uri, auth=(user, password))
@classmethod
def instance(cls, uri, user, password):
if not hasattr(Neo4jDb, "_instance"):
with Neo4jDb._instance_lock:
if not hasattr(Neo4jDb, "_instance"):
Neo4jDb._instance = Neo4jDb(uri, user, password)
logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 创建 Neo4jDb._instance")
return Neo4jDb._instance
def close(self):
# Don't forget to close the driver connection when you are finished with it
self.driver.close()
def get_db(self):
return self.driver.session()
def batchinto_entity(self, cql_str, Entity_batches=None):
with self.get_db() as session:
logger.debug("Neo4j write cql: %s", cql_str)
session.write_transaction(lambda tx: tx.run(cql_str, Entity_batches))
# return Map{"node":[{}], "relationship":[{}]}
def query_entity(self, cql_str, params=None):
with self.get_db() as session:
logger.debug("Neo4j query cql: %s", cql_str)
results_list = session.read_transaction(lambda tx: list(tx.run(cql_str, params)))
return results_list
# 自动划分点、边对象,分别放入列表
def partition_entity(self, result_list):
if len(result_list) == 0:
return {"node": [], "relationship": []}
node_list = {}
rel_list = {}
node_keys = []
rel_keys = []
first_record = result_list[0]
for k, v in first_record.items():
if isinstance(v, Node):
node_keys.append(k)
elif isinstance(v, Relationship):
rel_keys.append(k)
else:
logger.error("the value of {k} is not Node or Relationship", k=k)
for key in node_keys:
for record in result_list:
if record[key].id not in node_list:
node_list[record[key].id] = self._entity_to_dict(record[key])
for key in rel_keys:
for record in result_list:
if record[key].id not in node_list:
rel_list[record[key].id] = self._entity_to_dict(record[key])
return {"node": list(node_list.values()), "relationship": list(rel_list.values())}
# 点边对象转换成字段结构
def _entity_to_dict(self, entity):
attr_dict = {}
attr_dict['_id'] = entity.id
if (isinstance(entity, Node)):
label_str = ','.join([label for label in entity.labels])
if ('' != label_str):
attr_dict['_label'] = label_str
elif (isinstance(entity, Relationship)):
type_str = entity.type
if ('' != type_str):
attr_dict['_type'] = type_str
attr_dict['_startId'] = entity.start_node.id
attr_dict['_endId'] = entity.end_node.id
else:
logger.error("entity is not node or relationship")
attr_dict.update(entity.items())
return attr_dict
这是官方的驱动,还有一个官方推荐的第三方驱动,对neo4j的业务操作比较复杂的话推荐用这个第三方驱动:
The Py2neo Handbook — py2neo 2021.1
pip install --upgrade py2neo
api: Neo4j Java Driver 4.3 API
Using Neo4j from Java - Developer Guides
maven:
org.neo4j.driver
neo4j-java-driver
4.4.0
Gradle:
compile 'org.neo4j.driver:neo4j-java-driver:4.4.0'