我有两个收藏page
和likes
。在这两个集合中都有字段 pageId
。 likes
也有字段 userName
。
如果likes
集合包含一对pageId
/userName
,则表示该页面被特定用户喜欢。
如何运行查询来返回特定用户不喜欢的任意一页?
在 SQL 中,这看起来像这样:
SELECT *
FROM page
WHERE NOT EXISTS (SELECT 'a' FROM likes WHERE (likes.pageId = page.pageId) AND (likes.userName = 'userName'))
LIMIT 1
我考虑过使用 $exists
运算符,但是 here我读到它的含义与同名的 SQL 操作不同,我必须使用 $in
运算符。
我可以
- 找出特定用户喜欢的所有页面并将其保存在列表中
- 然后在
page
上运行查询,该查询应返回第一个页面,该页面未包含在列表中。
我正在犹豫是否要以这种方式实现它,因为它对我来说似乎效率很低(我在步骤 2 中只需要一页,但要获得该页,我需要遍历用户的所有点赞)。
请您参考如下方法:
在 3.6 版本中,我们提供了新的 $lookup 管道语法,允许您对相关文档执行管道。您可以使用它来查找符合您特定条件的相关文档。将其与更多步骤相结合,您可以构造一个 NOT EXISTS 查询。
- 查找相关对象
- 使用
$expr { $and: [] }
将连接与附加约束结合起来 - 使用
$limit: 1
进行优化,因为我们只关心是否存在 $match
其中连接属性$exists: false
- 为了更好地衡量,
$project
消除了空集合
例如,要查询特定用户没有点赞的页面,请按如下方式构建管道:
db.page.insertOne({ pageId: 1 })
db.page.insertOne({ pageId: 2 })
db.page.insertOne({ pageId: 3 })
db.likes.insertOne({ pageId: 1, userName: "Pinky" })
db.likes.insertOne({ pageId: 2, userName: "Pinky" })
db.likes.insertOne({ pageId: 2, userName: "Brain" })
db.likes.insertOne({ pageId: 3, userName: "Brain" })
db.page.aggregate([{
"$lookup": {
"from": "likes",
"let": {
"pageId": "$pageId"
},
"pipeline": [{
"$match": {
"$expr": {
"$and": [
{ "$eq": [ "$pageId", "$$pageId" ] },
{ "$eq": [ "$userName", "Pinky" ] }
]
}
}
}, {
"$limit": 1
}],
"as": "likes"
}
}, {
"$match": {
"likes.pageId": { "$exists": false }
}
}, {
"$project": {
"likes": false
}
}])