如何在MongoDB中找到具有不同过滤器的最小/最大聚合?



考虑使用此结构收集文档clothing

{
"brand": <string>,
"color": <string>,
"size": <number>
}

在单个brand聚合中,我想找到两个值:

  1. 组中所有文档的最小大小,其中color="blue"
  2. 组中所有文档的最大大小,其中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;它应该为maxmin显示相同的值。 以下是一些替代方案。 第一个是直接回答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]}}
}
}
]);

第二种解决方案更令人兴奋,因为它将为遇到的所有颜色产生maxmin

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'}} }}
]);

是的,您最终会在maxmin对象(特别是brandcolor)中获得重复/冗余数据,但所有其他对等字段将被转移而无需更改查询,例如:

{
"_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模式删除colorbrand

{$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" }
}
}
])

蒙戈游乐场

  1. $group- 按brand分组,按$cond$pushsizeredblue数组
  2. $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"}}}}])

相关内容

  • 没有找到相关文章

最新更新