【问题标题】:MongoDB aggregation: get counts of key, value pairsMongoDB 聚合:获取键、值对的计数
【发布时间】:2013-05-12 07:04:48
【问题描述】:

我有一个包含这样结构的文档的集合:

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

【问题讨论】:

  • 你知道所有的键名吗?
  • 不幸的是,我不知道键名——这会让事情变得容易得多。它们与用户输入相关。

标签: mongodb


【解决方案1】:

有几种方法可以做到这一点。聚合框架还不能做到这一点,因为您无法将键名映射到值。但是 map reduce 让它变得相对简单:

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

【讨论】:

    【解决方案2】:

    这可以通过 map reduce 来完成。你会发现你想用你的 map 函数计算的所有键,并在你的 reduce 函数中将发出的结果归约到一个计数中。

    这是一个很好的关于mongodb中map reduce的视频解释,你以前没用过……

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-30
      • 1970-01-01
      • 2016-07-03
      • 2020-06-21
      • 1970-01-01
      • 1970-01-01
      • 2023-01-12
      相关资源
      最近更新 更多