Проекция

Последнее обновление: 28.10.2022

Если документы имеют сложную структуру, которая нам не очень подходит для вывода, то мы можем использовать проекции, то есть из изначальных документов коллекции получить совершенно другие данные. Для создания проекции применяется построитель проекции Builders.Projection. Этот класс предоставляет два метода для управления проекцией:

  • Include(): добавляет поле в выходной результат

  • Exclude(): исключает поле из выходного результата

Эти методы возвращают определение проекции в виде объекта ProjectionDefinition

Для выполнения самой проекции вызывается метод Projection(), в который передается объект ProjectionDefinition

Допустим, у нас есть коллекция users, где храняться следующие документы:

{ "_id" : ObjectId("63596dc76348749cac779373"), "Name" : "Tom", "Age" : 38, "Languages" : ["english", "german", "spanish"] }
{ "_id" : ObjectId("63596dc76348749cac779374"), "Name" : "Bob", "Age" : 42, "Languages" : ["english"] }
{ "_id" : ObjectId("63596dc76348749cac779375"), "Name" : "Sam", "Age" : 25, "Languages" : ["english", "spanish"] }
{ "_id" : ObjectId("63596dc76348749cac779376"), "Name" : "Alice", "Age" : 33 }
{ "_id" : ObjectId("63596dc76348749cac779377"), "Name" : "Tom", "Age" : 33, "Languages" : ["english"] }

Оставим в документах только поля "Name" и "Age":

using MongoDB.Bson;
using MongoDB.Driver;

MongoClient client = new MongoClient("mongodb://localhost:27017");

var db = client.GetDatabase("test"); 
var collection = db.GetCollection<BsonDocument>("users");

var projection = Builders<BsonDocument>.Projection.Include("Name").Include("Age").Exclude("_id");
var users = await collection.Find(new BsonDocument()).Project(projection).ToListAsync();

foreach (var user in users)  Console.WriteLine(user);

Поскольку коллекция типизируется типом BsonDocument, то построитель проекции тоже типизируется данным типом. Для включения двух полей в выборку у построителя проекции последовательно вызывается два раза метод Include и один раз метод Exclude:

var projection = Builders<BsonDocument>.Projection.Include("Name").Include("Age").Exclude("_id");

Стоит отметить, что в отличие от других полей поле "_id" необходимо явным образом исключать при вызове методов Include.

Далее полученное определение проекции передается в метод Projection:

var users = await collection.Find(new BsonDocument()).Project(projection).ToListAsync();

В итоге мы получим следующий результат:

{ "Name" : "Tom", "Age" : 38 }
{ "Name" : "Bob", "Age" : 42 }
{ "Name" : "Sam", "Age" : 25 }
{ "Name" : "Alice", "Age" : 33 }
{ "Name" : "Tom", "Age" : 33 }

В данном случае мы также могли бы просто исключить поле Languages:

var projection = Builders<BsonDocument>.Projection.Exclude("Languages").Exclude("_id");

Как правило, логика следующая: если полей в коллекции много, а нам надо немного, то методом Include включаем нужные поля. Если же полей немного, и мы хотим исключить также небольшое количество полей, то проще исключить ненужные поля методом Exclude, как в случае выше.

Стоит отметить, что определение проекции также можно записать в виде строки:

using MongoDB.Bson;
using MongoDB.Driver;

MongoClient client = new MongoClient("mongodb://localhost:27017");

var db = client.GetDatabase("test"); 
var collection = db.GetCollection<BsonDocument>("users");

var users = await collection.Find("{}").Project("{Name:1, Age:1, _id:0}").ToListAsync();

foreach (var user in users)  Console.WriteLine(user);

Выражение "{Name:1, Age:1, _id:0}" указывает, что из всех полей в финальную выборку будут попадать только поля Name и Age, так как для ни установлена единица. Все остальные поля не добавляются. Исключение только _id, поэтому если мы не хотим видеть данное поле в выборке, то для него нало установить ноль.

Теперь спроецируем выборку документов на какой-нибудь другой тип. Для проекции определим дополнительный класс Person:

using MongoDB.Bson;
using MongoDB.Driver;

MongoClient client = new MongoClient("mongodb://localhost:27017");

var db = client.GetDatabase("test"); 
var collection = db.GetCollection<BsonDocument>("users");

var users = await collection.Find("{}")
    .Project(doc => new Person(doc["Name"].ToString()))
    .ToListAsync();

foreach (var user in users)  
    Console.WriteLine(user.Name);


record Person(string? Name);

В данном случае рассматриваем коллекцию users как коллекцию документов BsonDocument. В метод Project передается делегат, который принимает объект BsonDocument и возвращает объект Person. Для создания Person вызываем его конструктор и передаем в него значение поля "Name" из каждого документа коллекции.

Project(doc => new Person(doc["Name"].ToString()))

Следует учитывать, что при проекции одного типа в другой могут быть несоответствия по типу. Если система не сможет выполнить преобразование, то приложение сгенирирует ошибку. Также мы получим исключение, если в документах вовсе не окажется поля с указанным именем.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850