Mongodb:Mapreduce查询过滤器位置运算符

时间:2014-04-09 10:16:26

标签: javascript node.js mongodb mapreduce aggregation-framework

我有一个庞大的数据集(以百万计),格式如下:

{
  "userid" : "codejammer",
  "data" : [       
   {"type" : "number", "value" : "23748"},
   {"type" : "message","value" : "one"}
  ]
}

我希望message计算one userid codejammer - var map = function(){ emit(this.data[0].value,1); }

以下是我正在使用的mapreduce函数: 地图:

var reduce = function(key,values){
    return Array.sum(values);
}

减少

var options = {
             "query":{"userid" : "codejammer",
                 "data.type" : "message"},
             "out" : "aggregrated"
            }

选项

{
 "_id" : 23748,
  "value" : 1
}

mapreduce函数使用以下输出成功执行:

{
 "_id" : one,
 "value" : 1
}

但是,我期待以下输出:

query

选项中的data.type : "message"过滤器,即使我特别要求query

,也会将整个数组发送到地图功能

有没有办法在{{1}}过滤器中使用projection运算符来仅获取数组中所需的项?

非常感谢你的帮助。

1 个答案:

答案 0 :(得分:2)

你最好用aggregate做这件事。在这种情况下不需要mapReduce,聚合框架作为本机代码运行,并且比通过JavaScript解释器运行要快得多:

db.collection.aggregate([
    // Still makes sense to match the documents to reduce the set
    { "$match": {
        "userid": "codejammer",
        "data": { "$elemMatch": { 
            "type": "message", "value": "one" 
        }}
    }},

    // Unwind to de-normalize the array content
    { "$unwind": "$data" },

    // Filter the content of the array
    { "$match": {
        "data.type": "message",
        "data.value": "one"
    }},

    // Count all the matching entries
    { "$group": {
        "_id": null,
        "count": { "$sum": 1 }
    }}
])

当然,如果你真的只有一个"消息"在你的"数据"数组变得非常简单:

db.collection.aggregate([
    // Match the documents you want
    { "$match": {
        "userid": "codejammer",
        "data": { "$elemMatch": { 
            "type": "message", "value": "one" 
        }}
    }},

    // Simply count the documents
    { "$group": {
        "_id": null,
        "count": { "$sum": 1 }
    }}
])

但当然这与此无异:

db.collection.find({
    "userid": "codejammer",
    "data": { "$elemMatch": { 
        "type": "message", "value": "one" 
    }}
}).count()

因此虽然有一种方法可以使用mapReduce来实现这一点,但显示的其他方式要好得多。特别是在新发布的2.6版本及以上版本中。在较新的版本中,聚合管道可以利用磁盘存储来处理非常大的集合。

但是要使用mapReduce获取计数,你基本上是以错误的方式进行计算。投影不能作为输入,因此您需要从结果中取出元素。我还是会考虑你的数组中可能有多个匹配值,即使不是这样:

db.collection.mapReduce(
    function() {
        var userid = this.userid;
        this.data.forEach(function(doc) {
            if ( doc == condition )
                emit( userid, 1 ); 
        });
    },
    function(key,values) {
        return values.length;
    },
    {
        "query": { 
            "userid": "codejammer",
            "data": { "$elemMatch": { 
                "type": "message", "value": "one" 
            }}
        },
        "scope": {
           "condition": {"type" : "message", "value" : "one"}
        },
        "out": { "inline": 1 }
    }
)

所以以同样的方式发出"发出"在数据数组中找到符合条件的文档时公用键的值。所以你不能只投射匹配的元素,你得到所有这些元素并以这种方式过滤。

由于您只期望一个结果,因此实际输出到集合没有任何意义,因此只需将其作为一个结果发送出去。

但基本上,如果必须这样做,请使用聚合方法。