De query om de "unieke" voorvallen te tellen binnen een "EndpointId"
van elk van de "Uid"
in "Tags"
en de "Type"
in "Sensors"
zou zijn:
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" }
}}
])
Of voor 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)
}
);
Welke uitgangen:
{
"EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83",
"tagCount" : 4,
"sensorCount" : 16
}
Hoewel dit eigenlijk de "meest efficiënte" manier is om dit te doen, aangezien de gepresenteerde documenten unieke waarden hebben voor "Uid"
hoe dan ook zou zijn om $reduce
de bedragen in de documenten zelf:
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" ] }
}
}
}
}
}
}
}
}}
])
De instructie komt echter niet echt goed overeen met LINQ, dus u zou de BsonDocument
moeten gebruiken interface om de BSON voor de instructie te bouwen. En natuurlijk waar dezelfde "Uid"
waarden "deden" in feite voorkomen in meerdere documenten in de verzameling, dan is de $unwind
statements zijn nodig om deze te "groeperen" over documenten vanuit de array-items.
Origineel
U lost dit op door de $size
te verkrijgen
van de arrays. Voor de buitenste array is dit gewoon van toepassing op het veldpad van de array in het document, en voor de binnenste array-items moet je verwerken met $map
om elke "Tags"
. te verwerken element en verkrijg vervolgens de $size
van "Sensors"
en $sum
de resulterende array te reduceren tot de totale telling.
Per document zou dat zijn:
db.collection.aggregate([
{ "$project": {
"tags": { "$size": "$Tags" },
"sensors": {
"$sum": {
"$map": {
"input": "$Tags",
"in": { "$size": "$$this.Sensors" }
}
}
}
}}
])
Waar je klassen in je C#-code aan hebt toegewezen, zou er als volgt uitzien:
collection.AsQueryable()
.Select(p => new
{
tags = p.Tags.Count(),
sensors = p.Tags.Select(x => x.Sensors.Count()).Sum()
}
);
Waar die terugkeren:
{ "tags" : 3, "sensors" : 13 }
{ "tags" : 2, "sensors" : 8 }
Waar je naartoe wilt $group
de resultaten, zoals bijvoorbeeld over de hele collectie, dan zou je het volgende doen:
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" }
}
}
}
}
}}
])
Wat voor uw C#-code zoals voorheen zou zijn:
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())
}
);
Waar die terugkeren:
{ "tags" : 5, "sensors" : 21 }
En voor "EndpointId
, dan gebruik je dat veld gewoon als de groeperingssleutel, in plaats van de null
of 0
zoals het wordt toegepast door de C#-stuurprogrammatoewijzing:
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())
}
);
Wat natuurlijk dezelfde som is van de twee documentvoorbeelden die u ons gaf:
{ "tags" : 5, "sensors" : 21 }
Dit zijn dus heel eenvoudige resultaten, met eenvoudige pijplijnuitvoering als je eenmaal aan de syntaxis gewend bent.
Ik raad aan om vertrouwd te raken met de Aggregatie-operators uit de kerndocumentatie, en natuurlijk de "LINQ Cheatsheet" van uitdrukkingen en hun gebruikstoewijzing vanuit de C# Driver-coderepository.
Zie ook de algemene LINQ Reference in de C# Driver-referentie voor andere voorbeelden van hoe dit in het algemeen op het Aggregation Framework van MongoDB wordt weergegeven.