У меня есть такой объект:
public class DriverDocument : Document
{
public string? Email { get; set; }
public List<SubscriptionInfo> Subscriptions { get; set; } = new();
}
public class SubscriptionInfo
{
public string Id { get; set; } = null!;
public string PartnerId { get; set; } = null!;
}
и я бы вернул все DriverDocument
, где Subscriptions.PartnerId
= '123'.
Если я использую .Find(Builders<DriverDocument>.Filter.ElemMatch(x => x.Subscriptions, s => s.PartnerId == partnerId))
, это вернет все DriverDocument
, где ЛЮБОЙ из Subscriptions
содержит «123».
Я хочу вернуть DriverDocument
И Subscriptions
, которые содержат только соответствующие записи.
Если я сделаю что-то вроде приведенного ниже, мне придется использовать проекцию, что означает ручное сопоставление свойств. Если я добавлю новое свойство в DriverDocument
, мне придется не забыть включить его в проекцию.
.Find(Filter.ElemMatch(x => x.Subscriptions, y => y.PartnerId == partnerId))
.Project(Projection.Expression(
d => new DriverDocument
{
Id = d.Id,
Status = d.Status,
FirstName = d.FirstName,
LastName = d.LastName,
Email = d.Email,
Subscriptions = d.Subscriptions
.Where(sub => sub.PartnerId == partnerId)
.ToList()
}));
Есть ли другой способ вернуть записи DriverDocument
И Subscriptions
только с теми данными, которые мне нужны, без необходимости сопоставления свойств и без BsonDocument?
🤔 А знаете ли вы, что...
C# также используется для разработки игр с помощью Unity3D, популярного игрового движка.
Вместо использования простого поиска с проекцией вы можете запустить конвейер агрегации как минимум с двумя этапами:
$match
для фильтрации документов, имеющих подписку с соответствующим идентификатором партнера.$set
, чтобы назначить свойство Subscriptions
, чтобы список содержал только элементы с соответствующим идентификатором партнера.Для простоты вы можете использовать LINQ для настройки конвейера агрегации. Обратите внимание, что на момент написания этой статьи образец работал для драйвера MongoDB C# 2.28 и типа свойства List<SubscriptionInfo>
. Альтернативное решение см. ниже.
var pipeline = new EmptyPipelineDefinition<DriverDocument>()
.Match(x => x.Subscriptions.Any(y => y.PartnerId == "123"))
.Set(x => new DriverDocument()
{
Subscriptions = x.Subscriptions.Where(y => y.PartnerId == "123").ToList()
});
var result = await (await coll.AggregateAsync(pipeline)).ToListAsync();
Таким образом, вам не придется помнить о необходимости добавлять в оператор новые свойства.
[
{
$match: {
Subscriptions: {
$elemMatch: { PartnerId: "123" }
}
}
},
{
$set: {
Subscriptions: {
$filter: {
input: "$Subscriptions",
as: "y",
cond: { $eq: ["$$y.PartnerId", "123"] }
}
}
}
}
]
Если вы не можете выполнить обновление до версии 2.28 или тип вашего свойства отличается от List<SubscriptionInfo>
, вы также можете настроить этап $set
как BsonDocument
, например:
var partnerId = "123";
var setStage = BsonDocument.Parse("""
{
$set: {
Subscriptions: {
$filter: {
input: "$Subscriptions",
as: "y",
cond: { $eq: ["$$y.PartnerId", "%%PARTNER_ID%%"] }
}
}
}
}
""".Replace("%%PARTNER_ID%%", partnerId));
var pipeline = new EmptyPipelineDefinition<DriverDocument>()
.Match(x => x.Subscriptions.Any(y => y.PartnerId == "123"))
.AppendStage<DriverDocument, DriverDocument, DriverDocument>(setStage);
var result = await (await coll.AggregateAsync(pipeline)).ToListAsync();