首页 > 解决方案 > 如何在 mongodb 中进行条件嵌套查找搜索

问题描述

我在 mongodb 4.2 中有 2 个集合:

我对 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。有什么办法可以在同一个查询中完成它?先感谢您!

标签: mongodb

解决方案


所以看起来你正在寻找的实际上是以下内容:

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表情的现代形式。这主要有两个原因:

  • 我们实际上想要一个“内部”$lookup表达式来从其他集合中获取结果。

  • 我们希望在结果“作为数组”返回之前对结果进行操作,这是always的输出。这比操作之后返回的“数组”更有效。$lookup

此语法中要注意的一件事是let表达式:

    "let": { "id": "$Id", "bestResponse": "$BestResponseId" },

这里最常见的用例是提供来自文档的值,这些值可用于$expr初始$match指示“加入”条件的逻辑中,即哪个字段值与localforeign匹配。但在这种情况下,我们实际上还有另一个有效用途,尤其是bestResponse声明的值。

请注意,一旦我们为了获取相关的子项而“加入”了“自加入”部分,那么接下来我们想要的是另一个$lookup嵌套在这个pipeline表达式中。在这种情况下,我们希望在自己的表达式初始$match阶段对 使用加法约束,这是问题的一部分。这基本上就是如何在“加入”中包含多个条件。pipelineResultTypeId: 2

因为我们真的对集合中的嵌套细节不感兴趣,result并且真的不需要另一个“子”results数组中的数组,所以为了减少结果,我们在这个子管道中使用管道阶段。$count

现在这不完全是您想要的,因此在它的表达式中的初始$lookup操作中,您然后添加阶段以操作属性中本质上是一个数组(尽管只是具有一个属性的单个文档),使其成为单个属性每个子节点通过运算符具有奇异值。你可以这样做:pipeline$addFieldsresults$sum

"count": { "$arrayElemAt": [ "$results.count", 0 ] }

这将是相同的结果,但它明显比 .更长的表达式"$sum": "$results.count"

您想要的另一件事(尽管对于其余逻辑并不是真正必要的)是确定哪个“孩子”实际上与该BestResponseId值匹配。这实际上是我们使用bestResponse我们之前声明的变量。由于这是父项中的值,因此对管道内的每个子项进行处理,并简单地返回当前字段与项中的值实际匹配的位置。truefalse Id

一旦离开$lookup管道阶段,唯一剩下要做的就是在“加入”之后确定哪些结果文档实际上满足具有比标记为“最佳响应”的文章具有更高结果计数的文章的条件。这是通过另一个$match管道阶段完成的,该阶段$expr再次使用操作员。

简而言之,$max用于从 中获取每个条目中返回的值的最大值作为数组。这与操作符获得的值与数组中匹配父项的字段值进行比较(或者,在哪里是。但这就是我注意到不需要的原因)。拥有匹配的“索引值”,然后您可以通过从该数组中提取属性的奇异值并进行比较。如果它实际上是大于数字,则该文档有资格获得返回结果。countresponses$lookup$indexOfArrayIdresponsesBestResponseIdisBestResponsetruecount$arrayElemAt

当然,如果您想使用另一个$project或什至,您可以简单地返回带有原始字段的文档$addFields,或者如果您再次真的想要一个看起来与 SQL“连接”结果相同的结果,则可以“非规范化” 。但是基本逻辑实际上只需要三个阶段(和 a的 a )来实现的基本部分。$unwind$lookup $lookup


推荐阅读