是否有一种解决方法允许在Mongodb聚合管道中使用正则表达式

时间:2013-07-03 21:11:26

标签: regex mongodb mapreduce aggregation-framework pymongo

我正在尝试创建一个管道来计算有多少文档符合某些条件。我看不出在条件中使用正则表达式的任何方法。这是我的带有注释的管道的简化版本:

db.Collection.aggregate([
    // Pipeline before the issue
    {'$group': {
        '_id': {
            'field': '$my_field', // Included for completeness
        },
        'first_count': {'$sum': {                    // We're going to count the number
            '$cond': [                               // of documents that have 'foo' in 
                {'$eq: ['$field_foo', 'foo']}, 1, 0  // $field_foo.
            ] 
        }},                                       

        'second_count': {'$sum': {                       // Here, I want to count the
            '$cond': [                                   // Number of documents where
                {'$regex': ['$field_bar', regex]}, 1, 0  // the value of 'bar' matches
            ]                                            // the regex 
        }},                                          
    },
    // Additional operations
])

我知道语法错误,但我希望这能传达出我想要做的事情。有没有办法在$ cond操作中执行此匹配?或者,或者,我也可以在管道中的某个地方进行匹配,并将结果存储在文档中,以便我此时只需匹配一个布尔值。

1 个答案:

答案 0 :(得分:5)

这个问题似乎很多次没有解决方案。 我知道有两种可能的解决方案: 解决方案1-使用mapReduce。 mapReduce是聚合的一般形式,它允许用户做任何可以想象和可编程的事情。

以下是使用mapReduce的mongo shell解决方案 我们考虑以下'st'集合。

  

db.st.find()

{ "_id" : ObjectId("51d6d23b945770d6de5883f1"), "foo" : "foo1", "bar" : "bar1" }
{ "_id" : ObjectId("51d6d249945770d6de5883f2"), "foo" : "foo2", "bar" : "bar2" }
{ "_id" : ObjectId("51d6d25d945770d6de5883f3"), "foo" : "foo2", "bar" : "bar22" }
{ "_id" : ObjectId("51d6d28b945770d6de5883f4"), "foo" : "foo2", "bar" : "bar3" }
{ "_id" : ObjectId("51d6daf6945770d6de5883f5"), "foo" : "foo3", "bar" : "bar3" }
{ "_id" : ObjectId("51d6db03945770d6de5883f6"), "foo" : "foo4", "bar" : "bar24" }

我们希望按foo分组,并且对于每个foo,计算doc的数量,以及包含子字符串'bar2'的bar的doc数。即:

foo1: nbdoc=1, n_match = 0
foo2: nbdoc=3, n_match = 2
foo3: nbdoc=1, n_match = 0
foo4: nbdoc=1, n_match = 1

为此,请定义以下地图功能

var mapFunction = function() {
  var key = this.foo;
  var nb_match_bar2 = 0;
  if( this.bar.match(/bar2/g) ){
    nb_match_bar2 = 1;
  }
  var value = {
    count: 1,
    nb_match: nb_match_bar2
  };

  emit( key, value );
};

以及以下减少功能

var reduceFunction = function(key, values) {

  var reducedObject = {
    count: 0,
    nb_match:0
  };
  values.forEach( function(value) {
    reducedObject.count += value.count;
    reducedObject.nb_match += value.nb_match;
  }
  );
  return reducedObject;
};

运行mapduce并将结果存储在集合map_reduce_result

db.st.mapReduce(mapFunction, reduceFunction, {out:'map_reduce_result'})
{
  "result" : "map_reduce_result",
  "timeMillis" : 7,
  "counts" : {
    "input" : 6,
    "emit" : 6,
    "reduce" : 1,
    "output" : 4
},
"ok" : 1,
}

最后,我们可以查询集合map_reduce_result,瞧!解决方案

> db.map_reduce_result.find()
{ "_id" : "foo1", "value" : { "count" : 1, "nb_match" : 0 } }
{ "_id" : "foo2", "value" : { "count" : 3, "nb_match" : 2 } }
{ "_id" : "foo3", "value" : { "count" : 1, "nb_match" : 0 } }
{ "_id" : "foo4", "value" : { "count" : 1, "nb_match" : 1 } }

解决方案2-使用两个单独的聚合并合并 我不会详细介绍这个解决方案,因为任何mongo用户都可以轻松完成。 第1步:进行聚合,忽略需要正则表达式求和的部分。 步骤2:对与第一步相同的密钥进行第二次聚合分组。   管道的第1阶段:匹配正则表达式;   阶段2:在与第一步相同的密钥上分组,并计算每组中的doc数{$ sum:1}; 步骤3:合并步骤1和2的结果:对于两个结果中出现的每个键添加新字段,如果第二个结果集中没有键,则将新键设置为0。

瞧!另一种解决方案。