Заявката за преброяване на „уникалните“ появявания в рамките на "EndpointId"
на всеки от "Uid"
в "Tags"
и "Type"
в "Sensors"
ще бъде:
db.collection.aggregate([
{ "$unwind": "$Tags" },
{ "$unwind": "$Tags.Sensors" },
{ "$group": {
"_id": {
"EndpointId": "$EndpointId",
"Uid": "$Tags.Uid",
"Type": "$Tags.Sensors.Type"
},
}},
{ "$group": {
"_id": {
"EndpointId": "$_id.EndpointId",
"Uid": "$_id.Uid",
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.EndpointId",
"tagCount": { "$sum": 1 },
"sensorCount": { "$sum": "$count" }
}}
])
Или за C#
var results = collection.AsQueryable()
.SelectMany(p => p.Tags, (p, tag) => new
{
EndpointId = p.EndpointId,
Uid = tag.Uid,
Sensors = tag.Sensors
}
)
.SelectMany(p => p.Sensors, (p, sensor) => new
{
EndpointId = p.EndpointId,
Uid = p.Uid,
Type = sensor.Type
}
)
.GroupBy(p => new { EndpointId = p.EndpointId, Uid = p.Uid, Type = p.Type })
.GroupBy(p => new { EndpointId = p.Key.EndpointId, Uid = p.Key.Uid },
(k, s) => new { Key = k, count = s.Count() }
)
.GroupBy(p => p.Key.EndpointId,
(k, s) => new
{
EndpointId = k,
tagCount = s.Count(),
sensorCount = s.Sum(x => x.count)
}
);
Кое извежда:
{
"EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83",
"tagCount" : 4,
"sensorCount" : 16
}
Макар че всъщност „най-ефективният“ начин да направите това, като се има предвид, че представените документи имат уникални стойности за "Uid"
така или иначе би било $reduce
сумите в самите документи:
db.collection.aggregate([
{ "$group": {
"_id": "$EndpointId",
"tags": {
"$sum": {
"$size": { "$setUnion": ["$Tags.Uid",[]] }
}
},
"sensors": {
"$sum": {
"$sum": {
"$map": {
"input": { "$setUnion": ["$Tags.Uid",[]] },
"as": "tag",
"in": {
"$size": {
"$reduce": {
"input": {
"$filter": {
"input": {
"$map": {
"input": "$Tags",
"in": {
"Uid": "$$this.Uid",
"Type": "$$this.Sensors.Type"
}
}
},
"cond": { "$eq": [ "$$this.Uid", "$$tag" ] }
}
},
"initialValue": [],
"in": { "$setUnion": [ "$$value", "$$this.Type" ] }
}
}
}
}
}
}
}
}}
])
Изявлението обаче не се съпоставя добре с LINQ, така че ще трябва да използвате BsonDocument
интерфейс за изграждане на BSON за израза. И разбира се къде е същият "Uid"
стойностите "did" всъщност се срещат в множество документи в колекцията, тогава $unwind
изразите са необходими, за да ги „групирате“ заедно в документи от записите в масива.
Оригинал
Решавате това, като получите $size
на масивите. За външния масив това просто се прилага към пътя на полето на масива в документа, а за елементите на вътрешния масив трябва да обработите с $map
за да обработите всеки "Tags"
елемент и след това получете $size
на "Sensors"
и $sum
полученият масив да се намали до общия брой.
На документ, който би бил:
db.collection.aggregate([
{ "$project": {
"tags": { "$size": "$Tags" },
"sensors": {
"$sum": {
"$map": {
"input": "$Tags",
"in": { "$size": "$$this.Sensors" }
}
}
}
}}
])
Което къде сте присвоили на класове във вашия C# код ще бъде като:
collection.AsQueryable()
.Select(p => new
{
tags = p.Tags.Count(),
sensors = p.Tags.Select(x => x.Sensors.Count()).Sum()
}
);
Къде се връщат:
{ "tags" : 3, "sensors" : 13 }
{ "tags" : 2, "sensors" : 8 }
Къде искате да $group
резултатите, като например върху цялата колекция, тогава бихте направили:
db.collection.aggregate([
/* The shell would use $match for "query" conditions */
//{ "$match": { "EndpointId": "89799bcc-e86f-4c8a-b340-8b5ed53caf83" } },
{ "$group": {
"_id": null,
"tags": { "$sum": { "$size": "$Tags" } },
"sensors": {
"$sum": {
"$sum": {
"$map": {
"input": "$Tags",
"in": { "$size": "$$this.Sensors" }
}
}
}
}
}}
])
Което за вашия C# код, както преди, би било:
collection.AsQueryable()
.GroupBy(p => "", (k,s) => new
{
tags = s.Sum(p => p.Tags.Count()),
sensors = s.Sum(p => p.Tags.Select(x => x.Sensors.Count()).Sum())
}
);
Къде се връщат:
{ "tags" : 5, "sensors" : 21 }
И за "EndpointId
, тогава просто използвате това поле като ключ за групиране, вместо null
или 0
тъй като се прилага от съпоставянето на C# драйвера:
collection.AsQueryable()
/* Use the Where if you want a query to match only those documents */
//.Where(p => p.EndpointId == "89799bcc-e86f-4c8a-b340-8b5ed53caf83")
.GroupBy(p => p.EndpointId, (k,s) => new
{
tags = s.Sum(p => p.Tags.Count()),
sensors = s.Sum(p => p.Tags.Select(x => x.Sensors.Count()).Sum())
}
);
Което, разбира се, е същата сума от двата образеца на документа, които ни предоставихте:
{ "tags" : 5, "sensors" : 21 }
Така че това са много прости резултати, с просто конвейерно изпълнение, след като свикнете със синтаксиса.
Предлагам да се запознаете с операторите за агрегиране от основната документация и разбира се "LINQ Cheat Sheet" на изрази и картографиране на тяхното използване от хранилището на кода на C# Driver.
Вижте също общия Справочник за LINQ в справочника за C# драйвер за други примери за това как това се преобразува в рамката за агрегиране на MongoDB като цяло.