MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Aggregato Mongodb su documento secondario nell'array

MapReduce è lento, ma può gestire set di dati molto grandi. Il framework Aggregation, invece, è un po' più veloce, ma avrà problemi con grandi volumi di dati.

Il problema con la struttura mostrata è che è necessario "$ svolgere" gli array per aprire i dati. Ciò significa creare un nuovo documento per ogni elemento dell'array e con il framework di aggregazione necessario per farlo in memoria. Quindi, se hai 1000 documenti con 100 elementi dell'array, dovrai creare un flusso di 100.000 documenti per raggrupparli e contarli.

Potresti considerare di vedere se esiste un layout dello schema che servirà meglio le tue query, ma se vuoi farlo con il framework Aggregation ecco come potresti farlo (con alcuni dati di esempio in modo che l'intero script cadrà nella shell);

db.so.remove();
db.so.ensureIndex({ "items.sku": 1}, {unique:false});
db.so.insert([
    {
        _id: 42,
        last_modified: ISODate("2012-03-09T20:55:36Z"),
        status: 'active',
        items: [
            { sku: '00e8da9b', qty: 1, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
    ]
    },
    {
        _id: 43,
        last_modified: ISODate("2012-03-09T20:55:36Z"),
        status: 'active',
        items: [
            { sku: '00e8da9b', qty: 1, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
        ]
    },
]);


db.so.runCommand("aggregate", {
    pipeline: [
        {   // optional filter to exclude inactive elements - can be removed    
            // you'll want an index on this if you use it too
            $match: { status: "active" }
        },
        // unwind creates a doc for every array element
        { $unwind: "$items" },
        {
            $group: {
                // group by unique SKU, but you only wanted to count a SKU once per doc id
                _id: { _id: "$_id", sku: "$items.sku" },
            }
        },
        {
            $group: {
                // group by unique SKU, and count them
                _id: { sku:"$_id.sku" },
                doc_count: { $sum: 1 },
            }
        }
    ]
    //,explain:true
})

Nota che ho $group'd due volte, perché hai detto che uno SKU può contare solo una volta per documento, quindi dobbiamo prima ordinare le coppie doc/sku univoche e poi contarle.

Se vuoi che l'output sia leggermente diverso (in altre parole, ESATTAMENTE come nel tuo campione) possiamo $proiettarli.