MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Група драйвери на MongoDB .NET по времеви диапазон

Ако търсите "точното нещо" като посочената публикация за .NET, тогава вероятно всъщност няма да бъде внедрено така. Можете да направите това, но вероятно няма да се замислите и всъщност да изберете някоя от другите алтернативи, освен ако нямате нужда от „гъвкави интервали“ до степента, в която го правя аз..

Fluent Agregate

Ако имате наличен модерен сървър MongoDB 3.6 или по-висок, тогава можете да използвате $dateFromParts за да се възстанови датата от "закръглените" части, извлечени от датата:

DateTime startDate = new DateTime(2018, 5, 1, 0, 0, 0, DateTimeKind.Utc);
DateTime endDate = new DateTime(2018, 6, 1, 0, 0, 0, DateTimeKind.Utc);

var result = Collection.Aggregate()
  .Match(k => k.Timestamp >= startDate && k.Timestamp < endDate)
  .Group(k =>
    new DateTime(k.Timestamp.Year, k.Timestamp.Month, k.Timestamp.Day,
        k.Timestamp.Hour, k.Timestamp.Minute - (k.Timestamp.Minute % 15), 0),
    g => new { _id = g.Key, count = g.Count() }
  )
  .SortBy(d => d._id)
  .ToList();

Изявление, изпратено до сървъра:

[
  { "$match" : {
    "Timestamp" : {
      "$gte" : ISODate("2018-05-01T00:00:00Z"),
      "$lt" : ISODate("2018-06-01T00:00:00Z")
    }
  } },
  { "$group" : {
    "_id" : { 
      "$dateFromParts" : {
        "year" : { "$year" : "$Timestamp" },
        "month" : { "$month" : "$Timestamp" },
        "day" : { "$dayOfMonth" : "$Timestamp" },
        "hour" : { "$hour" : "$Timestamp" },
        "minute" : { "$subtract" : [
          { "$minute" : "$Timestamp" },
          { "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] }
        ] },
        "second" : 0
      }
    },
    "count" : { "$sum" : 1 }
  } },
  { "$sort": { "_id": 1 } }
]

Ако не разполагате с тази функция, можете просто да я оставите изключена и да оставите датата „разглобена“, но след това да я сглобите отново, докато обработвате курсора. Само за симулиране със списък:

var result = Collection.Aggregate()
 .Match(k => k.Timestamp >= startDate && k.Timestamp < endDate)
 .Group(k => new
    {
      year = k.Timestamp.Year,
      month = k.Timestamp.Month,
      day = k.Timestamp.Day,
      hour = k.Timestamp.Hour,
      minute = k.Timestamp.Minute - (k.Timestamp.Minute % 15)
    },
    g => new { _id = g.Key, count = g.Count() }
  )
  .SortBy(d => d._id)
  .ToList();

foreach (var doc in result)
{
  //System.Console.WriteLine(doc.ToBsonDocument());
  System.Console.WriteLine(
    new BsonDocument {
      { "_id", new DateTime(doc._id.year, doc._id.month, doc._id.day,
        doc._id.hour, doc._id.minute, 0) },
      { "count", doc.count }
    }
  );
}

Изявление, изпратено до сървъра:

[
  { "$match" : {
    "Timestamp" : {
      "$gte" : ISODate("2018-05-01T00:00:00Z"),
      "$lt" : ISODate("2018-06-01T00:00:00Z")
    }
  } },
  { "$group" : {
    "_id" : {
      "year" : { "$year" : "$Timestamp" },
      "month" : { "$month" : "$Timestamp" },
      "day" : { "$dayOfMonth" : "$Timestamp" },
      "hour" : { "$hour" : "$Timestamp" },
      "minute" : { "$subtract" : [
        { "$minute" : "$Timestamp" }, 
        { "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] }
      ] }
    },
    "count" : { "$sum" : 1 }
  } },
  { "$sort" : { "_id" : 1 } }
]

Има много малка разлика между двете по отношение на кода. Просто в един случай "връщането" към DateTime всъщност се случва на сървъра с $dateFromParts а в другия просто правим точно същото кастинг, използвайки DateTime конструктор в кода, докато повтаряте всеки резултат от курсора.

Така че те наистина са почти еднакви с единствената реална разлика е къде "сървърът" извършва кастинга, върнатата дата използва много по-малко байтове на документ. Всъщност "5 пъти" по-малко, тъй като всички цифрови формати тук (включително датата на BSON) са базирани на 64-битови цели числа. Въпреки това всички тези числа са всъщност „по-леки“ от изпращането обратно на каквото и да е „низово“ представяне на дата.

LINQ Queryable

Това са основните форми, които наистина остават същите, когато се съпоставят с тези различни форми:

var query = from p in Collection.AsQueryable()
            where p.Timestamp >= startDate && p.Timestamp < endDate
            group p by new DateTime(p.Timestamp.Year, p.Timestamp.Month, p.Timestamp.Day,
              p.Timestamp.Hour, p.Timestamp.Minute - (p.Timestamp.Minute % 15), 0) into g
            orderby g.Key
            select new { _id = g.Key, count = g.Count() };

Изявление, изпратено до сървъра:

[
  { "$match" : {
    "Timestamp" : {
      "$gte" : ISODate("2018-05-01T00:00:00Z"),
      "$lt" : ISODate("2018-06-01T00:00:00Z")
    }
  } },
  { "$group" : {
    "_id" : {
      "$dateFromParts" : {
        "year" : { "$year" : "$Timestamp" }, 
        "month" : { "$month" : "$Timestamp" },
        "day" : { "$dayOfMonth" : "$Timestamp" }, 
        "hour" : { "$hour" : "$Timestamp" }, 
        "minute" : { "$subtract" : [
          { "$minute" : "$Timestamp" },
          { "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] }
        ] },
        "second" : 0
      }
    },
    "__agg0" : { "$sum" : 1 }
  } },
  { "$sort" : { "_id" : 1 } },
  { "$project" : { "_id" : "$_id", "count" : "$__agg0" } }
]

Или с помощта на GroupBy()

var query = Collection.AsQueryable()
    .Where(k => k.Timestamp >= startDate && k.Timestamp < endDate)
    .GroupBy(k =>
      new DateTime(k.Timestamp.Year, k.Timestamp.Month, k.Timestamp.Day,
            k.Timestamp.Hour, k.Timestamp.Minute - (k.Timestamp.Minute % 15), 0),
      (k, s) => new { _id = k, count = s.Count() }
    )
    .OrderBy(k => k._id);

Изявление, изпратено до сървъра:

[
  { "$match" : {
    "Timestamp" : {
      "$gte" : ISODate("2018-05-01T00:00:00Z"),
      "$lt" : ISODate("2018-06-01T00:00:00Z")
    }
  } },
  { "$group" : {
    "_id" : {
      "$dateFromParts" : {
        "year" : { "$year" : "$Timestamp" },
        "month" : { "$month" : "$Timestamp" },
        "day" : { "$dayOfMonth" : "$Timestamp" },
        "hour" : { "$hour" : "$Timestamp" },
        "minute" : { "$subtract" : [ 
          { "$minute" : "$Timestamp" }, 
          { "$mod" : [ { "$minute" : "$Timestamp" }, 15 ] } 
        ] },
        "second" : 0
      }
    },
    "count" : { "$sum" : 1 }
  } },
  { "$sort" : { "_id" : 1 } }
]

Както можете да видите, всичко това е основно една и съща форма

Преобразуване на оригинала

Ако искате да възпроизведете оригиналния формуляр за „математика на датата“, както е публикуван, тогава той в момента попада извън обхвата на това, което всъщност можете да правите с LINQ или с конструкторите на Fluent. Единственият начин да получите същата последователност е с BsonDocument конструкция:

DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

var group = new BsonDocument { {
  "$group",
  new BsonDocument {
    { "_id",
    new BsonDocument { {
      "$add", new BsonArray
      {
        new BsonDocument { {
            "$subtract",
            new BsonArray {
              new BsonDocument { { "$subtract", new BsonArray { "$Timestamp", epoch } } },
              new BsonDocument { {
                "$mod", new BsonArray
                {
                 new BsonDocument { { "$subtract", new BsonArray { "$Timestamp", epoch } } },
                 1000 * 60 * 15
               }
             } }
           }
         } },
         epoch
       }
     } }
     },
     {
       "count", new BsonDocument("$sum", 1)
     }
   }
} };

var query = sales.Aggregate()
  .Match(k => k.Timestamp >= startDate && k.Timestamp < endDate)
  .AppendStage<BsonDocument>(group)
  .Sort(new BsonDocument("_id", 1))
  .ToList();

Заявката е изпратена до сървъра:

[
  { "$match" : {
    "Timestamp" : {
      "$gte" : ISODate("2018-05-01T00:00:00Z"),
      "$lt" : ISODate("2018-06-01T00:00:00Z")
    }
  } },
  { "$group" : {
    "_id" : { 
      "$add" : [
        { "$subtract" : [ 
          { "$subtract" : [ "$Timestamp", ISODate("1970-01-01T00:00:00Z") ] },
          { "$mod" : [ 
            { "$subtract" : [ "$Timestamp", ISODate("1970-01-01T00:00:00Z") ] },
            900000
          ] }
        ] },
        ISODate("1970-01-01T00:00:00Z")
      ]
    },
    "count" : { "$sum" : 1 }
  } },
  { "$sort" : { "_id" : 1 } }
]

Основната причина, поради която не можем да направим това в момента, е, че текущата сериализация на изразите по същество не е съгласна с точката, която .NET Framework казва, че изваждането на две DateTime стойностите връщат TimeSpan , а конструкцията MongoDB за изваждане на две BSON дати връща "милисекунди от епохата", което по същество е начинът, по който работи математиката.

"Буквалният" превод на израза lamdba е по същество:

p =>  epoch.AddMilliseconds(
       (p.Timestamp - epoch).TotalMilliseconds
       - ((p.Timestamp - epoch).TotalMilliseconds % 1000 * 60 * 15))

Но картографирането все още се нуждае от известна работа, за да разпознае изразите или да формализира кой вид изрази всъщност са предназначени за тази цел.

По-специално MongoDB 4.0 въвежда $convert оператор и общите псевдоними на $toLong и $toDate , които могат да бъдат използвани в конвейера вместо текущата обработка на „събиране“ и „изваждане“ с BSON дати. Те започват да формират по-"формална" спецификация за такива преобразувания, а не показания метод, който разчита единствено на това "добавяне" и "изваждане", които все още са валидни, но такива наименовани оператори са много по-ясни за намерението в кода:

{ "$group": {
  "_id": {
    "$toDate": {
      "$subtract": [
        { "$toLong": "$Timestamp" },
        { "$mod": [{ "$toLong": "$Timestamp" }, 1000 * 60 * 15 ] }
      ]
    }
  },
  "count": { "$sum": 1 }
}}

Доста ясно е да се види, че с "формализирани" оператори за конструиране на изрази с LINQ за такива функции "DateToLong" и "LongToDate", тогава изразът става много по-чист, без типовете "принуди", показани в "неработещия" ламбда израз, да бъдат готово.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Връщане на резултатите mongoose в заявката за намиране към променлива

  2. GridFS в Spring Data MongoDB

  3. MongoDB $allElementsTrue

  4. 10-те най-добри MongoDB хостинг платформи

  5. node.js не може да намери модул 'mongodb'