【问题标题】:Mongoose: Aggregate query in a nested array structureMongoose:嵌套数组结构中的聚合查询
【发布时间】:2015-10-01 21:58:55
【问题描述】:

我遇到了一个 Mongo DB 查询的小问题。

我有一个名为“Answers”的集合,其结构如下

{
  "_id": ObjectId("560acfcb904a29446a6d2617"),
  "path_id": ObjectId("560acae1904a29446a6d2610"),
  "step_id": "kids",
  "user_id": ObjectId("559ae27684ff12e88d194eb7"),
  "answers": [
    {
      "id": "kids_q",
      "question": "Do you have kids?",
      "answer": "No"
    }
  ]
}

如您所见,answers 是一个数组,它可以有一个或多个对象,但始终是一个数组。

首先,我想得到一个 step_id 中的答案总数

我通过以下使用聚合的查询得到它

Answer.aggregate( {
        $match: {
            'path_id': {
                $eq: path_id
            },
            'step_id': {
                $eq: step_id
            },
            ''

        }
    }, {
        $group: {
            _id: {
                step_id: '$step_id'
            },
            count: {
                $sum: 1
            }
        }
    }, function( err, results ) {
        if ( err ) {
            deferred.reject( err );
        }
        deferred.resolve( results );
    } );

效果很好。

其次,我想知道有多少答案与问题和答案相匹配。

我们以Do you have kids?问题为例,我想知道Yes有多少个答案,在命令行中运行查询我得到正确的结果:

db.answers.find( {
    path_id: ObjectId( '560acae1904a29446a6d2610' ),
    'answers.0.question': 'Do you have kids?',
    'answers.0.answer': 'Yes'
} )

我想使用 mongoose 将该查询转换为聚合查询,并避免对数组 answers.0.question 进行硬编码,因为该答案可以存储在随机索引中,可能存储在索引 1 中,也可能存储在索引 7 中。

感谢任何帮助。

谢谢

【问题讨论】:

    标签: node.js mongodb mongoose mongodb-query aggregation-framework


    【解决方案1】:

    使用$unwind,然后使用 $match 仅过滤您正在寻找的问题的答案:

    var steps = [
      {
        $match: {
          'path_id': ObjectId("560acae1904a29446a6d2610"),
          'step_id': 'kids'
        }
      },
      { $unwind : "$answers" },
      {
        $match: {
          "answers.question": 'Do you have kids?'
        }
      },
      {
        $group: {
            _id: '$answers.answer',
            count: {
                $sum: 1
            }
          }
        }
      ];
    Answer.aggregate(steps, function( err, results ) {
            //do whatever you want with the results
    } );
    

    【讨论】:

    • 非常感谢,$unwind 方法效果很好。但鉴于@blakes-seven 的回答,看起来聚合解决方案有点矫枉过正,而使用$elemMatch 的计数解决方案就像一个魅力。
    【解决方案2】:

    真的不确定.aggregate() 是否真的是您想要的。如果我理解正确,您的集合中有这些文档,其中包含一系列问题的答案,当然这些答案不在数组中的任何设置位置。但似乎也不太可能任何一份文档都有多个相同的答案类型。

    所以在我看来,您真正想要的只是数组元素值上的 $elemMatch 并确定包含它的文档数:

    Answer.count({
        "path_id": "560acae1904a29446a6d2610",
        "answers": { 
            "$elemMatch": { 
                "question": "Do you have kids?",
                "answer": "Yes"
            }
        }
    },function(err,count) {
    
    });
    

    $elemMatch 运算符将其所有条件应用于数组的每个元素,就像另一个查询一样。因此,需要在同一个元素上满足“and”的多个条件才能使其有效。无需按索引执行此操作。

    如果您想要更广泛的内容,并且仅当每个文档可能在这些条件的数组中包含多个可能的匹配项时,您将使用 .aggregate() 和条件来过滤和计算匹配项数组内。

    Answer.aggregate(
        [
           { "$match": {
               "answers": { 
                   "$elemMatch": { 
                       "question": "Do you have kids?",
                       "answer": "Yes"
                   }
               }
           }},
           { "$unwind": "$answers" },
           { "$match": {
               "answers.question": "Do you have kids?",
               "answers.answer": "Yes"
           }},
           { "$group": {
               "_id": "$path_id",
               "count": { "$sum": 1 }
           }}
        ],
        function(err,results) {
    
        }
    );
    

    但如果您确实在数组中有多个可能的匹配项并且您需要多个键在结果中进行分组,我只会这样做。

    因此,如果只是匹配恰好在一个数组条目中包含这些详细信息的文档,那么只需使用$elemMatch 进行查询,最多只使用$group 对给定键的计数,不要打扰通过$unwind过滤数组内容。

    Answer.aggregate(
        [
           { "$match": {
               "answers": { 
                   "$elemMatch": { 
                       "question": "Do you have kids?",
                       "answer": "Yes"
                   }
               }
           }},
           { "$group": {
               "_id": "$path_id",
               "count": { "$sum": 1 }
           }}
        ],
        function(err,results) {
    
        }
    );
    

    因此,如果数组中真的只有一个可能的匹配项,那么只需计算文档数

    【讨论】:

    • 你是完全正确的,聚合解决方案是矫枉过正,再次重新考虑该过程 Collection.count 是一种更好的方法。感谢战利品。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-10
    • 1970-01-01
    • 2020-03-29
    • 2021-08-13
    • 1970-01-01
    • 2019-08-01
    • 1970-01-01
    相关资源
    最近更新 更多