如何在 MongoDB 中获取(或聚合)数组的不同键
How to get (or aggregate) distinct keys of array in MongoDB
我正在尝试让 MongoDB 在不知道键的情况下通过具有不同键值对的数组为我聚合(只需一个简单的求和即可。)
示例文档:
{data: [{a: 3}, {b: 7}]}
{data: [{a: 5}, {c: 12}, {f: 25}]}
{data: [{f: 1}]}
{data: []}
所以基本上每个文档(或者它实际上是数组)可以有 0 个或多个条目,我不知道这些对象的键,但我想对这些键的值求和并取平均值。
现在我只是加载一堆文档并在 Node 中自己完成,但我想将这项工作卸载到 MongoDB。
我知道我可以先解除那些,但如何从那里着手呢?如果我不知道密钥,如何 sum/avg/min/max 值?
如果您不知道密钥或无法做出合理的有根据的猜测,那么您基本上无法进一步使用聚合框架。您可以提供 "all of the keys" 供考虑,但我希望您的实际数据看起来更像这样:
{ "data": [{ "film": 10 }, { "televsion": 5 },{ "boardGames": 1 }] }
所以在这里找到所有 "key names" 然后将其扔到聚合语句中没有什么意义。
郑重声明,"this is why you do not structure your data storage like this"。像这里的 "film" 这样的信息不应该用作 "key" 名称,因为它很有用 "data" 可以在数据库系统中进行搜索,最重要的是 "indexed"。
所以你的数据应该是这样的:
{
"data": [
{ "type": "film", "value": 10 },
{ "type": "televsion", "valule": 5 },
{ "type": "boardGames", "value": 1 }
]
}
那么聚合语句就简单了,还有很多其他的东西:
db.collection.aggregate([
{ "$unwind": "$data" },
{ "$group": {
"_id": null,
"sum": { "$sum": "$data.value" },
"avg": { "$avg": "$data.value" }
}}
])
但是由于key的名字在文档中是不断变化的,没有统一的结构,所以需要在服务器上JavaScript处理遍历key,也就是mapReduce:
db.collection.mapReduce(
function() {
this.data.forEach(function(data) {
Object.keys(data).forEach(function(key) {
emit(null,data[key]); // emit the value regardless of key name
});
});
},
function(key,values) {
return Array.sum(values); // Just summing for example
},
{ "out": { "inline": 1 } }
)
当然,这里的 JavaScript 执行比聚合框架可用的本机编码运算符要慢得多。
所以这应该是一个惨痛的教训,说明为什么在数据库中存储数据时不使用 "data" 作为 "key names"。聚合框架与标准结构一起工作并且速度很快,回退到 JavaScript 处理更灵活,但成本主要是速度和其他功能。
我正在尝试让 MongoDB 在不知道键的情况下通过具有不同键值对的数组为我聚合(只需一个简单的求和即可。)
示例文档:
{data: [{a: 3}, {b: 7}]}
{data: [{a: 5}, {c: 12}, {f: 25}]}
{data: [{f: 1}]}
{data: []}
所以基本上每个文档(或者它实际上是数组)可以有 0 个或多个条目,我不知道这些对象的键,但我想对这些键的值求和并取平均值。
现在我只是加载一堆文档并在 Node 中自己完成,但我想将这项工作卸载到 MongoDB。
我知道我可以先解除那些,但如何从那里着手呢?如果我不知道密钥,如何 sum/avg/min/max 值?
如果您不知道密钥或无法做出合理的有根据的猜测,那么您基本上无法进一步使用聚合框架。您可以提供 "all of the keys" 供考虑,但我希望您的实际数据看起来更像这样:
{ "data": [{ "film": 10 }, { "televsion": 5 },{ "boardGames": 1 }] }
所以在这里找到所有 "key names" 然后将其扔到聚合语句中没有什么意义。
郑重声明,"this is why you do not structure your data storage like this"。像这里的 "film" 这样的信息不应该用作 "key" 名称,因为它很有用 "data" 可以在数据库系统中进行搜索,最重要的是 "indexed"。
所以你的数据应该是这样的:
{
"data": [
{ "type": "film", "value": 10 },
{ "type": "televsion", "valule": 5 },
{ "type": "boardGames", "value": 1 }
]
}
那么聚合语句就简单了,还有很多其他的东西:
db.collection.aggregate([
{ "$unwind": "$data" },
{ "$group": {
"_id": null,
"sum": { "$sum": "$data.value" },
"avg": { "$avg": "$data.value" }
}}
])
但是由于key的名字在文档中是不断变化的,没有统一的结构,所以需要在服务器上JavaScript处理遍历key,也就是mapReduce:
db.collection.mapReduce(
function() {
this.data.forEach(function(data) {
Object.keys(data).forEach(function(key) {
emit(null,data[key]); // emit the value regardless of key name
});
});
},
function(key,values) {
return Array.sum(values); // Just summing for example
},
{ "out": { "inline": 1 } }
)
当然,这里的 JavaScript 执行比聚合框架可用的本机编码运算符要慢得多。
所以这应该是一个惨痛的教训,说明为什么在数据库中存储数据时不使用 "data" 作为 "key names"。聚合框架与标准结构一起工作并且速度很快,回退到 JavaScript 处理更灵活,但成本主要是速度和其他功能。