我正在尝试按频率对 MongoDB 集合的每个文档中的几个数组进行排序。
我的文档现在看起来像这样
{
"_id": whatever,
"color": "blue",
"fruit": ["apple", "banana", "apple", "orange", "apple", "orange", ...],
"vegetable": ["onion", "lettuce", "spinach", "lettuce", ...],
"meat": ["pulled pork", "steak", "chicken wings", "pulled pork", "pulled pork", ...]
}
注意:这不是真实数据,但文档的属性完全相同。
最终目标是为每种颜色找到最常见的水果、蔬菜和肉类,所以我想我是否可以按颜色分组并获取按频率排序的每个数组的第一个元素,这将给我我需要的东西。
我尝试过展开,但我的数据库太大,无法展开每个数组(每个数组都有大约 50.000 个元素,因此 50.000^3 似乎并不理想)。我还寻找了“模式”组函数,因为 MongoDB 有一个“中值”函数,但似乎没有(v5.0.22)。我还研究了用于管道化的映射和归约函数(db.collection.aggregate({$map //or $reduce...})),但老实说我并没有走得太远,因为这是一种对我来说是新的,尽管在我看来这可能是可行的方法。
有人做过类似的事情在这里可能有效吗?谢谢!!
OP 指出,我们在问题中看到的文档是
$group
(表面上在 color
)的结果,可能 $push
是 fruit
、vegetable
和 meat
值到永远-不断增长的数组(多达 50,000 个)。如果目标是获取每种颜色的模式,那么 $facet
可以用作“多组”。假设每个单独的文档都具有这种形状(注意:此处仅使用 fruit
和 meat
进行简化;该方法扩展到 vegetable
以及文档中的任何其他字段):
{"color": "blue", "fruit": "A", "meat": "X"}
以下
$facet
管道将产生我们寻求的模式:
db.foo.aggregate([
{$facet: {
"most_fruit": [
// Sum by color and fruit name:
{$group: {_id: {c:'$color', v:'$fruit'}, N: {$sum: 1}}}
// Reorg by color only....
,{$group: {_id: '$_id.c', X:{$push: {v:'$_id.v',N:'$N'}}}}
// ...and now sort highest-to-lowest and take the highest one.
// Nice thing is if you really want, you are already set up to
// capture, for example, the highest *nd* the lowest.
,{$project: {
X: {$first: {$sortArray: {input: '$X', sortBy: {'N':-1} }} }
}}
],
// Same thing ... but for meat
"most_meat": [
{$group: {_id: {c:'$color', v:'$meat'}, N: {$sum: 1}}}
,{$group: {_id: '$_id.c', X:{$push: {v:'$_id.v',N:'$N'}}}}
,{$project: {
X: {$first: {$sortArray: {input: '$X', sortBy: {'N':-1} }} }
}}
]
}}
]);
产生具有这种形状的东西:
{
most_fruit: [
{_id: 'blue', X: {v: 'A', N: 2} },
{_id: 'green',X: {v: 'F', N: 3} }
],
most_meat: [
{_id: 'green',X: {v: 'Z', N: 4} },
{_id: 'blue', X: {x: 'X', N: 3} }
]
}
返回包含所有信息的单个文档。尽管它不是按颜色组织的,但在数据库端(使用 MQL)无法进行进一步的处理来提高数据分组或过滤的效率;现在由客户端来设置信息。
这是候选客户端重组:
var oneDoc = c.next();
function processItem(obj, fname) {
for(var n = 0; n < obj[fname].length; n++) {
var cn = oneDoc[fname][n]['_id'];
if(undefined == color_major[cn]) {
// Set up ALL the possible modes. -1 is our way of
// signalling it has not yet been set.
color_major[cn] = {'most_fruit':-1,'most_meat':-1};
}
color_major[cn][fname] = oneDoc[fname][n]['X'];
}
}
var color_major = {};
processItem(oneDoc, 'most_fruit');
processItem(oneDoc, 'most_meat');
print(color_major);
产量:
{
green: { most_fruit: { v: 'F', N: 3 }, most_meat: { v: 'Z', N: 4 } },
blue: { most_fruit: { v: 'A', N: 2 }, most_meat: { v: 'X', N: 3 } }
}