高级查询
本章介绍 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 条件 | 表达式],它的工作方式是:
- 匹配指定的图模式
- 可选地过滤匹配结果
- 对每个匹配结果计算表达式
- 将所有结果收集为列表返回
基本用法
-- 收集朋友的名字列表
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
性能优势
模式理解相比传统写法有以下性能优势:
- 减少中间结果:不需要额外的 WITH 子句传递变量
- 自动优化:查询优化器可以更好地优化模式理解
- 内存效率:直接生成列表,避免临时关系存储
-- 性能对比示例
-- 传统写法(较慢)
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 { ... } 语法来支持子查询。
子查询的作用
子查询的主要作用包括:
- 计算聚合值后再过滤:先计算每个节点的聚合值,然后基于聚合结果进行筛选
- 模块化复杂查询:将复杂查询拆分为逻辑清晰的部分
- 性能优化:在特定上下文中执行查询,减少中间结果集
- 避免笛卡尔积:使用子查询可以避免多个独立 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
性能优化提示
-
使用参数化查询
MATCH (p:Person {name: $name})
RETURN p -
限制深度遍历
-- 好的做法:限制深度
MATCH (p)-[:FRIEND*1..5]-(other)
-- 避免:无限制深度
MATCH (p)-[:FRIEND*]-(other) -
使用 PROFILE 分析查询
PROFILE MATCH (p:Person)-[:FRIEND]->()
RETURN count(p) -
合理使用索引
-- 为常用查询属性创建索引
CREATE INDEX person_name FOR (p:Person) ON (p.name)
下一步
掌握了高级查询后,继续学习 索引与约束,了解如何优化查询性能和保证数据完整性。