假设我有文件,其中包括以下字段:
{
"class" : String,
"type" : String,
"name" : String,
}
例如,许多人喜欢这样:
{
"class": "class A",
"type": "type 1",
"Name": "ObjectA1"
}
{
"class": "class A",
"type": "type 2",
"Name": "ObjectA2_1"
}
{
"class": "class A",
"type": "type 2",
"Name": "ObjectA2_2"
}
{
"class": "class B ",
"type": "type 3",
"Name": "ObjectB3"
}
我想要的是一个返回以下结构的查询
{
"class A" : {
"type 1" : ["ObjectA1"],
"type 2" : ["ObjectA2_1", "ObjectA2_2"]
},
"class B" : {
"type 3" : ["ObjectB3"]
}
}
我尝试将聚合与$ group一起使用,但无法执行此操作。有什么想法吗?
PS:我想在mongodb shell上做这个,而不是像猫鼬一样。
答案 0 :(得分:1)
使用聚合框架的问题是您无法为对象的属性指定任意键名。因此,如果不能指定所有可能的密钥名称,那么使用它就不可能进行整形。
因此,要获得结果,您需要在JavaScript中使用mapReduce
:
首先定义一个mapper:
var mapper = function () {
var key = this["class"];
delete this._id;
delete this["class"];
emit( key, this );
};
然后是减速器:
var reducer = function (key, values) {
var reducedObj = {};
values.forEach(function(value) {
if ( !reducedObj.hasOwnProperty(value.type) )
reducedObj[value.type] = [];
reducedObj[value.type].push( value.Name );
});
return reducedObj;
};
并且因为您(至少在您的样本中)可能只使用1个键值从映射器发出的项目,您还需要一个finalize函数:
var finalize = function (key,value) {
if ( value.hasOwnProperty("name") ) {
value[value.type] = value.name;
delete value.type;
delete value.name;
}
return value;
};
然后按如下方式调用mapReduce函数:
db.collection.mapReduce(
mapper,
reducer,
{ "out": { "inline": 1 }, "finalize": finalize }
)
这给出了以下输出:
"results" : [
{
"_id" : "class A",
"value" : {
"type 1" : [
"ObjectA1"
],
"type 2" : [
"ObjectA2_1",
"ObjectA2_2"
]
}
},
{
"_id" : "class B ",
"value" : {
"type" : "type 3",
"Name" : "ObjectB3"
}
}
],
虽然结果是以非常mapReduce方式格式化的,但它与结果大致相同。
但如果你真的想进一步采取这种做法,你可以随时做到以下几点:
定义另一个映射器:
var mapper2 = function () {
emit( null, this );
};
另一个减速器:
var reducer2 = function (key,values) {
reducedObj = {};
values.forEach(function(value) {
reducedObj[value._id] = value.value;
});
return reducedObj;
};
然后将第一个mapReduce与输出一起运行到新集合:
db.collection.mapReduce(
mapper,
reducer,
{ "out": { "replace": "newcollection" }, "finalize": finalize }
)
接下来是新集合上的第二个mapReduce:
db.newcollection.mapReduce(
mapper2,
reducer2,
{ "out": { "inline": 1 } }
)
结果是:
"results" : [
{
"_id" : null,
"value" : {
"class A" : {
"type 1" : [
"ObjectA1"
],
"type 2" : [
"ObjectA2_1",
"ObjectA2_2"
]
},
"class B " : {
"type" : "type 3",
"Name" : "ObjectB3"
}
}
}
],
答案 1 :(得分:0)
我找到了我需要的解决方法。它不一样,但解决了我的问题。
db.myDb.aggregate(
{
$group:{
_id: {
class_name : "$class",
type_name : "$name"
},
items: {
$addToSet : "$name"
}
}
},
{
$group:{
_id : "$_id.class_name",
types : {
$addToSet : {
type : "$_id.type_name",
items : "$items"
}
}
}
})
这给了我类似的东西:
{
_id : "class A",
types: [
{
type: "type 1",
items: ["ObjectA1"]
},
{
type: "type 2",
items: ["ObjectA2_1", "ObjectA2_2"]
}
]
},
{
_id : "class B",
types: [
{
type: "type 3",
items: ["ObjectB3"]
}
]
}
代码和示例都写在这里,因此可能存在拼写错误。
所以这是关于它的。我要感谢#Neil Lunn的精彩回答和奉献精神。
马塞尔