免責事項:サーバー側(つまりMongoDB内)でこれを行うことはお勧めしませんが、クライアント側でその場合を処理します。
とはいえ、これが問題の一般的な解決策であり、特定のケースに簡単に適応できるはずです。
次のドキュメント(または例のように集約パイプラインからの出力)があるとします。
{
"category" : 1
}
{
"category" : 1
}
// note the missing { category: 2 } document here
{
"category" : 3
}
次のパイプラインは空のバケットを作成します(したがって、category
の値の範囲から欠落している「ギャップ」値のカウントが0のドキュメント フィールド-この場合は番号2):
var bucketSize = 1;
db.getCollection('test').aggregate({
$group: {
_id: null, // throw all documents into the same bucket
"min": { $min: "$category" }, // just to calculate the lowest
"max": { $max: "$category" }, // and the highest "category" value
"docs": { $push: "$$ROOT" } // and also keep the root documents
}
}, {
$addFields: {
"docs": { // modify the existing docs array - created in the previous stage
$concatArrays: [ // by concatenating
"$docs", // the existing docs array
{
$map: { // with some other array that will be generated
input: {
$range: [ "$min", "$max", bucketSize ] // based on the min and max values and the bucket size
},
as: "this",
in: { // but represented not as a plain number but as a document that effectively creates a bogus document
"category": "$$this", // the bogus category will be set to the respective value
"bogus": 1 // marker that allows us not to count this document in the next stage and still get a bucket from $group
}
}
}
]
}
}
}, {
$unwind: "$docs" // flatten the "docs" array which will now contain the bogus documents, too
}, {
$group: {
_id: "$docs.category", // group by category
"count": { // this is the result we are interested in
$sum: { // which will be aggregated by calculating the sum for each document of
$cond: [ // either 0 or 1 per document
{ $eq: [ "$docs.bogus", 1 ] }, // depending on whether the document should count as a result or not
0,
1
]
}
}
}
})
上記のクエリの出力は次のようになります:
{
"_id" : 2,
"count" : 0.0 // this is what we wanted to achieve
}
{
"_id" : 3,
"count" : 1.0 // correct number of matches
}
{
"_id" : 1,
"count" : 2.0 // correct number of matches
}