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

Agrégat Mongodb, Comment compter les documents par critères d'intervalle ?

Ce que vous voulez, c'est le $cond opérateur et quelques conditions imbriquées avec $and . Mais cela devrait vous donner exactement ce que vous voulez.

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$gte": ["$LoadTime", 2000] },
          "Slowest",                                   // return "Slowest" where true
          {"$cond": [
              {"$and": [
                  {"$lt": ["$LoadTime", 2000] },
                  {"$gte": ["$LoadTime", 1000] }
              ]},
              "Slow",                                  // then "Slow" here where true
              {"$cond": [
                  {"$and": [
                      {"$lt": ["$LoadTime", 1000] },
                      {"$gte": ["$LoadTime", 500 ] }
                  ]},
                  "Medium",                            // then "Medium" where true
                  "Fast"                               // and finally "Fast" < 500
              ]}
          ]}
      ]},
      "count": {"$sum": 1}
    }},
    {"$sort": { "count": 1 }}
])

Comme votre temps est entier millisecondes, vous pouvez voir pourquoi j'ai demandé la modification.

Donc, comme $cond est un ternaire opérateur, il prend trois arguments étant :

  • Une condition à évaluer qui renvoie un booléen
  • Une valeur de retour où la condition est vraie
  • Une valeur de retour où la condition est fausse

Par conséquent, l'idée est que vous imbriquez les conditions tout au long, en passant au suivant test sur faux jusqu'à ce que vous ayez trouvé une condition à faire correspondre et une valeur à renvoyer.

Le $et part est un tableau de conditions inclure. Cela vous donne les gammes . Donc dans les parties les plus longues :

          {"$cond": [                             // Evaluate here
              {"$and": [                          // Within the range of the next 2
                  {"$lt": ["$LoadTime", 2000] },
                  {"$gte": ["$LoadTime", 1000] }
              ]},
              "Slow",                            // true condition - return
              {"$cond": [                        // false - move to next eval

En cascade, vous vous retrouvez avec "Rapide" pendant times moins de 500 millisecondes.

Chacune de ces keys est émis au groupe et on se contente de { $sum: 1 } pour obtenir un décompte au fur et à mesure qu'ils sont regroupés.

Si vous en avez besoin dans votre propre implémentation de langage, l'ensemble du pipeline contenu dans

est juste JSON, vous pouvez donc l'analyser dans votre structure de données native si la traduction à la main vous échappe ou si, comme moi, vous êtes juste paresseux.

MODIFIER

En raison des commentaires il semble nécessaire d'expliquer la forme de la requête présentée. Voici donc l'addendum de modification pour clarification.

Quand apprendre utilisation du pipeline d'agrégation, et bien sûr bonne pratique pour écrire et tester une suite complexe d'étapes ou de logique, je trouve qu'il est utile de visualiser les résultats en implémentant les parties une étape à la fois . Donc, dans le cas d'écrire une telle chose, mon premier l'étape serait la suivante :

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$gte": ["$LoadTime", 2000] },
          "Slowest",
          null
       ]}
    }}
])

Maintenant, cela me donnerait le nombre de "le plus lent" comme je m'y attendais, puis seau tout le reste en null . Il y a donc une étape où je vois les résultats jusqu'à présent. Mais lors du test En fait, je ferais quelque chose comme ça avant de passer à la construction d'une chaîne :

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$and": [
              {"$lt": ["$LoadTime", 2000] },
              {"$gte": ["$LoadTime", 1000] }
          ]},
          "Slow",
          null
      ]}
    }}
])

Donc, je reçois juste les résultats pour "Lent" (entre 2000 et 1000) avec tout le reste dans le null seau. Mon décompte global reste donc le même.

En finale requête, comme cela a été souligné, dans un ternaire condition imbriquée comme celle-ci, la première l'étape a déjà évalué false pour les éléments testés par le prochain opérateur. Cela signifie qu'ils ne sont pas supérieur à la valeur qui a déjà été testée dans le premier étape, et cela évite d'avoir à tester cette condition, donc cela pourrait s'écrire comme suit :

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$gte": ["$LoadTime", 2000] },       // Caught everything over 2000
          "Slowest",
          {"$cond": [
              {"$gte": ["$LoadTime", 1000] }    // Catch things still over 1000
              "Slow",
              {"$cond": [                       // Things under 1000 go here

              // and so on

Et que les courts-circuits l'évaluation car il n'y a pas de réel besoin de tester les choses qui ne passeront pas par la condition logique suivante.

Donc purement pour des raisons visuelles et pour la pure paresse du couper-coller logique, nous nous retrouvons avec la forme développée en utilisant le $et condition pour envelopper la gamme. Mais pour ceux qui n'ont pas l'habitude l'utilisation du ternaire forme il y a un repère visuel clair que les résultats mis en correspondance dans cette phase se situeront entre les valeurs de 2000ms et 1000ms , et ainsi de suite, qui est ce que vous voulez comme résultat dans chaque plage.

Comme je l'ai dit, inutile d'avoir à cause de la façon dont la logique fonctionne, mais c'était l'était une phase de développement, et est clair aux personnes qui n'ont pas encore perçu utilisation du ternaire formez ce $cond fournit.