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

Filtra i risultati in base al valore del campo di immissione dell'ultima matrice

Il chilometraggio può variare su questo e potrebbe benissimo risultare che "attualmente" il processo che stai seguendo risulta essere almeno "più adatto". Ma probabilmente possiamo fare in modo più efficiente.

Cosa potresti fare ora

A condizione che i tuoi array siano già "ordinati" tramite $sort modificatore con $push , allora probabilmente puoi farlo:

db.somedb.find(
  { 
    "partn.is_partner": true,
    "$where": function() {
      return this.partn.slice(-1)[0].is_partner == true;
    }
  },
  { "partn": { "$slice": -1 } }
)

Quindi, purché partn,is_partner è "indicizzato" questo è ancora abbastanza efficiente in quanto la condizione di query iniziale può essere soddisfatta utilizzando un indice. La parte che non può essere utilizzata è $where clausola qui che utilizza la valutazione JavaScript.

Ma cos'è quella seconda parte nel $where sta facendo semplicemente "tagliare" l'ultimo elemento dall'array e testarne il valore di is_partner proprietà per vedere se è vero. Solo se viene soddisfatta anche tale condizione, il documento viene restituito.

C'è anche il $slice operatore di proiezione. Questo fa la stessa cosa nel restituire l'ultimo elemento dall'array. Le false corrispondenze sono già filtrate, quindi questo mostra solo l'ultimo elemento dove è vero.

Combinato con l'indice come menzionato, questo dovrebbe essere abbastanza veloce dato che i documenti sono già stati selezionati e la condizione JavaScript filtra solo il resto. Nota che senza un altro campo con una condizione di query standard da corrispondere, un $where la clausola non può utilizzare un indice. Quindi cerca sempre di usare "con parsimonia" con altre condizioni di query in atto.

Cosa puoi fare in futuro

Next Up, anche se non disponibile al momento della scrittura, ma sicuramente nel prossimo futuro sarà il $slice operatore per il framework di aggregazione. Questo è attualmente nel ramo di sviluppo, ma ecco una sbirciatina a come funziona:

db.somedb.aggregate([
  { "$match": { "partn.is_partner": true } },
  { "$redact": {
    "$cond": {
      "if": { 
        "$anyElementTrue": {
          "$map": {
            "input": { "$slice": ["$partn",-1] },
            "as": "el",
            "in": "$$el.is_partner"
          }
        }
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }},
  { "$project": {
      "partn": { "$slice": [ "$partn",-1 ] }
  }}
])

Combinando quel $slice all'interno di un $redact la fase qui consente di filtrare i documenti con una condizione logica, testando il documento. In questo caso il $slice produce un singolo elemento array che viene inviato a $map per estrarre solo il singolo is_partner value (sempre come array). Poiché nella migliore delle ipotesi si tratta ancora di un array a elemento singolo, l'altro test è $anyElementTrue il che rende questo un risultato booleano singolare, adatto per $cond .

Il $redact qui decide su quel risultato se $$KEEP o $$PRUNE il documento dai risultati. Successivamente utilizziamo $slice di nuovo nel progetto per restituire solo l'ultimo elemento dell'array dopo il filtraggio.

Risulta essere più o meno esattamente ciò che fa la versione JavaScript, con l'eccezione che utilizza tutti gli operatori codificati nativi e quindi dovrebbe essere un po' più veloce dell'alternativa JavaScript.

Entrambi i moduli restituiscono il tuo primo documento come previsto:

{
    "_id" : 0,
    "partn" : [
            {
                    "date" : ISODate("2015-07-28T00:59:14.963Z"),
                    "is_partner" : true
            },
            {
                    "date" : ISODate("2015-07-28T01:00:32.771Z"),
                    "is_partner" : false
            },
            {
                    "date" : ISODate("2015-07-28T01:15:29.916Z"),
                    "is_partner" : true
            },
            {
                    "date" : ISODate("2015-08-05T13:48:07.035Z"),
                    "is_partner" : false
            },
            {
                    "date" : ISODate("2015-08-05T13:50:56.482Z"),
                    "is_partner" : true
            }
    ]
}

Il grosso problema qui con entrambi è che il tuo array deve essere già ordinato, quindi l'ultima data è prima. Senza quello, allora hai bisogno del framework di aggregazione per $sort l'array, proprio come stai facendo ora.

Non molto efficiente, ecco perché dovresti "preordinare" il tuo array e mantenere l'ordine ad ogni aggiornamento.

Come trucco pratico, questo riordinerà effettivamente tutti gli elementi dell'array in tutti i documenti di raccolta in una semplice istruzione:

db.somedb.update(
    {},
    { "$push": { 
        "partn": { "$each": [], "$sort": { "date": 1 } }
    }},
    { "multi": true }
)

Quindi, anche se non stai "spingendo" un nuovo elemento in un array e stai semplicemente aggiornando una proprietà, puoi sempre applicare quel costrutto di base per mantenere l'array ordinato come lo desideri.

Vale la pena considerare perché dovrebbe rendere le cose molto più veloci.