mongodb - 如何在 mongodb 中进行条件嵌套查找搜索
问题描述
我在 mongodb 4.2 中有 2 个集合:
文章 - [Id,ArticletypeId,BestResponseId,Topic,PredecessorId]
{ Id: 1, ArticleTypeId:1, BestResponseId:2, Topic:"XYZ" }, { Id: 2, ArticleTypeId:2, PredecessorId:1 }, { Id: 3, ArticleTypeId:2, PredecessorId:1 }, { Id: 4, ArticleTypeId:2, BestResponseId:5, Topic:"ABC" }, { Id: 5, ArticleTypeId:1, PredecessorId:4 }, { Id: 6, ArticleTypeId:2, PredecessorId:4 }
结果-[Id,ArticleId,ResultTypeId]
{ Id: 1, ArticleId:1, ResultTypeId:2 }, { Id: 2, ArticleId:2, ResultTypeId:2 }, { Id: 3, ArticleId:2, ResultTypeId:2 }, { Id: 4, ArticleId:3, ResultTypeId:2 }, { Id: 5, ArticleId:2, ResultTypeId:2 }, { Id: 6, ArticleId:4, ResultTypeId:2 }, { Id: 7, ArticleId:5, ResultTypeId:2 }, { Id: 8, ArticleId:6, ResultTypeId:2 }, { Id: 9, ArticleId:6, ResultTypeId:2 }
在文章集合中,BestResponseId 是对给定文章的最佳响应的ArticleId,即对于Topic“XYZ”的ArticleId = 1,最佳响应是ArticleId=2,依此类推。
PredecessorId 指示响应是针对哪篇文章的。
在结果集合中,ArticleId 是外键引用 article-Id
我们需要找到 AnyResponse 中 Count(ResultTypeId=2) 大于 BestResponse 的主题列表,因此在下面的示例中:
对于 1-5 的结果,ArticleId(2) 的 Count(ResultTypeId = 2 ) 为 2,但针对同一文章的其他响应 Count(ResultTypeId=2) 为 1,因此最佳响应得到最佳结果,我们不会在输出中考虑。
但这里对于 6-9 的其他结果:ArticleId(5) 的 Count(ResultTypeId = 2 ) 为 1,而 ArticleId(5) 的 Count(ResultTypeId = 2 ) 为 2,因此预期输出将是
话题
“美国广播公司”
所以,基本上,你在文章和文章之间进行连接[在 Id 和 PredecessorId,self join] 上,获取 PredecessorIds 列表以及其中一个是 BestResponseId,所以第一级查找应该给出如下输出:
PredecessorId|ArticleId|IsBestResponse 1 |2 | true 1 |3 | false 4 |5 | true 4 |6 | false
现在,一旦你加入这个结果(ArticleId),并计算 ResultTypeId=2 groupBy ArticleId。所以在第二级查找之后,输出将是:
ArticleId|PredecessorId|IsBestResponse|ResultType2_Count 2 | 1 | true | 3 3 | 1 | false | 1 5 | 4 | true | 1 6 | 4 | false | 2
现在,我们需要输出 Articles 的前辈的主题名称,IsBestResponse=false 但 ResultType2_Count 大于 IsBestResponse=true 属于同一前辈的文章的 ResultType2_Count。
- 所以在 ArticleId 5 和 6 之间,这个条件是满足的。并且他们的前任 ["ABC"] 的对应 Topic 是预期的输出。
- 如果 2 & 满足相同的条件,我们也会打印“XYZ”。但是,事实并非如此。
我对 mongodb 和查找有点陌生,这是我到目前为止所做的:
db.article.aggregate([
{
$lookup:{
from:"article",
localField:"ArticleId",
foreignField:"PredecessorId",
as:"articles"
}
},
{$unwind:"$articles"},
{$lookup:{
from:"result",
localField:"answers.Id",
foreignField:"ArticleId",
as:"articles"
}},
{$unwind:"$articles"}
])
我确定我需要在嵌套查找的第二级中执行 $sum 或 $count。有什么办法可以在同一个查询中完成它?先感谢您!
解决方案
所以看起来你正在寻找的实际上是以下内容:
db.article.aggregate([
{ "$match": { "Topic": { "$exists": true } } },
{ "$lookup": {
"from": "article",
"let": { "id": "$Id", "bestResponse": "$BestResponseId" },
"pipeline": [
{ "$match": {
"$expr": { "$eq": [ "$$id", "$PredecessorId" ] }
}},
{ "$lookup": {
"from": "result",
"let": { "articleId": "$Id" },
"pipeline": [
{ "$match": {
"ResultTypeId": 2,
"$expr": { "$eq": [ "$$articleId", "$ArticleId" ] }
}},
{ "$count": "count" }
],
"as": "results"
}},
{ "$addFields": {
"results": "$$REMOVE",
"count": { "$sum": "$results.count" },
"isBestResponse": { "$eq": ["$$bestResponse", "$Id"] }
}}
],
"as": "responses"
}},
{ "$match": {
"$expr": {
"$gt": [
{ "$max": "$responses.count" },
{ "$arrayElemAt": [
"$responses.count",
{ "$indexOfArray": [ "$responses.Id", "$BestResponseId" ] }
]}
]
}
}}
])
这将提供(与您解释为的关系输出相比,输出更多类似于 MongoDB):
{
"_id" : ObjectId("5da1206f22b8db5a00668cc4"),
"Id" : 4,
"ArticleTypeId" : 2,
"BestResponseId" : 5,
"Topic" : "ABC",
"responses" : [
{
"_id" : ObjectId("5da1206f22b8db5a00668cc5"),
"Id" : 5,
"ArticleTypeId" : 1,
"PredecessorId" : 4,
"count" : 1,
"isBestResponse" : true
},
{
"_id" : ObjectId("5da1206f22b8db5a00668cc6"),
"Id" : 6,
"ArticleTypeId" : 2,
"PredecessorId" : 4,
"count" : 2,
"isBestResponse" : false
}
]
}
现在我将介绍它并解释为什么会这样。
首先,您希望$match
在管道开始时有一个阶段仅排除那些有效Topic
结果以外的任何内容。这使用了一个简单$exists
的方法,以便仅使用该字段检索那些结果,然后满足第一个“加入”的条件。
实际$lookup
是要使用带有pipeline
表情的现代形式。这主要有两个原因:
此语法中要注意的一件事是let
表达式:
"let": { "id": "$Id", "bestResponse": "$BestResponseId" },
这里最常见的用例是提供来自父文档的值,这些值可用于$expr
初始$match
指示“加入”条件的逻辑中,即哪个字段值与local和foreign匹配。但在这种情况下,我们实际上还有另一个有效用途,尤其是bestResponse
声明的值。
请注意,一旦我们为了获取相关的子项而“加入”了“自加入”部分,那么接下来我们想要的是另一个$lookup
嵌套在这个pipeline
表达式中。在这种情况下,我们希望在它自己的表达式的初始$match
阶段对 使用加法约束,这是问题的一部分。这基本上就是如何在“加入”中包含多个条件。pipeline
ResultTypeId: 2
因为我们真的对集合中的嵌套细节不感兴趣,result
并且真的不需要另一个“子”results
数组中的数组,所以为了减少结果,我们在这个子管道中使用管道阶段。$count
现在这不完全是您想要的,因此在它的表达式中的初始$lookup
操作中,您然后添加阶段以操作属性中本质上是一个数组(尽管只是具有一个属性的单个文档),使其成为单个属性每个子节点通过运算符具有奇异值。你可以这样做:pipeline
$addFields
results
$sum
"count": { "$arrayElemAt": [ "$results.count", 0 ] }
这将是相同的结果,但它明显比 .更长的表达式"$sum": "$results.count"
。
您想要的另一件事(尽管对于其余逻辑并不是真正必要的)是确定哪个“孩子”实际上与该BestResponseId
值匹配。这实际上是我们使用bestResponse
我们之前声明的变量。由于这是父项中的值,因此对管道内的每个子项进行处理,并简单地返回当前子字段与父项中的值实际匹配的位置。true
false
Id
一旦离开$lookup
管道阶段,唯一剩下要做的就是在“加入”之后确定哪些结果文档实际上满足具有比标记为“最佳响应”的文章具有更高结果计数的文章的条件。这是通过另一个$match
管道阶段完成的,该阶段$expr
再次使用操作员。
简而言之,$max
用于从 中获取每个子条目中返回的值的最大值作为数组。这与操作符获得的值与数组中匹配父项的字段值进行比较(或者,在哪里是。但这就是我注意到不需要的原因)。拥有匹配的“索引值”,然后您可以通过从该数组中提取属性的奇异值并进行比较。如果它实际上是大于数字,则该文档有资格获得返回结果。count
responses
$lookup
$indexOfArray
Id
responses
BestResponseId
isBestResponse
true
count
$arrayElemAt
当然,如果您想使用另一个$project
或什至,您可以简单地返回带有原始字段的文档$addFields
,或者如果您再次真的想要一个看起来与 SQL“连接”结果相同的结果,则可以“非规范化” 。但是基本逻辑实际上只需要三个阶段(和 a中的 a )来实现的基本部分。$unwind
$lookup
$lookup
推荐阅读
- javascript - 无服务器联系表单问题 html css ajax js
- python - Python socket接收bufsize参数值
- c++ - 抛出未处理的异常:读取访问冲突。x 是 0x20B4112
- r - 是否可以使用转换自引用列
- django - django_neomodel 在 neomodel 工作时无法连接到正在运行的数据库
- php - PHP - json 输出中的 Unicode 问题转换
- javascript - 选择一个随机的 HTML 元素
- php - 订单表中的 WooCommerce order_items
- python - 根据列中存在的项目过滤数据框
- python - 在 python 中安装 ccxt 模块时经常出现错误