MongoDB 3.6 以降では$objectToArray
、集計パイプライン内で演算子を使用して、ドキュメントを配列に変換します。返される配列には、元のドキュメントの各フィールド/値のペアの要素が含まれています。k
返される配列の各要素は、 と の 2 つのフィールドを含むドキュメントです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" }
]
}
次に、演算子を使用してドキュメントごとの answer_records 配列の長さを返す集計の$project
パイプラインを適用するだけです。$size
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 } }
)