sql >> Databasteknik >  >> NoSQL >> MongoDB

Hur man använder $regex inuti $eller som ett aggregationsuttryck

Allt inuti $expr är ett aggregeringsuttryck och dokumentationen får inte "säga att du inte uttryckligen kan" , men avsaknaden av någon namngiven operatör och JIRA issue SERVER-11947 säkert säga det. Så om du behöver ett reguljärt uttryck har du verkligen inget annat alternativ än att använda $where istället:

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 fortfarande använda $expr och aggregeringsuttryck för en exakt matchning, eller bara håll jämförelsen inom $where i alla fall. Men för närvarande är de enda reguljära uttryck som MongoDB förstår $regex inom ett "query"-uttryck .

Om du faktiskt "krävde" ett aggregeringspipelineuttryck som hindrar dig från att använda $where , då är den enda giltiga metoden att först "projicera" fältet separat från arrayen och sedan $match med det vanliga frågeuttrycket:

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

Vilket leder oss till det faktum att du verkar leta efter objektet i arrayen med det maximala datumvärdet. JavaScript-syntaxen borde göra det tydligt att det korrekta tillvägagångssättet här istället är att $sort arrayen på "uppdatering". På så sätt kan det "första" objektet i arrayen vara det "senaste". Och det här är något du kan göra med en vanlig fråga.

För att behålla ordningen, se till att nya objekt läggs till i arrayen med $push och $sort så här:

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

Faktiskt med ett tomt array-argument till $each en updateMany() kommer att uppdatera alla dina befintliga dokument:

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

Dessa borde egentligen bara vara nödvändiga när du faktiskt "ändrar" datumet som lagras under uppdateringar, och dessa uppdateringar ges bäst med bulkWrite() för att effektivt göra "både" uppdateringen och "sorten" av arrayen:

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 om du aldrig faktiskt "ändrade" datumet, är det förmodligen mer meningsfullt att helt enkelt använda $position modifierare och "pre-pend" till arrayen istället för att "lägga till" och undvika all overhead av en $sort :

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

Med arrayen permanent sorterad eller åtminstone konstruerad så att det "senaste" datumet faktiskt alltid är den "första" posten, då kan du helt enkelt använda ett vanligt frågeuttryck:

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

Så lektionen här är att inte försöka tvinga fram beräknade uttryck på din logik där du verkligen inte behöver. Det bör inte finnas någon övertygande anledning till varför du inte kan beställa arrayinnehållet som "lagrat" för att ha det "senaste datumet första " , och även om du trodde att du behövde arrayen i någon annan ordning bör du förmodligen väga upp vilket användningsfall som är viktigast.

När du har omoderat kan du till och med dra nytta av ett index i viss utsträckning så länge som de reguljära uttrycken antingen är förankrade i början av strängen eller åtminstone något annat i frågeuttrycket matchar exakt.

Om du känner att du verkligen inte kan ordna om arrayen, då $var fråga är ditt enda nuvarande alternativ tills JIRA-problemet löser sig. Vilket förhoppningsvis faktiskt är för 4.1-utgåvan som för närvarande målet, men det är mer än troligt 6 månader till ett år vid bästa uppskattning.




  1. MongoDB:vad är skillnaden mellan $elemMatch och $och att hitta objekt i arrayen?

  2. java - MongoDB + Solr föreställningar

  3. Sorterat set med fast storlek i Redis?

  4. Hur gör man ett frågedatum i mongodb med pymongo?