考虑使用此结构收集文档clothing
{
"brand": <string>,
"color": <string>,
"size": <number>
}
在单个brand
聚合中,我想找到两个值:
- 组中所有文档的最小大小,其中
color="blue"
- 组中所有文档的最大大小,其中
color="red"
在SQL中,它将是这样的:
SELECT MIN(CASE color WHEN "blue" THEN size ELSE NULL END),
MAX(CASE color WHEN "red" THEN size ELSE NULL END),
brand
FROM clothing
GROUP BY brand
如何在MongoDB查询中表达这一点?
当仅存在单个数据观察值时,上面的答案会产生 min 的null
;它应该为max
和min
显示相同的值。 以下是一些替代方案。 第一个是直接回答OP问题的简单问题 - 但它明显是颜色的"硬编码":
db.foo.aggregate([
{$group: {_id: '$brand',
'red_max': {$max: {$cond: [{$eq:['$color','red']}, '$size', 0]}},
'red_min': {$min: {$cond: [{$eq:['$color','red']}, '$size', 99999999]}},
'blue_max': {$max: {$cond: [{$eq:['$color','blue']}, '$size', 0]}},
'blue_min': {$min: {$cond: [{$eq:['$color','blue']}, '$size', 99999999]}}
}
}
]);
第二种解决方案更令人兴奋,因为它将为遇到的所有颜色产生max
和min
。
db.foo.aggregate([
{$group: {_id: {c:'$color',b:'$brand'}, max:{$max: "$size"}, min:{$min: "$size"}}}
// Arguably, you are done by the stage above -- but to make the output more
// convenient, employ a "regroup" stage to move the material around:
,{$group: {_id: '$_id.b', data: {$push: {color: '$_id.c', max: '$max', min: '$min'}}}}
,{$unwind: "$data"} // optional, really...
]);
您可以通过在$group
之前执行$match
来轻松捕获红色和蓝色,或者要将目标红色和蓝色以外的其他所有内容都设置为"OTHER",请将第一个$group
阶段更改为:
{$group: {_id: {c:{$cond:[{$in:['$color',['red','blue']]},'$color',"OTHER"]}, b:'$brand'},
max:{$max: "$size"}, min:{$min: "$size"}}}
最后,有时除了查找最大值和最小值之外,我们还希望了解与最大值和最小值相同的文档中的其他字段。 这可以通过稍微复杂的查询来实现:
db.foo.aggregate([
{$group: {_id: {c:'$color',b:'$brand'}, XX:{$push: "$$CURRENT"}}}
,{$addFields: {XX: {$reduce: {
input: "$XX",
initialValue: {max:{size:0}, min:{size:99999999}},
in: {
max: {$cond: [{$gt:['$$this.size','$$value.max.size']},'$$this','$$value.max']},
min: {$cond: [{$lt:['$$this.size','$$value.min.size']},'$$this','$$value.min']}
}
}}
}}
,{$group: {_id: '$_id.b', data: {$push: {color: '$_id.c', max: '$XX.max', min: '$XX.min'}} }}
]);
是的,您最终会在max
和min
对象(特别是brand
和color
)中获得重复/冗余数据,但所有其他对等字段将被转移而无需更改查询,例如:
{
"_id" : "B2",
"data" : [
{
"color" : "red",
"max" : {
"_id" : 13,
"brand" : "B2",
"color" : "red",
"size" : 8,
"o1" : "A14",
"o2" : [
"P",
"Q"
]
},
"min" : {
"_id" : 8,
"brand" : "B2",
"color" : "red",
"size" : 6,
"o1" : "A9",
"o2" : [
"K",
"L"
]
}
},
{
"color" : "blue",
"max" : {
"_id" : 9,
"brand" : "B2",
"color" : "blue",
"size" : 7,
"o1" : "A10",
"o2" : [
"L",
"M"
]
},
"min" : {
"_id" : 9,
"brand" : "B2",
"color" : "blue",
"size" : 7,
"o1" : "A10",
"o2" : [
"L",
"M"
]
}
},
...
如果冗余数据不可接受,则可以使用objectToArray-filter-arrayToObject
模式删除color
并brand
:
{$group: {_id: {c:'$color',b:'$brand'}, XX:{$push:
// "$$CURRENT"
{$arrayToObject: {$filter: {input: {$objectToArray: "$$CURRENT"},
cond: {$not:{$in:['$$this.k', ['brand','color']]}}
}}}
}}}
db.collection.aggregate([
{
"$match": {
"color": { "$in": [ "red", "blue" ] }
}
},
{
"$group": {
"_id": "$color",
"max": { "$max": "$size" },
"min": { "$min": "$size" }
}
}
])
蒙戈游乐场
$group
- 按brand
分组,按$cond
$push
size
red
或blue
数组
。$project
- 用$min
(蓝色)和$max
(红色)装饰输出文档。
db.collection.aggregate([
{
"$group": {
"_id": "$brand",
"red": {
$push: {
$cond: [
{
$eq: [
"$color",
"red"
]
},
"$$ROOT.size",
"$$REMOVE"
]
}
},
"blue": {
$push: {
$cond: [
{
$eq: [
"$color",
"blue"
]
},
"$$ROOT.size",
"$$REMOVE"
]
}
}
}
},
{
$project: {
brand: "$brand",
min: {
$min: "$blue"
},
max: {
$max: "$red"
}
}
}
])
样本蒙戈游乐场
查询
$min
,$max
也适用于文档,按顺序比较字段
">- 蓝色"比"红色"小,所以我们不需要任何额外的步骤 (如果不是这种情况,您可以说如果 blue => 1 否则 0 来更改顺序,例如为可能的字符串分配一个数字,并将该数字代替
$color
)
在此处测试代码
aggregate(
[{"$group":
{"_id":"$brand",
"min-blue":{"$min":{"color":"$color", "size":"$size"}},
"max-red":{"$max":{"color":"$color", "size":"$size"}}}}])