Lösung
Die folgende Pipeline sollte Ihnen das gewünschte Ergebnis
db.getCollection('account').aggregate(
[
{
$project: {
_id: '$product',
fields: [
{ name: { $literal: 'price' }, value: '$price', count: { $literal: 0 } },
{ name: { $literal: 'sale' }, value: '$sale', count: { $literal: 0 } },
{ name: { $literal: 'count' }, value: { $literal: 0 }, count: '$count' }
]
}
},
{
$unwind: {
path: '$fields'
}
},
{
$match: {
'fields.value': {
$exists: true
}
}
},
{
$group: {
_id: {
product: '$_id',
field: '$fields.name'
},
value: {
$last: '$fields.value'
},
count: {
$sum: '$fields.count'
}
}
},
{
$project: {
_id: '$_id.product',
price: {
$cond: { if: { $eq: [ '$_id.field', 'price' ] }, then: '$value', else: null }
},
sale: {
$cond: { if: { $eq: [ '$_id.field', 'sale' ] }, then: '$value', else: null }
},
count: {
$cond: { if: { $eq: [ '$_id.field', 'count' ] }, then: '$count', else: 0 }
}
}
},
{
$group: {
_id: '$_id',
price: {
$max: '$price'
},
sale: {
$max: '$sale'
},
count: {
$sum: '$count'
}
}
}
])
Erklärung
Es erstellt zuerst ein neues Array mit Elementen pro Feld, das Feldname, Feldwert und Zählwert enthält. Beachten Sie, dass das Feld count
so behandelt wird, wie es akkumuliert werden sollte, anstatt den letzten Wert davon zu erhalten. Also nach der ersten Stufe aussehen Dokumente wie folgt aus:
/* 1 */
{
"_id" : "2",
"fields" : [
{
"name" : "price",
"value" : 5400,
"count" : 0.0
},
{
"name" : "sale",
"count" : 0.0
},
{
"name" : "count",
"value" : 0.0,
"count" : 1
}
]
}
/* 2 */
{
"_id" : "2",
"fields" : [
{
"name" : "price",
"count" : 0.0
},
{
"name" : "sale",
"value" : 0.2,
"count" : 0.0
},
{
"name" : "count",
"value" : 0.0,
"count" : 0
}
]
}
/* 3 */
{
"_id" : "2",
"fields" : [
{
"name" : "price",
"count" : 0.0
},
{
"name" : "sale",
"count" : 0.0
},
{
"name" : "count",
"value" : 0.0,
"count" : 1
}
]
}
Es abwickelt dann das Array und filtert sie von Nullwerte, um loszuwerden, so dass nach der Stufe 2 & 3 Dokumente wie folgt aussehen:
/* 1 */
{
"_id" : "2",
"fields" : {
"name" : "price",
"value" : 5400,
"count" : 0.0
}
}
/* 2 */
{
"_id" : "2",
"fields" : {
"name" : "count",
"value" : 0.0,
"count" : 1
}
}
/* 3 */
{
"_id" : "2",
"fields" : {
"name" : "sale",
"value" : 0.2,
"count" : 0.0
}
}
/* 4 */
{
"_id" : "2",
"fields" : {
"name" : "count",
"value" : 0.0,
"count" : 0
}
}
/* 5 */
{
"_id" : "2",
"fields" : {
"name" : "count",
"value" : 0.0,
"count" : 1
}
}
In der vierten Stufe werden die letzten Werte der Felder und die Summe von count
gebildet. Ergebnis sieht wie folgt aus:
/* 1 */
{
"_id" : {
"product" : "2",
"field" : "sale"
},
"value" : 0.2,
"count" : 0.0
}
/* 2 */
{
"_id" : {
"product" : "2",
"field" : "count"
},
"value" : 0.0,
"count" : 2
}
/* 3 */
{
"_id" : {
"product" : "2",
"field" : "price"
},
"value" : 5400,
"count" : 0.0
}
Da die Werte sind nun in separaten Dokumenten mit einer anderen Form als unsere gewünschte Ergebnis sollte sein, müssen wir sie wieder in etwas projizieren wir können schließlich Gruppe. So in nach der fünften Stufe Dokumente sind wie folgt:
/* 1 */
{
"_id" : "2",
"count" : 0.0,
"price" : null,
"sale" : 0.2
}
/* 2 */
{
"_id" : "2",
"count" : 2,
"price" : null,
"sale" : null
}
/* 3 */
{
"_id" : "2",
"count" : 0.0,
"price" : 5400,
"sale" : null
}
Die letzte Stufe dann aggregiert nur diese Dokumente pro Produkt.
Ich denke, das ist eine Aufgabe, die das Lesen der Daten in die Sprache Ihrer Wahl erfordert. Jede Daten-Frame-Bibliothek (wie 'Pandas' in Python) würde dies trivial behandeln –