Есть ли обходной путь, позволяющий использовать регулярное выражение в конвейере агрегации Mongodb

Я пытаюсь создать конвейер, который будет подсчитывать, сколько документов соответствует некоторым условиям. Однако я не вижу способа использовать регулярное выражение в условиях. Вот упрощенная версия моего конвейера с аннотациями:

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? Или, в качестве альтернативы, я также открыт для возможности выполнить сопоставление где-нибудь на более раннем этапе конвейера и сохранить результат в документах, так что на этом этапе мне нужно сопоставить только логическое значение.


person Skunkwaffle    schedule 03.07.2013    source источник


Ответы (1)


Этот вопрос, кажется, возникает много раз без решения. Я знаю два возможных решения: решение 1 - использование mapReduce. mapReduce - это общая форма агрегирования, которая позволяет пользователю делать все, что можно вообразить и запрограммировать.

Ниже приводится решение оболочки mongo с использованием mapReduce. Мы рассматриваем следующую коллекцию «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, а также количество doc с bar, содержащим подстроку bar2, то есть:

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: сгруппируйте по тому же ключу, что и на первом этапе, и подсчитайте количество документов в каждой группе {$ sum: 1}; шаг 3: объединить результат шага 1 и 2: для каждого ключа, который появляется в обоих результатах, добавьте новое поле, если ключ отсутствует во втором результате, установите новый ключ на 0.

Вуаля! другое решение.

person innoSPG    schedule 05.07.2013