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

ordina per valore oggetto incorporato in Mongodb

Se è necessario calcolare qualcosa del genere in fase di esecuzione, con il contenuto "filtrato" dall'array che determina l'ordinamento, è meglio fare qualcosa con .aggregate() per rimodellare e determinare un valore di ordinamento come questo:

db.collection.aggregate([
    // Pre-filter the array elements
    { "$project": {
        "tags": 1,
        "score": {
            "$setDifference": [
                { "$map": {
                    "input": "$tags",
                    "as": "tag",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el.id", "t1" ] },
                            "$$el.score",
                            false
                        ]
                    }
                }},
                [false]
            ]
        }
    }},
    // Unwind to denormalize
    { "$unwind": "$score" },
    // Group back the "max" score
    { "$group": {
        "_id": "$_id",
        "tags": { "$first": "$tags" },
        "score": { "$max": "$score" }
    }},
    // Sort descending by score
    { "$sort": { "score": -1 } }
])

Dove la prima parte della pipeline viene utilizzata per "prefiltrare" il contenuto dell'array (oltre a mantenere il campo originale) solo per quei valori di "score" in cui l'id è uguale a "t1". Questo viene fatto elaborando $map che applica una condizione a ciascun elemento tramite $cond per determinare se restituire il "punteggio" per quell'elemento o false .

Il $setDifference l'operazione esegue un confronto con un singolo elemento array [false] che rimuove efficacemente qualsiasi false valori restituiti da $map . Come "set", rimuove anche le voci duplicate, ma ai fini dell'ordinamento qui è una buona cosa.

Con l'array ridotto e rimodellato ai valori, elabori $unwind pronti per la fase successiva per affrontare i valori come elementi individuali. Il $group la fase si applica essenzialmente $max sul "punteggio" per restituire il valore più alto contenuto nei risultati filtrati.

Quindi si tratta solo di applicare il $sort sul valore determinato per ordinare i documenti. Naturalmente se volevi il contrario, usa $min e ordina invece in ordine crescente.

Ovviamente aggiungi un $match dalla fase all'inizio se tutto ciò che vuoi veramente sono documenti che contengono effettivamente valori "t1" per id all'interno dei tag. Ma quella parte è meno rilevante per l'ordinamento dei risultati filtrati che desideri ottenere.

L'alternativa al calcolo è fare tutto mentre scrivi le voci nell'array nei documenti. Un po' disordinato, ma è più o meno così:

db.collection.update(
    { "_id": docId },
    {
        "$push": { "tags": { "id": "t1", "score": 60 } },
        "$max": { "maxt1score": 60 },
        "$min": { "mint1score": 60 }
    }
)

Qui il $max l'operatore di aggiornamento imposta il valore per il campo specificato solo se il nuovo valore è maggiore del valore esistente o se non esiste ancora alcuna proprietà. Il caso inverso è vero per $min , dove solo se minore di sarà sostituito con il nuovo valore.

Ciò avrebbe ovviamente l'effetto di aggiungere varie proprietà aggiuntive ai documenti, ma il risultato finale è che l'ordinamento è notevolmente semplificato:

db.collection.find().sort({ "maxt1score": -1 })

E funzionerà molto più velocemente rispetto al calcolo con una pipeline di aggregazione.

Quindi considera i principi di progettazione. Dati strutturati in matrici in cui si desidera ottenere risultati filtrati e accoppiati per l'ordinamento significa calcolare in fase di esecuzione per determinare su quale valore eseguire l'ordinamento. Aggiunta di ulteriori proprietà al documento su .update() significa che puoi semplicemente fare riferimento a tali proprietà per ordinare direttamente i risultati.