MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

So verwenden Sie $regex innerhalb von $or als Aggregationsausdruck

Alles in $expr ist ein Aggregationsausdruck, und die Dokumentation darf nicht "sagen, dass Sie nicht explizit können" , aber das Fehlen eines benannten Operators und das JIRA-Problem SERVER-11947 sag das auf jeden Fall. Wenn Sie also einen regulären Ausdruck benötigen, haben Sie wirklich keine andere Möglichkeit, als zu verwenden $wo stattdessen:

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";

  }
})

Sie können immer noch $expr verwenden und Aggregationsausdrücke für eine exakte Übereinstimmung oder halten Sie den Vergleich einfach innerhalb von $wo ohnehin. Aber derzeit versteht MongoDB nur $regex innerhalb eines "query"-Ausdrucks .

Wenn Sie tatsächlich "erfordern" ein Aggregations-Pipeline-Ausdruck, der Sie daran hindert, $where , dann besteht der einzig gültige Ansatz darin, zuerst das Feld getrennt vom Array zu „projizieren“ und dann $match mit dem regulären Abfrageausdruck:

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/]
    }
  }}
])

Was uns zu der Tatsache führt, dass Sie anscheinend nach dem Element im Array mit dem maximalen Datumswert suchen. Die JavaScript-Syntax sollte deutlich machen, dass der richtige Ansatz hier stattdessen ist $sortieren das Array auf "update". Auf diese Weise kann das „erste“ Element im Array das „neueste“ sein. Und das können Sie mit einer normalen Abfrage tun.

Um die Reihenfolge beizubehalten, stellen Sie sicher, dass dem Array neue Elemente mit $push und $sort so:

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

Tatsächlich mit einem leeren Array-Argument für $each ein updateMany() wird alle Ihre bestehenden Dokumente aktualisieren:

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

Diese sollten wirklich nur notwendig sein, wenn Sie tatsächlich das während Updates gespeicherte Datum "ändern", und diese Updates werden am besten mit bulkWrite() um "sowohl" die Aktualisierung als auch die "Sortierung" des Arrays effektiv durchzuführen:

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 } } }
    }
  }}
])

Wenn Sie das Datum jedoch noch nie wirklich "verändert" haben, ist es wahrscheinlich sinnvoller, einfach den $position Modifikator und "voranstellen" an das Array statt "anhängen" und Vermeidung von Overhead eines $sort :

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

Wenn das Array dauerhaft sortiert oder zumindest so aufgebaut ist, dass das "neueste" Datum tatsächlich immer der "erste" Eintrag ist, können Sie einfach einen regulären Abfrageausdruck verwenden:

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

Die Lektion hier ist also, versuchen Sie nicht, berechnete Ausdrücke Ihrer Logik aufzuzwingen, wo Sie es wirklich nicht brauchen. Es sollte keinen zwingenden Grund geben, warum Sie den Inhalt des Arrays nicht als "gespeichert" anordnen können, um das "neueste Datum zuerst zu haben " , und selbst wenn Sie dachten, dass Sie das Array in einer anderen Reihenfolge benötigen, sollten Sie wahrscheinlich abwägen, welcher Anwendungsfall wichtiger ist.

Nach der Neucodierung können Sie sogar bis zu einem gewissen Grad einen Index nutzen, solange die regulären Ausdrücke entweder am Anfang der Zeichenfolge verankert sind oder zumindest etwas anderes im Abfrageausdruck eine genaue Übereinstimmung liefert.

Falls Sie das Gefühl haben, dass Sie das Array wirklich nicht neu anordnen können, verwenden Sie den $wo Abfrage ist Ihre einzige aktuelle Option, bis das JIRA-Problem behoben ist. Das ist hoffentlich tatsächlich für die Version 4.1, wie sie derzeit angestrebt wird, aber das sind mehr als wahrscheinlich 6 Monate bis zu einem Jahr, bestenfalls geschätzt.