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

Come usare $regex dentro $o come espressione di aggregazione

Tutto all'interno di $expr è un'espressione di aggregazione e la documentazione potrebbe non "dire che non puoi esplicitamente" , ma la mancanza di qualsiasi operatore denominato e il JIRA Issue SERVER-11947 certo dirlo. Quindi, se hai bisogno di un'espressione regolare, non hai davvero altra opzione che usare $where invece:

db.getCollection('permits').find({
  "$where": function() {
    var description = this.inspections
       .sort((a,b) => b.inspectionDate.valueOf() - a.inspectionDate.valueOf())
       .shift().description;

     return /^Found a .* at the property$/.test(description) ||
           description === "Health Inspection";

  }
})

Puoi ancora utilizzare $expr ed espressioni di aggregazione per una corrispondenza esatta, o semplicemente mantieni il confronto all'interno di $where comunque. Ma in questo momento l'unica espressione regolare che MongoDB comprende è $regex all'interno di un'espressione "query" .

Se effettivamente "richiedi" un'espressione della pipeline di aggregazione che ti impedisce di utilizzare $where , quindi l'unico approccio valido corrente consiste nel "proiettare" prima il campo separatamente dall'array e quindi $match con l'espressione di query regolare:

db.getCollection('permits').aggregate([
  { "$addFields": {
     "lastDescription": {
       "$arrayElemAt": [
         "$inspections.description",
         { "$indexOfArray": [
           "$inspections.inspectionDate",
           { "$max": "$inspections.inspectionDate" }
         ]}
       ]
     }
  }},
  { "$match": {
    "lastDescription": {
      "$in": [/^Found a .* at the property$/,/Health Inspection/]
    }
  }}
])

Il che ci porta al fatto che sembra che tu stia cercando l'elemento nell'array con il valore di data massimo. La sintassi JavaScript dovrebbe chiarire che l'approccio corretto qui è invece $sort l'array su "aggiornamento". In questo modo l'elemento "primo" nell'array può essere l'"ultimo". E questo è qualcosa che puoi fare con una normale query.

Per mantenere l'ordine, assicurati che i nuovi elementi vengano aggiunti all'array con $push e $sort così:

db.getCollection('permits').updateOne(
  { "_id": _idOfDocument },
  {
    "$push": {
      "inspections": {
        "$each": [{ /* Detail of inspection object */ }],
        "$sort": { "inspectionDate": -1 }
      }
    }
  }
)

Infatti con un argomento array vuoto per $each un updateMany() aggiornerà tutti i tuoi documenti esistenti:

db.getCollection('permits').updateMany(
  { },
  {
    "$push": {
      "inspections": {
        "$each": [],
        "$sort": { "inspectionDate": -1 }
      }
    }
  }
)

Questi in realtà dovrebbero essere necessari solo quando di fatto "alteri" la data memorizzata durante gli aggiornamenti, e questi aggiornamenti vengono rilasciati al meglio con bulkWrite() per eseguire efficacemente "sia" l'aggiornamento che "ordinare" l'array:

db.getCollection('permits').bulkWrite([
  { "updateOne": {
    "filter": { "_id": _idOfDocument, "inspections._id": indentifierForArrayElement },
    "update": {
      "$set": { "inspections.$.inspectionDate": new Date() }
    }
  }},
  { "updateOne": {
    "filter": { "_id": _idOfDocument },
    "update": {
      "$push": { "inspections": { "$each": [], "$sort": { "inspectionDate": -1 } } }
    }
  }}
])

Tuttavia, se non hai mai effettivamente "modificato" la data, probabilmente ha più senso utilizzare semplicemente il $position modificatore e "pre-pend" all'array invece di "aggiungere" ed evitare qualsiasi sovraccarico di un $sort :

db.getCollection('permits').updateOne(
  { "_id": _idOfDocument },
  { 
    "$push": { 
      "inspections": {
        "$each": [{ /* Detail of inspection object */ }],
        "$position": 0
      }
    }
  }
)

Con l'array ordinato in modo permanente o almeno costruito in modo che la data "ultima" sia in realtà sempre la "prima" voce, puoi semplicemente utilizzare un'espressione di query regolare:

db.getCollection('permits').find({
  "inspections.0.description": { 
    "$in": [/^Found a .* at the property$/,/Health Inspection/]
  }
})

Quindi la lezione qui è non cercare di forzare espressioni calcolate sulla tua logica dove non ne hai davvero bisogno. Non dovrebbero esserci ragioni convincenti per cui non puoi ordinare il contenuto dell'array come "memorizzato" per avere la "ultima data prima " e anche se pensavi di aver bisogno dell'array in qualsiasi altro ordine, probabilmente dovresti valutare quale caso d'uso è più importante.

Una volta riordinato, puoi anche sfruttare in una certa misura un indice, purché le espressioni regolari siano ancorate all'inizio della stringa o almeno qualcos'altro nell'espressione della query corrisponda esattamente.

Nel caso in cui ritieni di non poter davvero riordinare l'array, allora $where query è l'unica opzione presente fino a quando il problema JIRA non viene risolto. Che si spera sia effettivamente per la versione 4.1 come attualmente previsto, ma è più che probabile che sia più che probabile da 6 mesi a un anno nella migliore delle ipotesi.