强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

AgensGraph 完全指南 / 第 04 章:Cypher 基础

第 04 章:Cypher 基础

4.1 Cypher 简介

Cypher 是由 Neo4j 设计的声明式图查询语言,现已成为 openCypher 标准。AgensGraph 完整支持 Cypher 语法,让开发者可以用直观的 ASCII 艺术来表达图模式。

4.1.1 Cypher 的设计哲学

Cypher 核心思想: "What, not How"

  SQL:     SELECT name FROM users WHERE age > 30       → 声明"要什么"
  Cypher:  MATCH (n:Person) WHERE n.age > 30 RETURN n   → 声明"要什么模式"
  Gremlin: g.V().hasLabel('Person').has('age',gt(30))   → 描述"怎么走"

4.1.2 Cypher 语法速查

符号含义示例
( )顶点(圆括号)(n)
-->有向边(a)-[:KNOWS]->(b)
--无向边(查询时)(a)--(b)
:Label标签(p:Person)
{key: val}属性(p:Person {name: 'Alice'})
[]边的描述[r:KNOWS]
*变长路径[:KNOWS*1..3]

4.2 设置图路径

在执行 Cypher 查询之前,必须先设置图路径(graph_path):

-- 创建图(如果不存在)
CREATE GRAPH IF NOT EXISTS demo;

-- 设置当前图路径
SET graph_path = demo;

-- 验证
SHOW graph_path;

4.3 CREATE — 创建数据

4.3.1 创建顶点

-- 创建一个简单顶点
CREATE (n:Person {name: '张三', age: 30});
RETURN n;

-- 创建多个标签的顶点
CREATE (n:Person:Employee {name: '李四', age: 28, department: '技术部'});
RETURN n;

-- 创建无标签顶点
CREATE (n {value: 42});
RETURN n;

-- 创建并返回
CREATE (n:Person {name: '王五'})
RETURN id(n) AS node_id, n.name AS name;

4.3.2 创建边

-- 创建两个顶点和它们之间的边
CREATE (a:Person {name: 'Alice'})
CREATE (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS {since: 2020}]->(b)
RETURN a, b;

-- 在已存在的顶点之间创建边
MATCH (a:Person {name: 'Alice'})
MATCH (b:Person {name: 'Bob'})
CREATE (a)-[:WORKS_WITH {project: 'GraphDB'}]->(b)
RETURN a, b;

4.3.3 创建复杂结构

-- 一次创建完整的子图
CREATE
  (alice:Person {name: 'Alice', age: 30}),
  (bob:Person {name: 'Bob', age: 28}),
  (carol:Person {name: 'Carol', age: 32}),
  (company:Company {name: 'TechCorp', founded: 2015}),
  (alice)-[:KNOWS {since: 2018}]->(bob),
  (bob)-[:KNOWS {since: 2019}]->(carol),
  (alice)-[:WORKS_AT {position: 'Tech Lead', since: 2020}]->(company),
  (bob)-[:WORKS_AT {position: 'Engineer', since: 2021}]->(company)
RETURN alice, bob, carol, company;

4.3.4 CREATE 注意事项

注意

  • CREATE 总是创建新元素,即使重复也会创建(可能产生重复数据)
  • 需要避免重复时,应使用 MERGE(见 4.6 节)
  • 边必须连接两个顶点,不能创建孤立的边

4.4 MATCH — 模式匹配

MATCH 是 Cypher 的核心语句,用于在图中查找符合特定模式的数据。

4.4.1 基本顶点匹配

-- 匹配所有顶点
MATCH (n)
RETURN n;

-- 匹配带标签的顶点
MATCH (p:Person)
RETURN p;

-- 匹配带属性的顶点
MATCH (p:Person {name: 'Alice'})
RETURN p;

-- 等价的 WHERE 写法
MATCH (p:Person)
WHERE p.name = 'Alice'
RETURN p;

4.4.2 边的匹配

-- 匹配有向关系
MATCH (a:Person)-[:KNOWS]->(b:Person)
RETURN a.name, b.name;

-- 匹配无向关系(双向都匹配)
MATCH (a:Person)--(b:Person)
RETURN a.name, b.name;

-- 匹配特定方向的关系(变量绑定)
MATCH (a:Person)-[r:KNOWS]->(b:Person)
RETURN a.name, type(r) AS relationship, b.name, r.since;

4.4.3 多模式匹配

-- 链式匹配:找朋友的朋友
MATCH (a:Person)-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)
RETURN a.name, b.name, c.name;

-- 分支匹配:找同时有工作关系和朋友关系的
MATCH (a:Person)-[:KNOWS]->(b:Person)
MATCH (a)-[:WORKS_AT]->(c:Company)
RETURN a.name, b.name AS friend, c.name AS company;

4.4.4 WHERE 条件过滤

-- 比较操作
MATCH (p:Person)
WHERE p.age > 25
RETURN p.name, p.age;

-- 字符串匹配
MATCH (p:Person)
WHERE p.name STARTS WITH 'A'
RETURN p.name;

-- 正则表达式
MATCH (p:Person)
WHERE p.name =~ '.*li.*'
RETURN p.name;

-- IN 操作
MATCH (p:Person)
WHERE p.name IN ['Alice', 'Bob', 'Carol']
RETURN p.name;

-- 空值检查
MATCH (p:Person)
WHERE p.email IS NOT NULL
RETURN p.name, p.email;

-- 逻辑组合
MATCH (p:Person)
WHERE p.age >= 25 AND p.age <= 35 AND p.city = '北京'
RETURN p.name, p.age;

4.4.5 WHERE 条件操作符汇总

操作符说明示例
=等于WHERE n.age = 30
<>不等于WHERE n.status <> 'deleted'
<, >, <=, >=比较WHERE n.age > 25
IS NULL / IS NOT NULL空值检查WHERE n.email IS NOT NULL
STARTS WITH前缀匹配WHERE n.name STARTS WITH 'A'
ENDS WITH后缀匹配WHERE n.email ENDS WITH '.com'
CONTAINS包含WHERE n.bio CONTAINS '图数据库'
=~正则匹配WHERE n.phone =~ '1[3-9]\\d{9}'
IN列表成员WHERE n.id IN [1, 2, 3]
AND / OR / NOT逻辑运算WHERE a AND (b OR c)

4.5 RETURN — 结果返回

4.5.1 返回顶点和边

-- 返回整个顶点
MATCH (p:Person)
RETURN p;

-- 返回属性
MATCH (p:Person)
RETURN p.name, p.age;

-- 使用别名
MATCH (p:Person)-[r:KNOWS]->(f:Person)
RETURN p.name AS person, r.since AS since_when, f.name AS friend;

-- 返回去重结果
MATCH (p:Person)-[:KNOWS]->(f:Person)
RETURN DISTINCT p.name;

-- 返回所有元素
MATCH (p:Person)-[r:KNOWS]->(f:Person)
RETURN *;

4.5.2 排序与分页

-- ORDER BY 排序
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age DESC;

-- SKIP + LIMIT 分页
MATCH (p:Person)
RETURN p.name, p.age
ORDER BY p.age DESC
SKIP 5
LIMIT 10;

-- 典型的分页公式
-- 第 page 页,每页 size 条
-- SKIP (page - 1) * size
-- LIMIT size

4.5.3 聚合函数

-- COUNT 计数
MATCH (p:Person)
RETURN count(p) AS total_persons;

-- 分组计数
MATCH (p:Person)-[:WORKS_AT]->(c:Company)
RETURN c.name AS company, count(p) AS employee_count
ORDER BY employee_count DESC;

-- 其他聚合
MATCH (p:Person)
RETURN
  count(p) AS total,
  avg(p.age) AS avg_age,
  min(p.age) AS min_age,
  max(p.age) AS max_age,
  sum(p.age) AS sum_age,
  collect(p.name) AS all_names;

4.6 MERGE — 创建或匹配

MERGE 是 Cypher 中最重要的语句之一:如果模式已存在则匹配,不存在则创建(“upsert"语义)。

4.6.1 基本 MERGE

-- 如果 Alice 存在则匹配,否则创建
MERGE (p:Person {name: 'Alice'})
ON CREATE SET p.created = datetime(), p.source = 'import'
ON MATCH SET p.last_seen = datetime()
RETURN p;

4.6.2 MERGE 的 ON CREATE 和 ON MATCH

-- 导入场景:用户注册(首次创建 vs 重复登录)
MERGE (u:User {email: '[email protected]'})
ON CREATE SET
  u.name = 'Alice',
  u.created = datetime(),
  u.login_count = 1
ON MATCH SET
  u.last_login = datetime(),
  u.login_count = u.login_count + 1
RETURN u;

4.6.3 MERGE 边

-- 确保两个顶点和它们之间的关系都存在
MATCH (a:Person {name: 'Alice'})
MATCH (b:Person {name: 'Bob'})
MERGE (a)-[r:KNOWS]->(b)
ON CREATE SET r.since = datetime()
RETURN a, r, b;

注意MERGE 会检查整个模式是否完全匹配。如果只部分存在,可能会创建重复数据。务必在唯一约束(Unique Constraint)或已知唯一的属性上使用 MERGE

4.6.4 MERGE 与 CREATE 的区别

语句行为是否幂等使用场景
CREATE总是创建❌ 否批量导入确定不存在的数据
MERGE存在则匹配,不存在则创建✅ 是去重导入、确保数据一致性

4.7 SET — 更新属性

-- 设置属性
MATCH (p:Person {name: 'Alice'})
SET p.age = 31, p.updated = datetime()
RETURN p;

-- 设置多个属性(Map 语法)
MATCH (p:Person {name: 'Alice'})
SET p += {age: 31, city: '上海', role: 'manager'}
RETURN p;

-- 添加标签
MATCH (p:Person {name: 'Alice'})
SET p:VIP:Manager
RETURN p, labels(p);

-- 移除属性
MATCH (p:Person {name: 'Alice'})
REMOVE p.role
RETURN p;

-- 移除标签
MATCH (p:Person {name: 'Alice'})
REMOVE p:VIP
RETURN p, labels(p);

4.8 DELETE — 删除数据

4.8.1 删除顶点

-- 删除单个顶点
MATCH (p:Person {name: 'Alice'})
DELETE p;

-- 带条件删除
MATCH (p:Person)
WHERE p.age < 18 AND p.status = 'inactive'
DELETE p;

4.8.2 删除边

-- 删除关系
MATCH (a:Person {name: 'Alice'})-[r:KNOWS]->(b:Person {name: 'Bob'})
DELETE r;

4.8.3 DETACH DELETE — 级联删除

-- 删除顶点及其所有关系(危险操作!)
MATCH (p:Person {name: 'Alice'})
DETACH DELETE p;

-- 删除所有数据(慎用!)
MATCH (n)
DETACH DELETE n;

注意

  • 普通 DELETE 不能删除有边连接的顶点,会报错
  • DETACH DELETE 会先删除所有连接的边,再删除顶点
  • 生产环境中务必带 WHERE 条件,避免误删

4.9 完整业务场景:员工管理系统

-- 创建部门
CREATE (:Department {name: '技术部', budget: 500000});
CREATE (:Department {name: '产品部', budget: 300000});
CREATE (:Department {name: '市场部', budget: 200000});

-- 创建员工
CREATE (:Employee {name: '张三', age: 30, salary: 25000, title: '高级工程师'});
CREATE (:Employee {name: '李四', age: 28, salary: 20000, title: '工程师'});
CREATE (:Employee {name: '王五', age: 35, salary: 30000, title: '技术总监'});
CREATE (:Employee {name: '赵六', age: 26, salary: 15000, title: '产品经理'});

-- 创建部门归属关系
MATCH (e:Employee {name: '张三'}), (d:Department {name: '技术部'})
CREATE (e)-[:BELONGS_TO {since: 2020}]->(d);

MATCH (e:Employee {name: '李四'}), (d:Department {name: '技术部'})
CREATE (e)-[:BELONGS_TO {since: 2021}]->(d);

MATCH (e:Employee {name: '王五'}), (d:Department {name: '技术部'})
CREATE (e)-[:BELONGS_TO {since: 2018}]->(d);

MATCH (e:Employee {name: '赵六'}), (d:Department {name: '产品部'})
CREATE (e)-[:BELONGS_TO {since: 2022}]->(d);

-- 创建汇报关系
MATCH (e:Employee {name: '张三'}), (m:Employee {name: '王五'})
CREATE (e)-[:REPORTS_TO]->(m);

MATCH (e:Employee {name: '李四'}), (m:Employee {name: '王五'})
CREATE (e)-[:REPORTS_TO]->(m);

查询:统计各部门员工数量

MATCH (e:Employee)-[:BELONGS_TO]->(d:Department)
RETURN d.name AS department, count(e) AS headcount, avg(e.salary) AS avg_salary
ORDER BY headcount DESC;

查询结果

departmentheadcountavg_salary
技术部325000.0
产品部115000.0

查询:找到某人的所有下属

MATCH (manager:Employee {name: '王五'})<-[:REPORTS_TO*]-(sub:Employee)
RETURN sub.name AS subordinate, sub.title AS title;
subordinatetitle
张三高级工程师
李四工程师

4.10 常见错误与陷阱

陷阱 1:CREATE 产生重复

-- ❌ 错误:每次运行都会创建新节点
CREATE (p:Person {name: 'Alice'})
RETURN p;

-- ✅ 正确:使用 MERGE 避免重复
MERGE (p:Person {name: 'Alice'})
RETURN p;

陷阱 2:MATCH 失败不会报错

-- 如果 Person 标签下没有数据,返回空结果集(不报错)
MATCH (p:Person {name: '不存在的人'})
RETURN p.name;
-- 结果: (0 rows)

陷阱 3:忘记 SET 图路径

-- ❌ 错误:未设置 graph_path
MATCH (n:Person) RETURN n;
-- ERROR: graph_path is not set

-- ✅ 正确
SET graph_path = demo;
MATCH (n:Person) RETURN n;

陷阱 4:DELETE 有边的顶点

-- ❌ 错误:直接删除有边连接的顶点
MATCH (p:Person {name: 'Alice'})
DELETE p;
-- ERROR: cannot delete vertex with edges

-- ✅ 正确:先删边或使用 DETACH DELETE
MATCH (p:Person {name: 'Alice'})
DETACH DELETE p;

4.11 本章小结

操作语法幂等性典型场景
CREATECREATE (n:Label {props})批量导入
MATCHMATCH (pattern) WHERE cond-查询数据
MERGEMERGE (pattern) ON CREATE/MATCH去重导入
SETSET n.prop = val更新属性
DELETEDELETE n / DETACH DELETE n-删除数据
RETURNRETURN expr AS alias-定义输出
WHEREWHERE condition-条件过滤
ORDER BYORDER BY expr DESC-结果排序
SKIP/LIMITSKIP n LIMIT m-分页查询

4.12 练习

  1. 创建一个包含 3 个 Product 节点和 3 个 Category 节点的图,并建立 BELONGS_TO 关系。
  2. 使用 MERGE 实现一个"确保用户存在并更新最后登录时间"的逻辑。
  3. 编写查询:找出所有价格大于 100 的产品及其所属分类。
  4. 编写查询:统计每个分类下的产品数量,并按数量降序排列。

4.13 扩展阅读