Статьи

MongoDB Aggregation Framework Примеры в C #

MongoDB версии 2.2 была выпущена в конце августа, и самым большим изменением стало добавление Aggregation Framework. Раньше для агрегации требовалось использовать отображение / уменьшение, которое в MongoDB работает не так хорошо, в основном из -за однопоточного исполнения на основе Javascript . Инфраструктура агрегирования отходит от Javascript и реализована на C ++ с целью повысить производительность аналитики и отчетности до 80 процентов по сравнению с использованием MapReduce.

Цель этого поста — показать примеры запуска MongoDB Aggregation Framework с официальными драйверами MongoDB C #.

Структура агрегации и Linq

Хотя текущая версия драйверов MongoDB C # (1.6) поддерживает Linq, эта поддержка не распространяется на структуру агрегации. Вполне вероятно, что Linq-поддержка будет добавлена ​​позже, и в исходном коде драйвера уже есть некоторые намеки на это. Но на этом этапе выполнение агрегатов требует использования BsonDocument-объектов.

Структура агрегации и GUID

Если вы используете GUID в своих документах, структура агрегирования не работает. Это связано с тем, что по умолчанию GUID хранятся в двоичном формате, и агрегаты не будут работать с документами, которые содержат двоичные данные. Решение состоит в том, чтобы хранить GUID в виде строк. Вы можете заставить драйверы C # сделать это преобразование автоматически, настроив отображение. Учитывая, что ваш класс C # имеет Id-свойство, определенное как GUID, следующий код говорит драйверу сериализовать GUID в виде строки:

BsonClassMap.RegisterClassMap<MyClass>(cm => 
{ 
    cm.AutoMap(); 
    cm.GetMemberMap(c => c.Id) 
      .SetRepresentation( 
          BsonType.String); 
});

Пример данных

В этих примерах используются следующие документы:

> db.examples.find ()
{«_id»: «1», «User»: «Tom», «Country»: «Finland», «Count»: 1}
{«_id»: «2», «User «:» Tom «,» Country «:» Finland «,» Count «: 3}
{» _id «:» 3 «,» User «:» Tom «,» Country «:» Finland «,» Count «: 2 }
{«_id»: «4», «Пользователь»: «Мэри», «Страна»: «Швеция», «Количество»: 1}
{«_id»: «5», «Пользователь»: «Мэри», » Страна «:» Швеция «,» Граф «: 7}

Пример 1. Базовое использование Aggregation Framework

Этот пример показывает, как структура агрегации может быть выполнена через C #. Мы не собираемся проводить какие-либо вычисления с данными, мы просто собираемся отфильтровать их по пользователю.

Для запуска агрегатов вы можете использовать MongoDatabase.RunCommand –method или вспомогательный MongoCollection.Aggregate. Мы собираемся использовать последний:

var coll = localDb.GetCollection("examples"); 
... 
coll.Aggregate(pipeline);

Самая сложная часть при работе с Aggregation Framework через C # — это создание конвейера. Трубопровод аналогична концепции к трубопроводу в PowerShell . Каждая операция в конвейере вносит изменения в данные: операции могут, например, фильтровать, группировать и проецировать данные. В C # конвейер является коллекцией объекта BsonDocument. Каждый документ представляет одну операцию.

В нашем первом примере нам нужно сделать только одну операцию: $ match. Этот оператор отфильтрует данные документы. Следующий BsonDocument является конвейерной операцией, которая отфильтровывает все документы, для которых в поле User не установлено значение «Tom».

var match = new BsonDocument 
                { 
                    { 
                        "$match", 
                        new BsonDocument 
                            { 
                                {"User", "Tom"} 
                            } 
                    } 
                };

Для выполнения этой операции мы добавляем ее в массив и передаем массив методу MongoCollection.Aggregate:

var pipeline = new[] { match }; 
var result = coll.Aggregate(pipeline);

MongoCollection.Aggregate-метод возвращает объект AggregateResult. Свойство ResultDocuments (IEnumarable <BsonDocument>) содержит документы, являющиеся выходными данными агрегации. Чтобы проверить, сколько было результатов, мы можем получить Count:

var result = coll.Aggregate(pipeline); 
Console.WriteLine(result.ResultDocuments.Count());

образ

Полученные документы являются объектами BsonDocument. Если у вас есть C # -класс, который представляет документы, вы можете привести результаты:

var matchingExamples = result.ResultDocuments 
    .Select(BsonSerializer.Deserialize<ExampleData>) 
    .ToList();

foreach (var example in matchingExamples) 
{ 
    var message = string.Format("{0} - {1}", example.User, example.Count); 
    Console.WriteLine(message); 
}

образ

Другой альтернативой является использование динамического типа C #. Следующий метод расширения использует  JSON.net для преобразования BsonDocument в динамический:

public static class MongoExtensions 
{ 
    public static dynamic ToDynamic(this BsonDocument doc) 
    { 
        var json = doc.ToJson(); 
        dynamic obj = JToken.Parse(json); 
        return obj; 
    } 
}

Вот способ преобразовать все полученные документы в динамические объекты:

var matchingExamples = result.ResultDocuments 
    .Select(x => x.ToDynamic()) 
    .ToList();

Пример 2. Несколько фильтров и операторов сравнения

В этом примере данные фильтруются по следующим критериям:

  • Пользователь: Tom
  • Количество:> = 2
var match = new BsonDocument 
                { 
                    { 
                        "$match", 
                        new BsonDocument 
                            { 
                                {"User", "Tom"}, 
                                {"Count", new BsonDocument 
                                                   { 
                                                       { 
                                                           "$gte", 2 
                                                       } 
                                                   }} 
                            } 
                    } 
                };

Выполнение этой операции идентично первому примеру:

var pipeline = new[] { match }; 
var result = coll.Aggregate(pipeline);

var matchingExamples = result.ResultDocuments 
    .Select(x => x.ToDynamic()) 
    .ToList();

Также результат, как и ожидалось:

foreach (var example in matchingExamples) 
{ 
    var message = string.Format("{0} - {1}", example.User, example.Count); 
    Console.WriteLine(message); 
}

образ

Пример 3: несколько операций

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

  • Пользователь: Tom
  • Количество:> = 2
var match = new BsonDocument 
                { 
                    { 
                        "$match", 
                        new BsonDocument 
                            { 
                                {"User", "Tom"} 
                            } 
                    } 
                };

var match2 = new BsonDocument 
                { 
                    { 
                        "$match", 
                        new BsonDocument 
                            { 
                                {"Count", new BsonDocument 
                                                   { 
                                                       { 
                                                           "$gte", 2 
                                                       } 
                                                   }} 
                            } 
                    } 
                };

var pipeline = new[] { match, match2 };

Результат остается прежним:

образ

Первая операция «сопоставить» берет все документы из коллекции примеров и удаляет каждый документ, который не соответствует критериям User = Tom. Выходные данные этой операции (3 документа) затем перемещаются ко второй операции «match2» конвейера. Эта операция видит только те 3 документа, а не оригинальную коллекцию. Операция отфильтровывает эти документы на основе своих критериев и перемещает результат (2 документа) вперед. На этом наш трубопровод заканчивается, и это тоже наш результат.

Пример 4: Группа и сумма

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

var group = new BsonDocument 
                { 
                    { "$group", 
                        new BsonDocument 
                            { 
                                { "_id", new BsonDocument 
                                             { 
                                                 { 
                                                     "MyUser","$User" 
                                                 } 
                                             } 
                                }, 
                                { 
                                    "Count", new BsonDocument 
                                                 { 
                                                     { 
                                                         "$sum", 1 
                                                     } 
                                                 } 
                                } 
                            } 
                  } 
                };

Группирования ключа (в нашем случае пользователя поле) определяются с _id. В приведенном выше примере указано, что ключ группировки имеет одно поле («MyUser»), и значение этого поля берется из пользовательского поля документа ($ User). В операции $ group другие поля являются агрегатными функциями . В этом примере определяется поле «Количество» и добавляется 1 к нему для каждого документа, который соответствует ключу группы (_id).

var pipeline = new[] { group }; 
var result = coll.Aggregate(pipeline);

var matchingExamples = result.ResultDocuments 
    .Select(x => x.ToDynamic()) 
    .ToList();

foreach (var example in matchingExamples) 
{ 
    var message = string.Format("{0} - {1}", example._id.MyUser, example.Count); 
    Console.WriteLine(message); 
}

образ

Обратите внимание на формат, в котором выводятся результаты: имя пользователя доступно через _id.MyUser-property.

Пример 5: Группировка и сумма по полю

Этот пример аналогичен примеру 4. Но вместо того, чтобы вычислять количество документов, мы вычисляем сумму полей Count пользователем:

var group = new BsonDocument 
                { 
                    { "$group", 
                        new BsonDocument 
                            { 
                                { "_id", new BsonDocument 
                                             { 
                                                 { 
                                                     "MyUser","$User" 
                                                 } 
                                             } 
                                }, 
                                { 
                                    "Count", new BsonDocument 
                                                 { 
                                                     { 
                                                         "$sum", "$Count" 
                                                     } 
                                                 } 
                                } 
                            } 
                  } 
                };

Единственное изменение состоит в том, что вместо добавления 1 мы добавляем значение из поля Count («$ Count»).

образ

Пример 6: Прогнозы

В этом примере показано, как оператор $ project может использоваться для изменения формата вывода. Группировка в примере 5 работает хорошо, но для доступа к имени пользователя нам необходимо указать свойство _id.MyUser. Давайте изменим это так, чтобы имя пользователя было доступно напрямую через UserName-property:

var group = new BsonDocument 
                { 
                    { "$group", 
                        new BsonDocument 
                            { 
                                { "_id", new BsonDocument 
                                             { 
                                                 { 
                                                     "MyUser","$User" 
                                                 } 
                                             } 
                                }, 
                                { 
                                    "Count", new BsonDocument 
                                                 { 
                                                     { 
                                                         "$sum", "$Count" 
                                                     } 
                                                 } 
                                } 
                            } 
                  } 
                };

var project = new BsonDocument 
                { 
                    { 
                        "$project", 
                        new BsonDocument 
                            { 
                                {"_id", 0}, 
                                {"UserName","$_id.MyUser"}, 
                                {"Count", 1}, 
                            } 
                    } 
                };

var pipeline = new[] { group, project };

Код удаляет _id –property из вывода. Он добавляет свойство UserName, значение которого доступно из поля _id.MyUser. Операции проецирования также утверждают, что значение Count должно оставаться как есть.

var matchingExamples = result.ResultDocuments 
    .Select(x => x.ToDynamic()) 
    .ToList();

foreach (var example in matchingExamples) 
{ 
    var message = string.Format("{0} - {1}", example.UserName, example.Count); 
    Console.WriteLine(message); 
}

образ

Пример 7: Группировка с несколькими полями в ключах

Для этого примера мы добавляем новую строку в нашу коллекцию документов, оставляя нам следующее:

{«_id»: «1», «Пользователь»: «Том», «Страна»: «Финляндия», «Количество»: 1}
{«_id»: «2», «Пользователь»: «Том», «Страна» «:» Finland «,» Count «: 3}
{» _id «:» 3 «,» User «:» Tom «,» Country «:» Finland «,» Count «: 2}
{» _id «:» 4 «,» Пользователь «:» Мэри «,» Страна «:» Швеция «,» Количество «: 1}
{» _id «:» 5 «,» Пользователь «:» Мэри «,» Страна «:» Швеция «,» Count «: 7}
{» _id «:» 6 «,» User «:» Tom «,» Country «:»Англия «,» Граф «: 3}

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

var group = new BsonDocument 
                { 
                    { "$group", 
                        new BsonDocument 
                            { 
                                { "_id", new BsonDocument 
                                             { 
                                                 { "MyUser","$User" }, 
                                                 { "Country","$Country" }, 
                                             } 
                                }, 
                                { 
                                    "Count", new BsonDocument 
                                                 { 
                                                     { "$sum", "$Count" } 
                                                 } 
                                } 
                            } 
                  } 
                };

var project = new BsonDocument 
                { 
                    { 
                        "$project", 
                        new BsonDocument 
                            { 
                                {"_id", 0}, 
                                {"UserName","$_id.MyUser"}, 
                                {"Country", "$_id.Country"}, 
                                {"Count", 1}, 
                            } 
                    } 
                };

var pipeline = new[] { group, project }; 
var result = coll.Aggregate(pipeline);

var matchingExamples = result.ResultDocuments 
    .Select(x => x.ToDynamic()) 
    .ToList();

foreach (var example in matchingExamples) 
{ 
    var message = string.Format("{0} - {1} - {2}", example.UserName, example.Country, example.Count); 
    Console.WriteLine(message); 
}

образ

Пример 8: Матч, группа и проект

Этот пример показывает, как вы можете комбинировать множество различных конвейерных операций. Данные сначала фильтруются ( $ match ) по User = Tom, затем группируются по стране (« $ group ») и, наконец, выходные данные форматируются в читаемый формат ( $ project ).

Совпадение:

var match = new BsonDocument 
                { 
                    { 
                        "$match", 
                        new BsonDocument 
                            { 
                                {"User", "Tom"} 
                            } 
                    } 
                };

Группа:

var group = new BsonDocument 
                { 
                    { "$group", 
                        new BsonDocument 
                            { 
                                { "_id", new BsonDocument 
                                             { 
                                                 { "Country","$Country" }, 
                                             } 
                                }, 
                                { 
                                    "Count", new BsonDocument 
                                                 { 
                                                     { "$sum", "$Count" } 
                                                 } 
                                } 
                            } 
                  } 
                };

Проект:

var project = new BsonDocument 
                { 
                    { 
                        "$project", 
                        new BsonDocument 
                            { 
                                {"_id", 0}, 
                                {"Country", "$_id.Country"}, 
                                {"Count", 1}, 
                            } 
                    } 
                };

Результат:

var pipeline = new[] { match, group, project }; 
var result = coll.Aggregate(pipeline);

var matchingExamples = result.ResultDocuments 
    .Select(x => x.ToDynamic()) 
    .ToList();

foreach (var example in matchingExamples) 
{ 
    var message = string.Format("{0} - {1}", example.Country, example.Count); 
    Console.WriteLine(message); 
}

образ

Больше

В платформе агрегации MongoDB есть много других интересных операторов, таких как $ unwind и $ sort . Использование этих операторов идентично тем, которые мы использовали выше, поэтому должна быть возможность скопировать и вставить один из примеров и использовать его в качестве основы для этих других операций.

связи