использование $regex в структуре агрегации mongodb в $group

Рассмотрим следующий пример:

db.article.aggregate(
  { $group : {
      _id : "$author",
      docsPerAuthor : { $sum : 1 },
      viewsPerAuthor : { $sum : "$pageViews" }
  }}
);

Это группирует по полю автора и вычисляет два поля.

У меня есть значения для $author = FirstName_LastName. Теперь вместо группировки по $author я хочу сгруппировать по всем авторам с одинаковой фамилией.

Я попытался $regex сгруппировать по всем совпадающим строкам после '_'

$author.match(/_[a-zA-Z0-9]+$/)

db.article.aggregate(
  { $group : {
      _id : "$author".match(/_[a-zA-Z0-9]+$/),
      docsPerAuthor : { $sum : 1 },
      viewsPerAuthor : { $sum : "$pageViews" }
  }}
);

also tried the following:

 db.article.aggregate(
  { $group : {
      _id : {$author: {$regex: /_[a-zA-Z0-9]+$/}},
      docsPerAuthor : { $sum : 1 },
      viewsPerAuthor : { $sum : "$pageViews" }
  }}
);

person user1447121    schedule 09.02.2013    source источник


Ответы (4)


На самом деле нет такого метода, который обеспечивает такую ​​функциональность, или я не смог найти подходящую версию, которая его содержит. Я думаю, что это не будет работать с $regexp: http://docs.mongodb.org/manual/reference/operator/regex/ это просто для сопоставления с образцом.

В jira есть запрос на улучшение: https://jira.mongodb.org/browse/SERVER-6773

Он находится в открытом неразрешенном состоянии. НО

в github я нашел это обсуждение: https://github.com/mongodb/mongo/pull/336

И если вы проверите этот коммит: https://github.com/nleite/mongo/commit/2dd175a5acda86aaad61f5eb9dab83ee19915709

он содержит более или менее именно тот метод, который вы, вероятно, имеете. Я не очень понимаю суть этого улучшения: в версии 2.2.3 оно не работает.

person attish    schedule 09.02.2013

Используйте mapReduce: это общая форма агрегации. Вот как действовать в оболочке монго: определить функцию карты

var mapFunction = function() {
  var key = this.author.match(/_[a-zA-Z0-9]+$/)[0];
  var nb_match_bar2 = 0;
  if( this.bar.match(/bar2/g) ){
    nb_match_bar2 = 1;
  }
  var value = {
    docsPerAuthor: 1,
    viewsPerAuthor: Array.sum(this.pageViews)
  };

  emit( key, value );
};

и функция уменьшения

var reduceFunction = function(key, values) {

  var reducedObject = {
    _id: key,
    docsPerAuthor: 0,
    viewsPerAuthor: 0
  };

  values.forEach( function(value) {
    reducedObject.docsPerAuthor += value.docsPerAuthor;
    reducedObject.viewsPerAuthor += value.viewsPerAuthor;
  }
  );
  return reducedObject;
};

запустите mapReduce и сохраните результат в map_reduce_result

>db.st.mapReduce(mapFunction, reduceFunction, {out:'map_reduce_result'})

запросите map_reduce_result, чтобы получить результат

>db.map_reduce_result.find()
person innoSPG    schedule 05.07.2013

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

Здесь мы вычисляем имя поля как подстроку после символа '_', пробуем каждую из его возможных позиций (вот почему существует цепочка $cond) и возвращаем полное $author, если первое имя слишком длинная:

http://mongotry.herokuapp.com/#?bookmarkId=52fb5f24a0378802003b4c68

[
{
    "$project": {
        "author": 1,
        "pageViews": 1,
        "name": {
            "$cond": [
                {
                    "$eq": [
                        {
                            "$substr": [
                                "$author",
                                0,
                                1
                            ]
                        },
                        "_"
                    ]
                },
                {
                    "$substr": [
                        "$author",
                        1,
                        999
                    ]
                },
                {
                    "$cond": [
                        {
                            "$eq": [
                                {
                                    "$substr": [
                                        "$author",
                                        1,
                                        1
                                    ]
                                },
                                "_"
                            ]
                        },
                        {
                            "$substr": [
                                "$author",
                                2,
                                999
                            ]
                        },
                        {
                            "$cond": [
                                {
                                    "$eq": [
                                        {
                                            "$substr": [
                                                "$author",
                                                2,
                                                1
                                            ]
                                        },
                                        "_"
                                    ]
                                },
                                {
                                    "$substr": [
                                        "$author",
                                        3,
                                        999
                                    ]
                                },
                                {
                                    "$cond": [
                                        {
                                            "$eq": [
                                                {
                                                    "$substr": [
                                                        "$author",
                                                        3,
                                                        1
                                                    ]
                                                },
                                                "_"
                                            ]
                                        },
                                        {
                                            "$substr": [
                                                "$author",
                                                4,
                                                999
                                            ]
                                        },
                                        {
                                            "$cond": [
                                                {
                                                    "$eq": [
                                                        {
                                                            "$substr": [
                                                                "$author",
                                                                4,
                                                                1
                                                            ]
                                                        },
                                                        "_"
                                                    ]
                                                },
                                                {
                                                    "$substr": [
                                                        "$author",
                                                        5,
                                                        999
                                                    ]
                                                },
                                                "$author"
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    }
},
{
    "$group": {
        "_id": "$name",
        "viewsPerAuthor": {
            "$sum": "$pageViews"
        }
    }
}
]
person blacelle    schedule 12.02.2014

$group у меня работает сочетание $addFields и $arrayElemAt (версия ≥ 3.4).

Скажем, у нас есть следующие данные в коллекции faculty, базе данных school:

{ "_id" : ObjectId("5ed5a59b1febc4c796a88e80"), "name" : "Harry_Potter" }
{ "_id" : ObjectId("5ed5a60e1febc4c796a88e81"), "name" : "Edison_Potter" }
{ "_id" : ObjectId("5ed5a6231febc4c796a88e82"), "name" : "Jack_Potter" }
{ "_id" : ObjectId("5ed5a62f1febc4c796a88e83"), "name" : "Alice_Walker" }
{ "_id" : ObjectId("5ed5a65f1febc4c796a88e84"), "name" : "Bob_Walker" }
{ "_id" : ObjectId("5ed5a6731febc4c796a88e85"), "name" : "Will_Smith" }

Ниже можно сгруппировать каждый документ по фамилии:

db.faculty.aggregate([
  {
    $addFields: {
      lastName: {
        $arrayElemAt: [ { $split: ["$name", "_"] }, 1 ]
      }
    }
  },
  {
    $group: {
      _id: "$lastName",
      count: {$sum: 1}
    }
  }
])

Результат работы:

{ "_id" : "Potter", "count" : 3 }
{ "_id" : "Walker", "count" : 2 }
{ "_id" : "Smith", "count" : 1 }

Я использовал трюк, чтобы добавить поле с именем lastName. Основываясь на том, что у вас есть для поля name, его можно разделить на массив по _. Фамилия находится в индексе 1, а имя в индексе 0.

Ссылка

$addFields (агрегация)

$arrayElemAt (агрегация)

person Wenhe Qi    schedule 02.06.2020