跳到主要内容

高级查询

本章介绍 Cypher 的高级功能,包括聚合函数、路径查找、条件表达式等,帮助你处理更复杂的查询场景。

聚合函数

常用聚合函数

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

-- 统计不同城市的数量
MATCH (p:Person)
RETURN count(DISTINCT p.city) AS cityCount

-- SUM:求和
MATCH (p:Product)
RETURN sum(p.price) AS totalValue

-- AVG:平均值
MATCH (p:Person)
RETURN avg(p.age) AS averageAge

-- MIN/MAX:最值
MATCH (p:Person)
RETURN min(p.age) AS youngest, max(p.age) AS oldest

-- COLLECT:收集为列表
MATCH (p:Person)
RETURN collect(p.name) AS allNames

分组聚合

-- 按城市统计人数
MATCH (p:Person)
RETURN p.city, count(p) AS count
ORDER BY count DESC

-- 统计每个标签的节点数
MATCH (n)
RETURN labels(n), count(n)

-- 统计每种关系的数量
MATCH ()-[r]->()
RETURN type(r), count(r)

路径查找

最短路径

-- 查找两个节点间的最短路径
MATCH path = shortestPath(
(a:Person {name: '张三'})-[:FRIEND*]-(b:Person {name: '王五'})
)
RETURN path, length(path) AS hops

-- 限制最大深度
MATCH path = shortestPath(
(a:Person {name: '张三'})-[:FRIEND*..10]-(b:Person {name: '王五'})
)
RETURN path

所有路径

-- 查找所有路径
MATCH paths = allShortestPaths(
(a:Person {name: '张三'})-[:FRIEND*]-(b:Person {name: '王五'})
)
RETURN paths

-- 查找特定长度的路径
MATCH path = (a:Person {name: '张三'})-[:FRIEND*3]-(b)
WHERE a <> b
RETURN path

路径函数

-- 获取路径中的节点
MATCH path = (a:Person)-[:FRIEND*2]->(b)
RETURN nodes(path)

-- 获取路径中的关系
MATCH path = (a:Person)-[:FRIEND*2]->(b)
RETURN relationships(path)

-- 获取路径长度
MATCH path = (a:Person)-[:FRIEND*2]->(b)
RETURN length(path)

条件表达式

CASE 表达式

-- 简单 CASE
MATCH (p:Person)
RETURN p.name,
CASE p.age
WHEN 20 THEN '二十岁'
WHEN 30 THEN '三十岁'
ELSE '其他年龄'
END AS ageGroup

-- 搜索 CASE
MATCH (p:Person)
RETURN p.name,
CASE
WHEN p.age < 18 THEN '未成年'
WHEN p.age >= 18 AND p.age < 60 THEN '成年人'
ELSE '老年人'
END AS ageGroup

COALESCE 函数

-- 返回第一个非空值
MATCH (p:Person)
RETURN p.name, coalesce(p.nickname, p.name, 'Unknown') AS displayName

列表操作

列表函数

-- 创建列表
RETURN [1, 2, 3, 4, 5] AS numbers

-- 范围列表
RETURN range(1, 10) AS numbers
RETURN range(1, 10, 2) AS oddNumbers

-- 列表大小
RETURN size([1, 2, 3]) AS listSize

-- 反转列表
RETURN reverse([1, 2, 3]) AS reversed

-- 提取子列表
RETURN [1, 2, 3, 4, 5][0..3] AS sublist

列表推导式

-- 过滤列表
WITH [1, 2, 3, 4, 5] AS numbers
RETURN [x IN numbers WHERE x > 2] AS filtered

-- 映射列表
WITH [1, 2, 3, 4, 5] AS numbers
RETURN [x IN numbers | x * 2] AS doubled

-- 过滤并映射
WITH [1, 2, 3, 4, 5] AS numbers
RETURN [x IN numbers WHERE x > 2 | x * 2] AS result

列表聚合

-- 使用 reduce 累加
WITH [1, 2, 3, 4, 5] AS numbers
RETURN reduce(sum = 0, x IN numbers | sum + x) AS total

-- 使用 extract 提取属性
MATCH (p:Person)
WITH collect(p) AS people
RETURN extract(x IN people | x.name) AS names

字符串函数

-- 字符串长度
RETURN length('Hello') AS strLength

-- 截取子串
RETURN substring('Hello World', 0, 5) AS sub

-- 替换
RETURN replace('Hello World', 'World', 'Neo4j') AS replaced

-- 转小写/大写
RETURN toLower('Hello') AS lower, toUpper('Hello') AS upper

-- 去除空白
RETURN trim(' Hello ') AS trimmed

-- 分割字符串
RETURN split('a,b,c', ',') AS parts

-- 连接字符串
RETURN join(['a', 'b', 'c'], '-') AS joined

数学函数

-- 绝对值
RETURN abs(-10) AS absolute

-- 四舍五入
RETURN round(3.14) AS rounded

-- 向上/向下取整
RETURN ceil(3.14) AS up, floor(3.14) AS down

-- 随机数
RETURN rand() AS random

-- 幂运算
RETURN pow(2, 3) AS power

-- 平方根
RETURN sqrt(16) AS squareRoot

日期时间函数

-- 当前日期时间
RETURN datetime() AS now
RETURN date() AS today
RETURN time() AS currentTime

-- 创建日期
RETURN date('2024-01-15') AS specificDate

-- 日期运算
RETURN date() + duration('P1D') AS tomorrow
RETURN date() - duration('P7D') AS lastWeek

-- 日期组件
WITH datetime() AS now
RETURN now.year, now.month, now.day, now.hour, now.minute

-- 格式化日期
RETURN datetime().epochMillis AS timestamp

模式理解(Pattern Comprehension)

模式理解是 Cypher 中一种简洁而强大的语法,它允许你在表达式中直接匹配模式并收集结果。相比传统的 MATCH + RETURN 组合,模式理解更加简洁高效。

什么是模式理解

模式理解的基本语法是 [变量 IN 模式 WHERE 条件 | 表达式],它的工作方式是:

  1. 匹配指定的图模式
  2. 可选地过滤匹配结果
  3. 对每个匹配结果计算表达式
  4. 将所有结果收集为列表返回

基本用法

-- 收集朋友的名字列表
MATCH (p:Person)
RETURN p.name, [(p)-[:FRIEND]->(friend) | friend.name] AS friends

-- 等价于传统写法
MATCH (p:Person)
OPTIONAL MATCH (p)-[:FRIEND]->(friend)
RETURN p.name, collect(friend.name) AS friends

关键区别

  • 模式理解自动处理 OPTIONAL 情况(无匹配时返回空列表)
  • 语法更简洁,一行代码完成匹配和收集
  • 性能通常更好,因为减少了中间结果集

带过滤条件的模式理解

可以在模式理解中添加 WHERE 条件:

-- 只收集北京的朋友
MATCH (p:Person)
RETURN p.name,
[(p)-[:FRIEND]->(friend) WHERE friend.city = '北京' | friend.name] AS beijingFriends

-- 收集最近的好友关系
MATCH (p:Person)
RETURN p.name,
[(p)-[r:FRIEND]->(friend) WHERE r.since > date('2023-01-01') | friend.name] AS recentFriends

收集关系属性

模式理解不仅可以收集节点,还可以收集关系属性:

-- 收集好友关系建立时间
MATCH (p:Person)
RETURN p.name,
[(p)-[r:FRIEND]->() | r.since] AS friendSinceDates

-- 收集带权重的评分
MATCH (p:Person)
RETURN p.name,
[(p)-[r:RATED]->(movie) | {title: movie.title, score: r.score}] AS ratings

返回结构化数据

模式理解可以返回复杂的数据结构:

-- 返回朋友及其城市信息
MATCH (p:Person)
RETURN p.name,
[(p)-[:FRIEND]->(f) | {name: f.name, city: f.city, age: f.age}] AS friendDetails

-- 返回交易记录
MATCH (account:Account)
RETURN account.accountId,
[(account)-[t:TRANSFERRED]->(to:Account) | {
toAccount: to.accountId,
amount: t.amount,
date: t.date
}] AS transfers

多跳关系

模式理解可以处理多跳关系:

-- 收集朋友的朋友(去重)
MATCH (p:Person)
RETURN p.name,
[(p)-[:FRIEND]->()-[:FRIEND]->(fof) WHERE fof <> p | fof.name] AS friendsOfFriends

-- 指定深度的路径
MATCH (p:Person)
RETURN p.name,
[(p)-[:FRIEND*2]-(connection) WHERE connection <> p | connection.name] AS secondDegreeConnections

在 WHERE 子句中使用

模式理解可以在 WHERE 子句中用于条件判断:

-- 找出有至少 3 个朋友的人
MATCH (p:Person)
WHERE size([(p)-[:FRIEND]->(f) | 1]) >= 3
RETURN p.name

-- 找出朋友都是北京人的人
MATCH (p:Person)
WHERE all(f IN [(p)-[:FRIEND]->(friend) | friend.city] WHERE f = '北京')
RETURN p.name

-- 使用模式理解进行存在性检查
MATCH (p:Person)
WHERE size([(p)-[:FRIEND]->(f) WHERE f.city = '上海' | 1]) > 0
RETURN p.name AS hasShanghaiFriend

性能优势

模式理解相比传统写法有以下性能优势:

  1. 减少中间结果:不需要额外的 WITH 子句传递变量
  2. 自动优化:查询优化器可以更好地优化模式理解
  3. 内存效率:直接生成列表,避免临时关系存储
-- 性能对比示例

-- 传统写法(较慢)
MATCH (p:Person)
OPTIONAL MATCH (p)-[:FRIEND]->(friend)
WITH p, collect(friend.name) AS friends
OPTIONAL MATCH (p)-[:COLLEAGUE]->(colleague)
WITH p, friends, collect(colleague.name) AS colleagues
RETURN p.name, friends, colleagues

-- 模式理解写法(更快)
MATCH (p:Person)
RETURN p.name,
[(p)-[:FRIEND]->(f) | f.name] AS friends,
[(p)-[:COLLEAGUE]->(c) | c.name] AS colleagues

实际应用示例

-- 构建用户画像
MATCH (u:User)
RETURN {
name: u.name,
friends: [(u)-[:FRIEND]->(f) | f.name],
interests: [(u)-[:INTERESTED_IN]->(i) | i.name],
recentPosts: [(u)-[:POSTED]->(p) ORDER BY p.createdAt DESC LIMIT 3 | p.title],
totalFriends: size([(u)-[:FRIEND]->() | 1])
} AS userProfile

-- 推荐系统:收集用户行为特征
MATCH (u:User {userId: 'u001'})
RETURN {
viewedProducts: [(u)-[:VIEWED]->(p:Product) | p.productId],
purchasedProducts: [(u)-[:PURCHASED]->(p:Product) | p.productId],
likedCategories: [(u)-[:LIKED]->(c:Category) | c.name]
} AS userFeatures

UNWIND 展开列表

-- 展开列表为行
WITH [1, 2, 3] AS numbers
UNWIND numbers AS num
RETURN num

-- 处理嵌套列表
WITH [[1, 2], [3, 4], [5, 6]] AS nested
UNWIND nested AS inner
UNWIND inner AS num
RETURN num

-- 实际应用:批量创建
WITH ['张三', '李四', '王五'] AS names
UNWIND names AS name
CREATE (p:Person {name: name})
RETURN p

调用过程(CALL)

-- 列出所有标签
CALL db.labels() YIELD label
RETURN label

-- 列出所有关系类型
CALL db.relationshipTypes() YIELD relationshipType
RETURN relationshipType

-- 列出所有属性键
CALL db.propertyKeys() YIELD propertyKey
RETURN propertyKey

-- 使用 APOC 库(需要安装)
CALL apoc.meta.stats() YIELD stats
RETURN stats

子查询

子查询允许你在主查询内部嵌套执行另一个查询,这对于复杂的分析场景非常有用。Neo4j 4.0+ 引入了 CALL { ... } 语法来支持子查询。

子查询的作用

子查询的主要作用包括:

  1. 计算聚合值后再过滤:先计算每个节点的聚合值,然后基于聚合结果进行筛选
  2. 模块化复杂查询:将复杂查询拆分为逻辑清晰的部分
  3. 性能优化:在特定上下文中执行查询,减少中间结果集
  4. 避免笛卡尔积:使用子查询可以避免多个独立 MATCH 产生的笛卡尔积

基本子查询语法

使用 CALL { ... } 包裹子查询:

-- 基本结构
MATCH (p:Person)
CALL {
-- 子查询从这里开始
WITH p -- 导入外部变量
MATCH (p)-[:FRIEND]->(friend)
RETURN count(friend) AS friendCount -- 必须返回结果
}
RETURN p.name, friendCount

关键规则

  • 子查询必须以 WITH 子句导入外部变量
  • 子查询必须以 RETURN 子句返回结果
  • 返回的变量名不能与外部变量名冲突

计算聚合后过滤

一个常见的场景是先计算每个节点的聚合值,然后基于聚合结果进行筛选:

-- 找出朋友数超过 5 的人
MATCH (p:Person)
CALL {
WITH p
MATCH (p)-[:FRIEND]->(friend)
RETURN count(friend) AS friendCount
}
WHERE friendCount > 5
RETURN p.name, friendCount
ORDER BY friendCount DESC

-- 统计每个用户的活跃度得分
MATCH (u:User)
CALL {
WITH u
OPTIONAL MATCH (u)-[:POSTED]->(post:Post)
OPTIONAL MATCH (u)-[:COMMENTED]->(comment:Comment)
RETURN count(DISTINCT post) * 3 + count(DISTINCT comment) AS activityScore
}
RETURN u.name, activityScore
ORDER BY activityScore DESC
LIMIT 10

EXISTS 子查询

EXISTS 子查询用于检查是否存在满足条件的模式,返回布尔值:

-- 找出有北京朋友的人
MATCH (p:Person)
WHERE EXISTS {
MATCH (p)-[:FRIEND]->(f) WHERE f.city = '北京'
}
RETURN p.name

-- 找出没有任何关系的孤立节点
MATCH (p:Person)
WHERE NOT EXISTS {
MATCH (p)--()
}
RETURN p.name

-- 复杂条件检查
MATCH (p:Person)
WHERE EXISTS {
MATCH (p)-[:FRIEND]->(friend)-[:LIVES_IN]->(city:City)
WHERE city.name = '北京' AND friend.age > 30
}
RETURN DISTINCT p.name

COUNT 子查询

COUNT 子查询直接返回匹配模式的数量:

-- 统计每个用户的活跃朋友数
MATCH (p:Person)
RETURN p.name, count {
MATCH (p)-[:FRIEND]->(friend)
WHERE friend.lastActive > date() - duration('P30D')
} AS activeFriendCount
ORDER BY activeFriendCount DESC

-- 统计间接连接数
MATCH (p:Person)
RETURN p.name, count {
MATCH (p)-[:FRIEND]->()-[:FRIEND]->(fof)
WHERE fof <> p
} AS friendsOfFriendsCount

子查询中的事务控制

对于修改数据的子查询,可以使用 IN TRANSACTIONS 来控制事务:

-- 批量更新操作
MATCH (p:Person)
CALL {
WITH p
MATCH (p)-[:FRIEND]->(friend)
SET friend.contactCount = coalesce(friend.contactCount, 0) + 1
} IN TRANSACTIONS OF 1000 ROWS

嵌套子查询

子查询可以嵌套使用,但要注意可读性:

-- 复杂分析:找出影响力高的活跃用户
MATCH (p:Person)
CALL {
WITH p
MATCH (p)-[:FRIEND]->(friend)
CALL {
WITH friend
MATCH (friend)-[:POSTED]->(post)
RETURN count(post) AS postCount
}
RETURN sum(postCount) AS totalFriendPosts
}
WHERE totalFriendPosts > 10
RETURN p.name, totalFriendPosts
ORDER BY totalFriendPosts DESC

综合示例

社交网络分析

-- 找出影响力最大的用户(朋友最多)
MATCH (p:Person)
OPTIONAL MATCH (p)-[:FRIEND]-(friend)
WITH p, count(friend) AS friendCount
ORDER BY friendCount DESC
RETURN p.name, friendCount
LIMIT 10

-- 找出共同好友
MATCH (a:Person {name: '张三'})-[:FRIEND]-(common)-[:FRIEND]-(b:Person {name: '李四'})
WHERE a <> b
RETURN common.name AS commonFriend

-- 计算聚类系数(朋友间也是朋友的比例)
MATCH (p:Person {name: '张三'})-[:FRIEND]-(friend)
WITH p, collect(friend) AS friends, count(friend) AS friendCount
MATCH (f1)-[:FRIEND]-(f2)
WHERE f1 IN friends AND f2 IN friends AND f1 <> f2
RETURN friendCount, count(*)/2.0 / (friendCount * (friendCount - 1) / 2.0) AS clusteringCoefficient

推荐系统

-- 基于共同好友的推荐
MATCH (user:Person {name: '张三'})-[:FRIEND]-(friend)-[:FRIEND]-(potential)
WHERE NOT (user)-[:FRIEND]-(potential) AND user <> potential
WITH potential, count(friend) AS commonFriends
ORDER BY commonFriends DESC
RETURN potential.name, commonFriends
LIMIT 5

-- 基于兴趣的推荐
MATCH (user:Person {name: '张三'})-[:LIKES]->(interest)<-[:LIKES]-(similar)
WHERE user <> similar
WITH similar, count(interest) AS commonInterests
ORDER BY commonInterests DESC
RETURN similar.name, commonInterests
LIMIT 5

性能优化提示

  1. 使用参数化查询

    MATCH (p:Person {name: $name})
    RETURN p
  2. 限制深度遍历

    -- 好的做法:限制深度
    MATCH (p)-[:FRIEND*1..5]-(other)

    -- 避免:无限制深度
    MATCH (p)-[:FRIEND*]-(other)
  3. 使用 PROFILE 分析查询

    PROFILE MATCH (p:Person)-[:FRIEND]->()
    RETURN count(p)
  4. 合理使用索引

    -- 为常用查询属性创建索引
    CREATE INDEX person_name FOR (p:Person) ON (p.name)

下一步

掌握了高级查询后,继续学习 索引与约束,了解如何优化查询性能和保证数据完整性。