sql >> Databasteknik >  >> NoSQL >> MongoDB

Mongo-aggregation inom tidsintervall

Det finns ett par sätt att närma sig detta beroende på vilket utdataformat som bäst passar dina behov. Den viktigaste anmärkningen är att med "aggregationsramverket" i sig kan du faktiskt inte returnera något "cast" som ett datum, men du kan få värden som enkelt kan rekonstrueras till ett Date objekt när du bearbetar resultat i ditt API.

Den första metoden är att använda "Date Aggregation Operators" tillgänglig för aggregeringsramverket:

db.collection.aggregate([
    { "$match": {
        "time": { "$gte": startDate, "$lt": endDate }
    }},
    { "$group": {
        "_id": {
            "year": { "$year": "$time" },
            "dayOfYear": { "$dayOfYear": "$time" },
            "hour": { "$hour": "$time" },
            "minute": {
                "$subtract": [
                    { "$minute": "$time" },
                    { "$mod": [ { "$minute": "$time" }, 10 ] }
                ]
            }
        },
        "count": { "$sum": 1 }
    }}
])

Som returnerar en sammansatt nyckel för _id som innehåller alla värden du vill ha för ett "datum". Alternativt, om bara inom en "timme" alltid, använd bara "minut"-delen och räkna ut det faktiska datumet baserat på startDate av ditt urval.

Eller så kan du bara använda vanlig "Date math" för att få millisekunderna sedan "epok" som återigen kan matas direkt till en datumkonstruktör.

db.collection.aggregate([
    { "$match": {
        "time": { "$gte": startDate, "$lt": endDate }
    }},
    { "$group": {
        "_id": {
            "$subtract": [
               { "$subtract": [ "$time", new Date(0) ] },
               { "$mod": [
                   { "$subtract": [ "$time", new Date(0) ] },
                   1000 * 60 * 10
               ]}
            ]
        },
        "count": { "$sum": 1 }
    }}
])

I alla fall vad du inte vill göra är att använda $project innan du faktiskt tillämpar $group . Som ett "pipeline stadium", $project måste "cirkulera" genom alla valda dokument och "omvandla" innehållet.

Detta tar tid , och läggs till exekveringssumman för frågan. Du kan helt enkelt ansöka till $group direkt som visats.

Eller om du verkligen är "ren" om ett Date objekt som returneras utan efterbearbetning, då kan du alltid använda "mapReduce" , eftersom JavaScript-funktionerna faktiskt tillåter omarbetning som ett datum, men långsammare än aggregeringsramverket och naturligtvis utan markörsvar:

db.collection.mapReduce(
   function() {
       var date = new Date(
           this.time.valueOf() 
           - ( this.time.valueOf() % ( 1000 * 60 * 10 ) )
       );
       emit(date,1);
   },
   function(key,values) {
       return Array.sum(values);
   },
   { "out": { "inline": 1 } }
)

Det bästa är dock att använda aggregering, eftersom det är ganska enkelt att omvandla svaret:

db.collection.aggregate([
    { "$match": {
        "time": { "$gte": startDate, "$lt": endDate }
    }},
    { "$group": {
        "_id": {
            "year": { "$year": "$time" },
            "dayOfYear": { "$dayOfYear": "$time" },
            "hour": { "$hour": "$time" },
            "minute": {
                "$subtract": [
                    { "$minute": "$time" },
                    { "$mod": [ { "$minute": "$time" }, 10 ] }
                ]
            }
        },
        "count": { "$sum": 1 }
    }}
]).forEach(function(doc) {
    doc._id = new Date(doc._id);
    printjson(doc);
})

Och sedan har du din intervallgrupperingsutdata med riktig Date objekt.



  1. Beräkna avstånd i Java med MongoDB

  2. använder sureIndex i mongodb-schemat med mongoose

  3. Problem med uppdatering av MongoDB-dokumentversion med JaVers

  4. hur man tar bort array med värdet null efter unset i mongodb