MongoDB如何基于用户多列表订阅匹配帖子标签查询
实现基于用户订阅列表的MongoDB帖子查询
场景回顾
帖子集合(posts)文档结构:
{ "id": 1, "title": "Tv for sale", "city":"doha", "tags": ["doha", "tv", "sale", "used" ] }, { "id": 2, "title": "flat for rent", "city":"dubai", "tags": ["dubai", "flat", "rent" ] }
用户集合(users)文档结构:
{ "id": 1, "name": "user1", "subscriptions": [["doha", "tv", "used"],["dubai", "flat", "sale"]] }
需求:返回满足「用户任意一个订阅标签组完全包含在帖子tags数组中」的帖子,示例中仅返回id为1的帖子。
解决方案
方法一:先获取订阅列表,再构造查询条件
这是最直接高效的方式,先从用户集合拿到目标用户的订阅标签组,再用$or+$all组合查询帖子。
- 获取用户订阅列表:
const targetUser = db.users.findOne({ id: 1 }); const userSubscriptions = targetUser.subscriptions;
- 查询符合条件的帖子:
db.posts.find({ $or: userSubscriptions.map(subGroup => ({ tags: { $all: subGroup } })) })
原理:
$all用于匹配tags数组包含指定子组所有元素的帖子;map把每个订阅标签组转化为一个查询条件,再用$or包裹,表示满足任意一个条件即可。- 示例中,第一个订阅组
["doha", "tv", "used"]的所有元素都在帖子1的tags里,因此匹配;第二个订阅组["dubai", "flat", "sale"]里的sale不在帖子2的tags中,所以不匹配,最终只返回帖子1。
方法二:用聚合管道关联查询(适合复杂场景)
如果需要在一次查询中完成用户订阅获取+帖子匹配,可以用聚合管道的$lookup关联用户集合,再通过$setIsSubset和$anyElementTrue判断子集关系。
db.posts.aggregate([ // 关联用户集合,获取目标用户的订阅列表 { $lookup: { from: "users", let: { targetUserId: 1 }, pipeline: [ { $match: { $expr: { $eq: ["$id", "$$targetUserId"] } } }, { $project: { subscriptions: 1, _id: 0 } } ], as: "userSubInfo" } }, // 展开订阅信息数组(因为$lookup返回的是数组) { $unwind: "$userSubInfo" }, // 筛选出存在订阅组是tags子集的帖子 { $match: { $expr: { $anyElementTrue: { $map: { input: "$userSubInfo.subscriptions", as: "subGroup", in: { $setIsSubset: ["$$subGroup", "$tags"] } } } } } }, // 移除不需要的关联字段 { $project: { userSubInfo: 0 } } ])
原理:
$lookup关联用户集合,只获取目标用户的订阅数据;$setIsSubset判断某个订阅组是否是帖子tags的子集;$map遍历所有订阅组,返回每个组的判断结果(布尔值);$anyElementTrue检查是否存在至少一个true的结果,即满足任意一个订阅组的匹配条件。
内容的提问来源于stack exchange,提问作者Emad Jumaah
相关产品推荐
相关产品推荐

