2014-04-11 53 views
0

假設我有文檔,等等,有這些領域:MongoDB的查詢2級分組

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

我試過使用aggr與$ group的egate,但不能這樣做。有什麼想法嗎?

PS:我想在mongodb shell上做這個,不是貓鼬或者類似的東西。

回答

1

使用聚合框架的問題是,您無法爲對象的屬性指定任意的鍵名稱。因此,如果不能指定所有可能的密鑰名稱,就不可能重新使用它。

因此,要獲得你需要努力的東西在JavaScript中,如mapReduce結果:

首先定義一個映射:

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; 
}; 

而且,因爲您(至少在您的示例中)只有一個鍵值y的映射器將會發射可能的項目歐還需要一個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" 
          } 
        } 
      } 
    ], 
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"] 
     } 
    ] 
} 

代碼和例子在這裏被寫入所以可能有錯別字。

所以這就是它。我想感謝#尼爾倫恩的傑出回答和奉獻。

馬塞爾