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

limitare e ordinare ogni gruppo in mongoDB usando l'aggregazione

L'opzione migliore qui è eseguire query separate per ogni "Paese" (idealmente in parallelo) e restituire i risultati combinati. Le query sono abbastanza semplici e restituiscono solo i primi 2 valori dopo aver applicato un ordinamento al valore di valutazione e verranno eseguite abbastanza rapidamente anche se è necessario eseguire più query per ottenere il risultato completo.

Il quadro di aggregazione non è adatto a questo, ora e anche nel prossimo futuro. Il problema è che non esiste un tale operatore che "limiti" il risultato di qualsiasi raggruppamento in alcun modo. Quindi, per fare ciò, devi fondamentalmente $push tutto il contenuto in un array ed estrai i valori "top n" da quello.

Le attuali operazioni necessarie per farlo sono piuttosto orribili e il problema principale è che è probabile che i risultati superino il limite BSON di 16 MB per documento sulla maggior parte delle origini dati reali.

Inoltre c'è un n complessità a questo a causa di come dovresti farlo in questo momento. Ma solo per dimostrare con 2 articoli:

db.collection.aggregate([
    // Sort content by country and rating
    { "$sort": { "Country": 1, "rating": -1 } },

    // Group by country and push all items, keeping first result
    { "$group": {
        "_id": "$Country",
        "results": {
            "$push": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        },
        "first": { 
            "$first": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        }
    }},

    // Unwind the array
    { "$unwind": "results" },

    // Remove the seen result from the array
    { "$redact": {
        "$cond": {
            "if": { "$eq": [ "$results.id", "$first.id" ] },
            "then": "$$PRUNE",
            "else": "$$KEEP"
        }
    }},

    // Group to return the second result which is now first on stack
    { "$group": {
        "_id": "$_id",
        "first": { "$first": "$first" },
        "second": { 
            "$first": {
                "name": "$results.name", 
                "rating": "$results.rating",
                "id": "$results.id"
            }
        }
    }},

    // Optionally put these in an array format
    { "$project": {
        "results": { 
            "$map": {
                "input": ["A","B"],
                "as": "el",
                "in": {
                    "$cond": {
                        "if": { "$eq": [ "$$el", "A" ] },
                        "then": "$first",
                        "else": "$second"
                    }
                }
            }
        }
    }}
])

Questo ottiene il risultato ma non è un ottimo approccio e diventa molto più complesso con iterazioni per limiti più alti o anche dove i raggruppamenti hanno probabilmente meno di n risultati da restituire in alcuni casi.

L'attuale serie di sviluppo ( 3.1.x ) al momento della stesura ha un $slice operatore che lo rende un po' più semplice, ma ha ancora la stessa trappola "dimensione":

db.collection.aggregate([
    // Sort content by country and rating
    { "$sort": { "Country": 1, "rating": -1 } },

    // Group by country and push all items, keeping first result
    { "$group": {
        "_id": "$Country",
        "results": {
            "$push": {
                "name": "$name", 
                "rating": "$rating",
                "id": "$id"
            }
        }
    }},
    { "$project": {
        "results": { "$slice": [ "$results", 2 ] }
    }}
])

Ma fondamentalmente fino a quando il framework di aggregazione non ha un modo per "limitare" il numero di elementi prodotti da $push o un operatore di "limite" di raggruppamento simile, allora il framework di aggregazione non è proprio la soluzione ottimale per questo tipo di problema.

Query semplici come questa:

db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)

Esegui per ogni paese distinto e idealmente in parallelo l'elaborazione per ciclo di eventi di thread con un risultato combinato produce l'approccio più ottimale in questo momento. Prelevano solo ciò che è necessario, che è il grosso problema che il framework di aggregazione non è ancora in grado di gestire in tale raggruppamento.

Quindi cerca il supporto per eseguire questi "risultati di query combinati" nel modo più ottimale per il linguaggio scelto, poiché sarà molto meno complesso e molto più performante rispetto a lanciarlo nel framework di aggregazione.