📖 Cypher查询语言
Cypher是Neo4j的声明式图查询语言,使用ASCII艺术风格描述图模式——用圆括号表示节点,箭头表示关系,非常直观。
🎯 Cypher核心语法
| 子句 | 用途 | 示例 |
| MATCH | 模式匹配 | MATCH (n:Person) RETURN n |
| WHERE | 条件过滤 | WHERE n.age > 30 |
| RETURN | 返回结果 | RETURN n.name, n.age |
| ORDER BY | 排序 | ORDER BY n.age DESC |
| LIMIT | 限制数量 | LIMIT 10 |
| CREATE | 创建数据 | CREATE (n:Person {name: 'Tom'}) |
| MERGE | 创建或匹配 | MERGE (n:Person {name: 'Tom'}) |
| DELETE | 删除 | DELETE n |
| SET | 修改属性 | SET n.age = 30 |
| WITH | 管道传递 | WITH n ORDER BY n.age |
💻 Python实现:Cypher解析与执行引擎
import re
from typing import List, Dict, Any, Optional
class CypherEngine:
"""简易Cypher查询引擎"""
def __init__(self, graph):
self.graph = graph
def execute(self, query):
">>>执行Cypher查询(支持MATCH/WHERE/RETURN/ORDER BY/LIMIT)"""
query = query.strip()
if query.upper().startswith("MATCH"):
return self._execute_match(query)
elif query.upper().startswith("CREATE"):
return self._execute_create(query)
return {"error": "不支持的查询类型"}
def _parse_match(self, query):
"""解析MATCH查询"""
result = {"patterns": [], "where": None, "return_items": [], "order_by": None, "limit": None}
match_match = re.search(r'MATCH\s+(.+?)(?:\s+WHERE|\s+RETURN|$)', query, re.IGNORECASE)
if match_match:
pattern_str = match_match.group(1).strip()
node_pattern = re.findall(r'\((\w+)(?::(\w+))?\)', pattern_str)
result["patterns"] = [(var, label) for var, label in node_pattern]
where_match = re.search(r'WHERE\s+(.+?)(?:\s+RETURN|$)', query, re.IGNORECASE)
if where_match:
result["where"] = where_match.group(1).strip()
return_match = re.search(r'RETURN\s+(.+?)(?:\s+ORDER|\s+LIMIT|$)', query, re.IGNORECASE)
if return_match:
items = return_match.group(1).strip().split(",")
result["return_items"] = [item.strip() for item in items]
order_match = re.search(r'ORDER\s+BY\s+(.+?)(?:\s+LIMIT|$)', query, re.IGNORECASE)
if order_match:
result["order_by"] = order_match.group(1).strip()
limit_match = re.search(r'LIMIT\s+(\d+)', query, re.IGNORECASE)
if limit_match:
result["limit"] = int(limit_match.group(1))
return result
def _execute_match(self, query):
parsed = self._parse_match(query)
results = []
bindings_list = [{}]
for var, label in parsed["patterns"]:
new_bindings = []
for bindings in bindings_list:
nodes = self.graph.match_nodes(label)
for nid, node in nodes:
new_binding = dict(bindings)
new_binding[var] = (nid, node)
new_bindings.append(new_binding)
bindings_list = new_bindings
if parsed["where"]:
filtered = []
for bindings in bindings_list:
try:
where_str = parsed["where"]
for var, (nid, node) in bindings.items():
for k, v in node["properties"].items():
where_str = where_str.replace(f"{var}.{k}", repr(v))
if eval(where_str):
filtered.append(bindings)
except:
pass
bindings_list = filtered
for bindings in bindings_list:
row = {}
for item in parsed["return_items"]:
if "." in item:
var, prop = item.split(".", 1)
if var in bindings:
row[item] = bindings[var][1]["properties"].get(prop)
elif item in bindings:
row[item] = bindings[item][1]["properties"]
results.append(row)
if parsed["order_by"]:
desc = "DESC" in parsed["order_by"].upper()
key = parsed["order_by"].replace("DESC", "").replace("ASC", "").strip()
results.sort(key=lambda r: r.get(key, 0), reverse=desc)
if parsed["limit"]:
results = results[:parsed["limit"]]
return results
def _execute_create(self, query):
"""执行CREATE语句"""
node_pattern = re.search(r'\((\w+):(\w+)\s*\{([^}]*)\}\)', query)
if node_pattern:
var, label, props_str = node_pattern.groups()
properties = {}
for pair in props_str.split(","):
if ":" in pair:
k, v = pair.split(":", 1)
properties[k.strip()] = v.strip().strip(""'")
nid = self.graph.create_node(label, properties)
return {"created": nid, "properties": properties}
return {"error": "无法解析CREATE语句"}
class NG:
"""简化图用于演示Cypher"""
def __init__(self):
self.nodes = {}
self._id = 0
self.label_index = {}
def create_node(self, label, properties):
nid = self._id; self._id += 1
self.nodes[nid] = {"labels": {label}, "properties": properties}
self.label_index.setdefault(label, []).append(nid)
return nid
def match_nodes(self, label):
return [(nid, self.nodes[nid]) for nid in self.label_index.get(label, [])]
ng = NG()
ng.create_node("Person", {"name": "鲁迅", "born": 1881})
ng.create_node("Person", {"name": "老舍", "born": 1899})
ng.create_node("Person", {"name": "徐志摩", "born": 1897})
ng.create_node("Book", {"name": "呐喊", "year": 1923})
ng.create_node("Book", {"name": "骆驼祥子", "year": 1937})
engine = CypherEngine(ng)
print("=== MATCH (n:Person) RETURN n.name, n.born ===")
for row in engine.execute("MATCH (n:Person) RETURN n.name, n.born"):
print(f" {row}")
print("
=== MATCH (n:Person) WHERE n.born > 1890 RETURN n.name ===")
for row in engine.execute("MATCH (n:Person) WHERE n.born > 1890 RETURN n.name"):
print(f" {row}")
print("
=== CREATE (n:Person {name: '巴金', born: 1904}) ===")
print(engine.execute("CREATE (n:Person {name: '巴金', born: 1904})"))
=== MATCH (n:Person) RETURN n.name, n.born ===
{'n.name': '鲁迅', 'n.born': 1881}
{'n.name': '老舍', 'n.born': 1899}
{'n.name': '徐志摩', 'n.born': 1897}
=== MATCH (n:Person) WHERE n.born > 1890 RETURN n.name ===
{'n.name': '老舍'}
{'n.name': '徐志摩'}
=== CREATE (n:Person {name: '巴金', born: 1904}) ===
{'created': 5, 'properties': {'name': '巴金', 'born': 1904}}
🔧 Cypher高级查询模式
路径查询
- MATCH p=(a)-[*1..3]->(b) 路径长度1-3跳
- MATCH p=shortestPath((a)-[*]->(b)) 最短路径
- RETURN length(p) 路径长度
- RETURN nodes(p), relationships(p) 路径中的节点和关系
聚合查询
- COUNT(n) 计数
- AVG(n.age) 平均值
- COLLECT(n.name) 聚合为数组
- GROUP BY 分组(Cypher中通过WITH隐式分组)
📝 实战练习
练习1:实现多跳路径查询
扩展CypherEngine支持 MATCH (a)-[r1]->(b)-[r2]->(c) 模式。
练习2:实现聚合函数
添加COUNT、AVG、MAX等聚合函数支持。
练习3:实现MERGE
MERGE语义:存在则返回,不存在则创建。
🔤
🏆 第13课成就解锁
Cypher查询师
🔤 Cypher语法
🔍 MATCH/WHERE
📊 聚合查询
🛤️ 路径查询