MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Comment utiliser $regex dans $or comme expression d'agrégation

Tout à l'intérieur de $expr est une expression d'agrégation, et la documentation ne peut pas "dire que vous ne pouvez pas explicitement" , mais le l'absence d'opérateur nommé et le problème JIRA SERVER-11947 le dire certainement. Donc, si vous avez besoin d'une expression régulière, vous n'avez vraiment pas d'autre choix que d'utiliser $where à la place :

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

  }
})

Vous pouvez toujours utiliser $expr et des expressions d'agrégation pour une correspondance exacte, ou conservez simplement la comparaison dans $where De toute façon. Mais pour le moment, les seules expressions régulières que MongoDB comprend sont $regex dans une expression "query" .

Si vous avez effectivement "exigé" une expression de pipeline d'agrégation qui vous empêche d'utiliser $where , alors la seule approche valide actuelle consiste à d'abord "projeter" le champ séparément du tableau, puis $match avec l'expression de requête régulière :

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

Ce qui nous amène au fait que vous semblez rechercher l'élément du tableau avec la valeur de date maximale. La syntaxe JavaScript devrait indiquer clairement que l'approche correcte ici est plutôt de $sort le tableau sur "mise à jour". De cette façon, le "premier" élément du tableau peut être le "dernier". Et c'est quelque chose que vous pouvez faire avec une requête normale.

Pour maintenir l'ordre, assurez-vous que de nouveaux éléments sont ajoutés au tableau avec $push et $sort comme ceci :

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

En fait avec un argument de tableau vide à $each un updateMany() mettra à jour tous vos documents existants :

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

Celles-ci ne devraient vraiment être nécessaires que lorsque vous "modifiez" la date stockée lors des mises à jour, et ces mises à jour sont mieux publiées avec bulkWrite() pour faire effectivement "à la fois" la mise à jour et le "tri" du tableau :

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

Cependant, si vous n'avez jamais "modifié" la date, il est probablement plus logique d'utiliser simplement le $position modificateur et "pré-pendre" au tableau au lieu de "ajouter", et en évitant toute surcharge d'un $sort :

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

Avec le tableau trié en permanence ou au moins construit de sorte que la "dernière" date soit en fait toujours la "première" entrée, vous pouvez simplement utiliser une expression de requête régulière :

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

Donc, la leçon ici est de ne pas essayer de forcer des expressions calculées sur votre logique là où vous n'en avez vraiment pas besoin. Il ne devrait y avoir aucune raison impérieuse pour laquelle vous ne pouvez pas commander le contenu du tableau comme "stocké" pour avoir la "date la plus récente en premier " , et même si vous pensiez avoir besoin du tableau dans un autre ordre, vous devriez probablement déterminer quel cas d'utilisation est le plus important.

Une fois réordonné, vous pouvez même tirer parti d'un index dans une certaine mesure tant que les expressions régulières sont ancrées au début de la chaîne ou qu'au moins quelque chose d'autre dans l'expression de la requête fait une correspondance exacte.

Si vous sentez que vous ne pouvez vraiment pas réorganiser le tableau, alors le $where query est votre seule option actuelle jusqu'à ce que le problème JIRA soit résolu. Ce qui est, espérons-le, en fait pour la version 4.1 telle qu'elle est actuellement ciblée, mais c'est plus que probablement de 6 mois à un an au mieux.