sql >> Database >  >> NoSQL >> MongoDB

Mongodb-aggregaat, hoe documenten te tellen op intervalcriteria?

Wat je wel wilt is de $cond operator en een flink aantal geneste voorwaarden met $and . Maar dit zou je precies moeten geven wat je wilt.

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

Omdat je tijd heel is milliseconden kun je zien waarom ik om de bewerking heb gevraagd.

Dus als $cond is een ternair operator, er zijn drie argumenten nodig, namelijk:

  • Een voorwaarde om te evalueren die een boolean returns retourneert
  • Een retourwaarde waarbij de voorwaarde waar is
  • Een retourwaarde waarbij de voorwaarde false is

Daarom is het idee dat je nest de voorwaarden overal, ga naar de volgende test op false totdat je een voorwaarde hebt gevonden die overeenkomt en een waarde die moet worden geretourneerd.

De $en deel is een reeks voorwaarden op te nemen. Dit geeft je de bereiken . Dus in de langste delen:

          {"$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

Als je door je heen loopt, blijft er "Snel" staan ​​voor times minder dan 500 milliseconden.

Elk van deze keys wordt verzonden naar de groep en we { $sum: 1 } om een ​​telling te krijgen als ze zijn gegroepeerd.

Als je dat nodig hebt in je eigen taalimplementatie, de hele pipeline inhoud binnen

is gewoon JSON, dus je kunt dat parseren in je eigen gegevensstructuur als handmatig vertalen je niet lukt, of als je net als ik gewoon lui bent.

BEWERKEN

Vanwege de opmerkingen het lijkt nodig om de vorm . uit te leggen van de gepresenteerde vraag. Dus hier het edit-addendum ter verduidelijking.

Wanneer leren gebruik van de aggregatiepijplijn, en inderdaad goede praktijken voor uitschrijven en testen een complexe reeks van fasen of logica, vind ik het nuttig om visualiseren de resultaten door onderdelen stap voor stap te implementeren . Dus in het geval dat ik zoiets schrijf, mijn eerste stap zou als volgt zijn:

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

Dat zou me de telling van "Langzaamste" geven zoals ik zou verwachten en dan emmer al het andere in null . Er is dus een fase waarin ik de resultaten tot nu toe zie. Maar wanneer testen Ik zou eigenlijk zoiets doen voordat ik verder ga met het opbouwen van een ketting:

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

Dus ik krijg alleen de resultaten voor "Slow" (tussen 2000 en 1000) met al het andere in de null emmer. Dus mijn totale telling blijft hetzelfde.

In de finale query, zoals werd opgemerkt, in een ternair voorwaarde die zo genest is, de eerste stage heeft al geëvalueerd false voor de items die worden getest door de volgende exploitant. Dit betekent dat ze niet . zijn groter dan de waarde die al werd getest in de eerste stadium, en dat maakt het niet nodig om op die aandoening te testen, dus dit zou als volgt worden geschreven:

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

En dat kortsluitingen de evaluatie omdat er geen echte . is moet testen op dingen die niet doorkomen naar de volgende logische voorwaarde.

Dus puur om visuele redenen en voor pure luiheid van knippen en plakken logica, we eindigen met de uitgebreide vorm met behulp van de $en voorwaarde om te inpakken het bereik. Maar voor degenen die niet gewend zijn het gebruik van de ternair vorm is er een duidelijke visuele aanwijzing dat de resultaten die in deze fase worden vergeleken, vallen tussen de waarden van 2000ms en 1000ms , enzovoort, wat u wilt als resultaat in elk bereik.

Zoals ik al zei, onnodig om te hebben vanwege hoe de logica werkt, maar het was was een ontwikkelingsfase, en is duidelijk aan de mensen die hun hoofd nog moeten begrijpen gebruik van de ternair vorm dat $cond biedt.




  1. Hoe het resultaat aggregeren en samenvoegen tot een verzameling?

  2. Hoe kan ik een unieke id met twee kolommen toevoegen aan de mongodb in een meteor-app?

  3. Mongoose:verzameling wordt niet ingevuld bij gebruik als ref in een ander document

  4. Zijn mongodb-subdocumenten gelijk aan Firestore-subcollecties?