对于 MongoDB 3.6 和更高版本,使用$objectToArray
聚合管道中的运算符将文档转换为数组。返回数组包含原始文档中每个字段/值对的元素。返回数组中的每个元素都是一个包含两个字段k
和的文档v
。
获取数组后,您可以利用$addFields
管道步骤创建一个保存计数的字段,并使用运算符得出实际计数$size
。
所有这些都可以通过嵌套表达式在单个管道中完成,如下所示:
db.collection.aggregate([
{
"$addFields": {
"answers_count": {
"$size": {
"$objectToArray": "$answer_records"
}
}
}
}
])
样本输出
{
"_id" : ObjectId("57eb386e37b4842ff5f386c9"),
"lesson_id" : ObjectId("57e27cd190e6993e393f5c74"),
"student_id" : ObjectId("57d3c3f590e6995fe8de7932"),
"answer_records" : {
"1" : {
"answer" : [
"A"
]
},
"3" : {
"answer" : [
"C"
]
}
},
"answers_count": 2
}
对于不支持上述运算符的 MongoDB 服务器版本,您需要更改架构设计才能使用聚合框架执行高效查询。由于目前您需要使用 JavaScript 在客户端或服务器上预处理文档,因此您将无法充分利用 MongoDB 为更快查询而构建的更好的基础架构。
理想的设计如下:
{
"_id" : ObjectId("57eb386e37b4842ff5f386c9"),
"lesson_id" : ObjectId("57e27cd190e6993e393f5c74"),
"student_id" : ObjectId("57d3c3f590e6995fe8de7932"),
"answer_records" : [
{ "id": "1", "answer": "A" }
{ "id": "3", "answer": "C" }
]
}
然后您可以简单地应用聚合的$project
管道,该管道使用$size
运算符返回每个文档的 answer_records 数组的长度:
db.collection.aggregate([
{
"$project": {
"lesson_id": 1,
"student_id": 1,
"count": { "$size": "$answer_records" }
}
}
])
如果您想要整个集合的答案记录总数,则添加另一个$group
管道以使用 null 的 _id 获取所有文档的累积总数:
db.collection.aggregate([
{
"$project": {
"count": { "$size": "$answer_records" }
}
},
{
"$group": {
"_id": null,
"total_answers": { "$sum": "$count" }
}
}
])
否则,对于当前设计,您唯一的选择是 MapReduce,它要慢得多:
db.collection.mapReduce(
function() {
emit(this._id, Object.keys(this.answer_records).length);
},
function() { },
{ "out": { "inline": 1 } }
)
样本输出:
{
"results" : [
{
"_id" : ObjectId("57eb386e37b4842ff5f386c9"),
"value" : 2
}
],
....
}
要获取集合中所有文档的总数,请运行以下 mapReduce 操作:
db.collection.mapReduce(
function() {
emit(null, Object.keys(this.answer_records).length);
},
function(key, values) {
return Array.sum(values);
},
{ "out": { "inline": 1 } }
)