【问题标题】:Is there a workaround to allow using a regex in the Mongodb aggregation pipeline是否有解决方法允许在 Mongodb 聚合管道中使用正则表达式
【发布时间】:2013-07-01 17:52:57
【问题描述】:

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

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 操作中执行这个匹配?或者,或者,我也愿意在管道中较早的地方进行匹配并将结果存储在文档中,这样我此时只需在布尔值上进行匹配。

【问题讨论】:

    标签: regex mongodb mapreduce aggregation-framework pymongo


    【解决方案1】:

    这个问题似乎来了很多次都没有解决办法。 我知道有两种可能的解决方案: 解决方案 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 的数量,以及 bar 包含子字符串 'bar2' 的 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 );
    };
    

    还有下面的reduce函数

    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:在与第一步相同的 key 上分组,并计算每组中的 doc 数 {$sum: 1}; 第 3 步:合并第 1 步和第 2 步的结果:为两个结果中出现的每个键添加新字段,如果第二个结果中不存在该键,则将新键设置为 0。

    瞧!另一个解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-18
      • 2018-08-23
      • 2014-08-11
      • 1970-01-01
      • 1970-01-01
      • 2011-05-23
      相关资源
      最近更新 更多