2013-05-11 112 views
1

我已经有一个文档集合的结构,像这样的罪状:MongoDB的聚合:获取密钥,值对

{ 
    responses: { 
    key1: bar, 
    key2: quux 
    ... 
    },... 
} 

有没有办法让每个唯一键的数量,值对在responses对象?例如,我希望看到类似以下的结果(具体结构无关紧要):

{ 
    key1: { 
    foo: 7 // aka 7 objects are similar to { responses: [{key: foo}] } 
    bar: 30 
    }, 
    key2: { 
    baz: 24, 
    quux: 13 
    } 
} 
+1

你事先知道所有的按键名称或没有? – 2013-05-11 03:29:02

+0

不幸的是,我不知道关键的名字 - 会让事情变得更容易。它们与用户输入相关联。 – 2013-05-11 06:29:55

回答

2

有几个方法可以做到这一点。聚合框架无法完成,因为您无法将键名映射到值。但地图降低使得它相对简单:

map = function() { 
     for (k in this.responses) { 
      emit({ key: k, response: this.responses[k]}, 1); 
     } 
} 

reduce = function (k, values) { 
     result = 0; 
     values.forEach(function(v) { result += v; }); 
     return result; 
} 

这个样本数据集:

> db.responses.find({},{_id:0,responses:1}).pretty() 
{ "responses" : { "key1" : "foo", "key2" : "bar" } } 
{ "responses" : { "key1" : "foo", "key3" : "bar" } } 
{ "responses" : { "key2" : "foo", "key3" : "bar" } } 
{ "responses" : { "key3" : "baz" } } 

运行MR让你:

> db.responses.mapReduce(map, reduce, {out:{inline:1}}) 
{ 
    "results" : [ 
     { 
      "_id" : { 
       "key" : "key1", 
       "response" : "foo" 
      }, 
      "value" : 2 
     }, 
     { 
      "_id" : { 
       "key" : "key2", 
       "response" : "bar" 
      }, 
      "value" : 1 
     }, 
     { 
      "_id" : { 
       "key" : "key2", 
       "response" : "foo" 
      }, 
      "value" : 1 
     }, 
     { 
      "_id" : { 
       "key" : "key3", 
       "response" : "bar" 
      }, 
      "value" : 2 
     }, 
     { 
      "_id" : { 
       "key" : "key3", 
       "response" : "baz" 
      }, 
      "value" : 1 
     } 
    ], 
    "timeMillis" : 65, 
    "counts" : { 
     "input" : 4, 
     "emit" : 7, 
     "reduce" : 2, 
     "output" : 5 
    }, 
    "ok" : 1, 
} 

这里是通过MapReduce的做到这一点的第二种方式 - 这给出的输出更像你所说的你正在寻找的东西:

m2 = function() { 
    for (k in this.responses) { 
     keyname = this.responses[k]; 
     val = {}; 
     val[keyname] = 1; 
     emit (k, val); 
    } 
} 
r2 = function (k, values) { 
     result = { }; 
     values.forEach (function(v) { 
      for (k in v) { 
      if (result[k] > 0) { 
       result[k] += v[k]; 
      } else { 
       result[k] = v[k]; 
      } 
      } 
     }); 
     return result; 
} 

结果是:

> db.responses.mapReduce(m2, r2, {out:{inline:1}}) 
{ 
    "results" : [ 
     { 
      "_id" : "key1", 
      "value" : { 
       "foo" : 2 
      } 
     }, 
     { 
      "_id" : "key2", 
      "value" : { 
       "bar" : 1, 
       "foo" : 1 
      } 
     }, 
     { 
      "_id" : "key3", 
      "value" : { 
       "bar" : 2, 
       "baz" : 1 
      } 
     } 
    ], 
    "timeMillis" : 3, 
    "counts" : { 
     "input" : 4, 
     "emit" : 7, 
     "reduce" : 3, 
     "output" : 3 
    }, 
    "ok" : 1, 
} 
1

这可以通过缩小地图来完成。你会找到你想要用你的地图功能计算的所有键,并减少发射到你的缩小功能计数的结果。

这是地图的一个很好的视频的解释在你的MongoDB减少之前没有使用过...

http://www.youtube.com/watch?v=WovfjprPD_I