Mongoose:嵌套数组结构中的聚合查询

时间:2015-10-01 21:58:56

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

我在使用Mongo数据库查询时遇到了一些问题。

我有一个名为“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中

感谢任何帮助。

由于

2 个答案:

答案 0 :(得分:2)

使用$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
} );

答案 1 :(得分:1)

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

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

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

});

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

如果你想要更广泛的东西,那么只有当每个文档可能在这些条件的数组中包含多个可能的匹配时,你才会使用带有条件的.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) {

    }
);

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