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

Sådan bruger du $regex inde i $eller som et aggregeringsudtryk

Alt inde i $expr er et aggregeringsudtryk, og dokumentationen må ikke "sige du ikke eksplicit" , men manglen på en navngiven operatør og JIRA-udgaven SERVER-11947 bestemt sige det. Så hvis du har brug for et regulært udtryk, har du virkelig ingen anden mulighed end at bruge $where i stedet:

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

  }
})

Du kan stadig bruge $expr og aggregeringsudtryk for et nøjagtigt match, eller bare behold sammenligningen inden for $hvor alligevel. Men på nuværende tidspunkt er de eneste regulære udtryk, MongoDB forstår, $regex inden for et "query"-udtryk .

Hvis du faktisk "krævede" et aggregeringspipeline-udtryk, der udelukker dig fra at bruge $where , så er den eneste aktuelle gyldige tilgang først at "projektere" feltet separat fra arrayet og derefter $match med det regulære forespørgselsudtryk:

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

Hvilket leder os til det faktum, at du ser ud til at lede efter varen i arrayet med den maksimale datoværdi. JavaScript-syntaksen burde gøre det klart, at den korrekte tilgang her i stedet er at $sort arrayet på "opdatering". På den måde kan det "første" element i arrayet være det "seneste". Og det er noget, du kan gøre med en almindelig forespørgsel.

For at opretholde rækkefølgen skal du sikre dig, at nye elementer tilføjes til arrayet med $push og $sort sådan her:

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

Faktisk med et tomt array-argument til $each en updateMany() vil opdatere alle dine eksisterende dokumenter:

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

Disse burde egentlig kun være nødvendige, når du faktisk "ændrer" den dato, der er gemt under opdateringer, og disse opdateringer udstedes bedst med bulkWrite() for effektivt at udføre "både" opdateringen og "sorten" af arrayet:

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

Men hvis du aldrig faktisk "ændrede" datoen, så giver det sandsynligvis mere mening blot at bruge $position modifikator og "pre-pend" til arrayet i stedet for "appending" og undgå enhver overhead af en $sort :

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

Med arrayet permanent sorteret eller i det mindste konstrueret, så den "seneste" dato faktisk altid er den "første" post, så kan du blot bruge et regulært forespørgselsudtryk:

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

Så lektionen her er, prøv ikke at tvinge beregnede udtryk på din logik, hvor du virkelig ikke behøver det. Der burde ikke være nogen tvingende grund til, at du ikke kan bestille matrixindholdet som "gemt" til at have den "seneste dato første " , og selvom du troede, at du havde brug for arrayet i en hvilken som helst anden rækkefølge, bør du sandsynligvis afveje, hvilken brugssag der er vigtigst.

Når du først er blevet reoderet, kan du endda drage fordel af et indeks til en vis grad, så længe de regulære udtryk enten er forankret til begyndelsen af ​​strengen eller i det mindste noget andet i forespørgselsudtrykket matcher nøjagtigt.

Hvis du føler, at du virkelig ikke kan omarrangere arrayet, så $hvor forespørgsel er din eneste nuværende mulighed, indtil JIRA-problemet er løst. Hvilket forhåbentlig faktisk er til 4.1-udgivelsen, som det i øjeblikket er målet, men det er mere end sandsynligt 6 måneder til et år efter bedste skøn.




  1. NoSQL bedste praksis

  2. Redis Async API'er

  3. Opretter forbindelse til ekstern redis-server

  4. Hvordan forespørger du *korrekt* Redis fra Tornado?