MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Nhóm theo ngày với múi giờ địa phương trong MongoDB

Vấn đề chung về xử lý "ngày địa phương"

Vì vậy, có một câu trả lời ngắn cho điều này và một câu trả lời dài. Trường hợp cơ bản là thay vì sử dụng bất kỳ "toán tử tổng hợp ngày" nào, thay vào đó bạn muốn và "cần" thực sự "thực hiện phép toán" trên các đối tượng ngày. Điều đầu tiên ở đây là điều chỉnh các giá trị theo độ lệch từ UTC cho múi giờ địa phương nhất định và sau đó "làm tròn" đến khoảng thời gian cần thiết.

"Câu trả lời dài hơn nhiều" và cũng là vấn đề chính cần xem xét liên quan đến việc các ngày thường chịu sự thay đổi của "Giờ tiết kiệm ánh sáng ban ngày" trong chênh lệch so với giờ UTC tại các thời điểm khác nhau trong năm. Vì vậy, điều này có nghĩa là khi chuyển đổi sang "giờ địa phương" cho các mục đích tổng hợp như vậy, bạn thực sự nên xem xét ranh giới cho những thay đổi đó tồn tại ở đâu.

Cũng có một cân nhắc khác, đó là bất kể bạn làm gì để "tổng hợp" tại một khoảng thời gian nhất định, các giá trị đầu ra "phải" ít nhất ban đầu phải xuất hiện dưới dạng UTC. Đây là một thực tiễn tốt vì hiển thị thành "ngôn ngữ" thực sự là một "chức năng khách" và như được mô tả sau đó, các giao diện ứng dụng khách thường sẽ có cách hiển thị trong ngôn ngữ hiện tại dựa trên tiền đề rằng nó đã được đưa vào thực tế dữ liệu dưới dạng UTC.

Xác định Chênh lệch Vị trí và Tiết kiệm Ánh sáng Ban ngày

Đây nói chung là vấn đề chính cần được giải quyết. Toán học chung để "làm tròn" một ngày thành một khoảng thời gian là một phần đơn giản, nhưng không có phép toán thực tế nào mà bạn có thể áp dụng để biết khi nào các ranh giới đó áp dụng và các quy tắc thay đổi ở mọi ngôn ngữ và thường xuyên hàng năm.

Vì vậy, đây là nơi "thư viện" đi vào và tùy chọn tốt nhất ở đây theo ý kiến ​​của các tác giả cho nền tảng JavaScript là múi giờ thời điểm, về cơ bản là "tập hợp siêu" của khoảnh khắc. Js bao gồm tất cả các tính năng "thời gian" quan trọng mà chúng tôi muốn để sử dụng.

Múi giờ thời điểm về cơ bản xác định cấu trúc như vậy cho từng múi giờ địa phương như sau:

{
    name    : 'America/Los_Angeles',          // the unique identifier
    abbrs   : ['PDT', 'PST'],                 // the abbreviations
    untils  : [1414918800000, 1425808800000], // the timestamps in milliseconds
    offsets : [420, 480]                      // the offsets in minutes
}

Tất nhiên các đối tượng ở đâu nhiều lớn hơn đối với untilsoffsets tài sản thực sự được ghi nhận. Tuy nhiên, đó là dữ liệu bạn cần truy cập để xem liệu có thực sự thay đổi về bù đắp đối với các thay đổi về tiết kiệm ánh sáng ban ngày của một khu vực nhất định hay không.

Khối danh sách mã sau này là khối về cơ bản chúng tôi sử dụng để xác định một start đã cho và end giá trị cho một phạm vi, ranh giới tiết kiệm ánh sáng ban ngày được vượt qua, nếu có:

  const zone = moment.tz.zone(locale);
  if ( zone.hasOwnProperty('untils') ) {
    let between = zone.untils.filter( u =>
      u >= start.valueOf() && u < end.valueOf()
    );
    if ( between.length > 0 )
      branches = between
        .map( d => moment.tz(d, locale) )
        .reduce((acc,curr,i,arr) =>
          acc.concat(
            ( i === 0 )
              ? [{ start, end: curr }] : [{ start: acc[i-1].end, end: curr }],
            ( i === arr.length-1 ) ? [{ start: curr, end }] : []
          )
        ,[]);
  }

Nhìn lại toàn bộ năm 2017 cho Australia/Sydney ngôn ngữ đầu ra của điều này sẽ là:

[
  {
    "start": "2016-12-31T13:00:00.000Z",    // Interval is +11 hours here
    "end": "2017-04-01T16:00:00.000Z"
  },
  {
    "start": "2017-04-01T16:00:00.000Z",    // Changes to +10 hours here
    "end": "2017-09-30T16:00:00.000Z"
  },
  {
    "start": "2017-09-30T16:00:00.000Z",    // Changes back to +11 hours here
    "end": "2017-12-31T13:00:00.000Z"
  }
]

Về cơ bản, tiết lộ rằng giữa chuỗi ngày đầu tiên, chênh lệch sẽ là +11 giờ, sau đó thay đổi thành +10 giờ giữa các ngày trong chuỗi thứ hai và sau đó chuyển trở lại +11 giờ cho khoảng thời gian đến cuối năm và phạm vi được chỉ định.

Logic này sau đó cần được dịch thành một cấu trúc sẽ được MongoDB hiểu như một phần của đường ống tổng hợp.

Áp dụng Toán học

Nguyên tắc toán học ở đây để tổng hợp thành bất kỳ "khoảng ngày làm tròn" nào về cơ bản dựa vào việc sử dụng giá trị mili giây của ngày được biểu thị được "làm tròn" xuống số gần nhất đại diện cho "khoảng ngày" được yêu cầu.

Về cơ bản, bạn thực hiện việc này bằng cách tìm "modulo" hoặc "phần còn lại" của giá trị hiện tại được áp dụng cho khoảng thời gian cần thiết. Sau đó, bạn "trừ" phần còn lại đó khỏi giá trị hiện tại, giá trị này sẽ trả về một giá trị ở khoảng thời gian gần nhất.

Ví dụ:với ngày hiện tại:

  var d = new Date("2017-07-14T01:28:34.931Z"); // toValue() is 1499995714931 millis
  // 1000 millseconds * 60 seconds * 60 minutes = 1 hour or 3600000 millis
  var v = d.valueOf() - ( d.valueOf() % ( 1000 * 60 * 60 ) );
  // v equals 1499994000000 millis or as a date
  new Date(1499994000000);
  ISODate("2017-07-14T01:00:00Z") 
  // which removed the 28 minutes and change to nearest 1 hour interval

Đây là phép toán chung mà chúng tôi cũng cần áp dụng trong quy trình tổng hợp bằng cách sử dụng $subtract$mod các phép toán, là các biểu thức tổng hợp được sử dụng cho các phép toán tương tự được trình bày ở trên.

Cấu trúc chung của đường ống tổng hợp sau đó là:

    let pipeline = [
      { "$match": {
        "createdAt": { "$gte": start.toDate(), "$lt": end.toDate() }
      }},
      { "$group": {
        "_id": {
          "$add": [
            { "$subtract": [
              { "$subtract": [
                { "$subtract": [ "$createdAt", new Date(0) ] },
                switchOffset(start,end,"$createdAt",false)
              ]},
              { "$mod": [
                { "$subtract": [
                  { "$subtract": [ "$createdAt", new Date(0) ] },
                  switchOffset(start,end,"$createdAt",false)
                ]},
                interval
              ]}
            ]},
            new Date(0)
          ]
        },
        "amount": { "$sum": "$amount" }
      }},
      { "$addFields": {
        "_id": {
          "$add": [
            "$_id", switchOffset(start,end,"$_id",true)
          ]
        }
      }},
      { "$sort": { "_id": 1 } }
    ];

Các phần chính ở đây bạn cần hiểu là chuyển đổi từ Date đối tượng như được lưu trữ trong MongoDB thành Numeric đại diện cho giá trị dấu thời gian bên trong. Chúng ta cần dạng "số" và để làm điều này là một mẹo toán học trong đó chúng ta lấy một Ngày BSON trừ đi một Ngày khác mà tạo ra sự khác biệt về số giữa chúng. Đây chính xác là những gì câu lệnh này thực hiện:

{ "$subtract": [ "$createdAt", new Date(0) ] }

Bây giờ chúng ta có một giá trị số để xử lý, chúng ta có thể áp dụng modulo và trừ nó khỏi biểu diễn số của ngày để làm "tròn" nó. Vì vậy, biểu diễn "thẳng" của điều này giống như:

{ "$subtract": [
  { "$subtract": [ "$createdAt", new Date(0) ] },
  { "$mod": [
    { "$subtract": [ "$createdAt", new Date(0) ] },
    ( 1000 * 60 * 60 * 24 ) // 24 hours
  ]}
]}

Phương pháp này phản ánh cách tiếp cận toán học JavaScript tương tự như được hiển thị trước đó nhưng được áp dụng cho các giá trị tài liệu thực tế trong quy trình tổng hợp. Bạn cũng sẽ lưu ý "thủ thuật" khác ở đó chúng tôi áp dụng $add hoạt động với một biểu diễn khác của ngày BSON kể từ epoch (hoặc 0 mili giây) trong đó "thêm" Ngày BSON vào giá trị "số", trả về "Ngày BSON" đại diện cho mili giây mà nó được cung cấp làm đầu vào.

Tất nhiên, xem xét khác trong mã được liệt kê đó là "phần bù" thực tế từ UTC đang điều chỉnh các giá trị số để đảm bảo "làm tròn" diễn ra cho múi giờ hiện tại. Điều này được triển khai trong một hàm dựa trên mô tả trước đó về việc tìm vị trí xảy ra các hiệu số khác nhau và trả về một định dạng có thể sử dụng được trong biểu thức đường ống tổng hợp bằng cách so sánh các ngày nhập và trả về độ lệch chính xác.

Với việc mở rộng đầy đủ tất cả các chi tiết, bao gồm cả việc tạo ra cách xử lý các khoảng cách thời gian "Tiết kiệm ánh sáng ban ngày" khác nhau đó sẽ như sau:

[
  {
    "$match": {
      "createdAt": {
        "$gte": "2016-12-31T13:00:00.000Z",
        "$lt": "2017-12-31T13:00:00.000Z"
      }
    }
  },
  {
    "$group": {
      "_id": {
        "$add": [
          {
            "$subtract": [
              {
                "$subtract": [
                  {
                    "$subtract": [
                      "$createdAt",
                      "1970-01-01T00:00:00.000Z"
                    ]
                  },
                  {
                    "$switch": {
                      "branches": [
                        {
                          "case": {
                            "$and": [
                              {
                                "$gte": [
                                  "$createdAt",
                                  "2016-12-31T13:00:00.000Z"
                                ]
                              },
                              {
                                "$lt": [
                                  "$createdAt",
                                  "2017-04-01T16:00:00.000Z"
                                ]
                              }
                            ]
                          },
                          "then": -39600000
                        },
                        {
                          "case": {
                            "$and": [
                              {
                                "$gte": [
                                  "$createdAt",
                                  "2017-04-01T16:00:00.000Z"
                                ]
                              },
                              {
                                "$lt": [
                                  "$createdAt",
                                  "2017-09-30T16:00:00.000Z"
                                ]
                              }
                            ]
                          },
                          "then": -36000000
                        },
                        {
                          "case": {
                            "$and": [
                              {
                                "$gte": [
                                  "$createdAt",
                                  "2017-09-30T16:00:00.000Z"
                                ]
                              },
                              {
                                "$lt": [
                                  "$createdAt",
                                  "2017-12-31T13:00:00.000Z"
                                ]
                              }
                            ]
                          },
                          "then": -39600000
                        }
                      ]
                    }
                  }
                ]
              },
              {
                "$mod": [
                  {
                    "$subtract": [
                      {
                        "$subtract": [
                          "$createdAt",
                          "1970-01-01T00:00:00.000Z"
                        ]
                      },
                      {
                        "$switch": {
                          "branches": [
                            {
                              "case": {
                                "$and": [
                                  {
                                    "$gte": [
                                      "$createdAt",
                                      "2016-12-31T13:00:00.000Z"
                                    ]
                                  },
                                  {
                                    "$lt": [
                                      "$createdAt",
                                      "2017-04-01T16:00:00.000Z"
                                    ]
                                  }
                                ]
                              },
                              "then": -39600000
                            },
                            {
                              "case": {
                                "$and": [
                                  {
                                    "$gte": [
                                      "$createdAt",
                                      "2017-04-01T16:00:00.000Z"
                                    ]
                                  },
                                  {
                                    "$lt": [
                                      "$createdAt",
                                      "2017-09-30T16:00:00.000Z"
                                    ]
                                  }
                                ]
                              },
                              "then": -36000000
                            },
                            {
                              "case": {
                                "$and": [
                                  {
                                    "$gte": [
                                      "$createdAt",
                                      "2017-09-30T16:00:00.000Z"
                                    ]
                                  },
                                  {
                                    "$lt": [
                                      "$createdAt",
                                      "2017-12-31T13:00:00.000Z"
                                    ]
                                  }
                                ]
                              },
                              "then": -39600000
                            }
                          ]
                        }
                      }
                    ]
                  },
                  86400000
                ]
              }
            ]
          },
          "1970-01-01T00:00:00.000Z"
        ]
      },
      "amount": {
        "$sum": "$amount"
      }
    }
  },
  {
    "$addFields": {
      "_id": {
        "$add": [
          "$_id",
          {
            "$switch": {
              "branches": [
                {
                  "case": {
                    "$and": [
                      {
                        "$gte": [
                          "$_id",
                          "2017-01-01T00:00:00.000Z"
                        ]
                      },
                      {
                        "$lt": [
                          "$_id",
                          "2017-04-02T03:00:00.000Z"
                        ]
                      }
                    ]
                  },
                  "then": -39600000
                },
                {
                  "case": {
                    "$and": [
                      {
                        "$gte": [
                          "$_id",
                          "2017-04-02T02:00:00.000Z"
                        ]
                      },
                      {
                        "$lt": [
                          "$_id",
                          "2017-10-01T02:00:00.000Z"
                        ]
                      }
                    ]
                  },
                  "then": -36000000
                },
                {
                  "case": {
                    "$and": [
                      {
                        "$gte": [
                          "$_id",
                          "2017-10-01T03:00:00.000Z"
                        ]
                      },
                      {
                        "$lt": [
                          "$_id",
                          "2018-01-01T00:00:00.000Z"
                        ]
                      }
                    ]
                  },
                  "then": -39600000
                }
              ]
            }
          }
        ]
      }
    }
  },
  {
    "$sort": {
      "_id": 1
    }
  }
]

Mở rộng đó đang sử dụng $switch để áp dụng phạm vi ngày làm điều kiện cho thời điểm trả về các giá trị bù đắp đã cho. Đây là hình thức thuận tiện nhất vì "branches" đối số tương ứng trực tiếp với "mảng", là kết quả đầu ra thuận tiện nhất của "dải ô" được xác định bằng cách kiểm tra untils đại diện cho "điểm cắt" bù đắp cho múi giờ nhất định trong phạm vi ngày được cung cấp của truy vấn.

Có thể áp dụng logic tương tự trong các phiên bản trước của MongoDB bằng cách sử dụng triển khai "lồng nhau" của $cond thay vào đó, nhưng nó hơi lộn xộn hơn để triển khai, vì vậy chúng tôi chỉ sử dụng phương pháp thuận tiện nhất để triển khai ở đây.

Sau khi tất cả các điều kiện đó được áp dụng, ngày "tổng hợp" thực sự là ngày đại diện cho giờ "địa phương" như được xác định bởi locale được cung cấp . Điều này thực sự đưa chúng ta đến giai đoạn tổng hợp cuối cùng là gì, và lý do tại sao nó ở đó cũng như cách xử lý sau này như được trình bày trong danh sách.

Kết quả cuối cùng

Tôi đã đề cập trước đó rằng khuyến nghị chung là "đầu ra" vẫn phải trả về giá trị ngày ở định dạng UTC của ít nhất một số mô tả và do đó, đó chính xác là những gì đường ống ở đây đang thực hiện bằng cách chuyển đổi "từ" UTC sang cục bộ trước tiên bằng cách áp dụng mức chênh lệch khi "làm tròn", nhưng sau đó các số cuối cùng "sau khi nhóm" được điều chỉnh lại bằng cùng mức chênh lệch áp dụng cho các giá trị ngày "làm tròn".

Danh sách ở đây cung cấp cho "ba" khả năng đầu ra khác nhau ở đây như:

// ISO Format string from JSON stringify default
[
  {
    "_id": "2016-12-31T13:00:00.000Z",
    "amount": 2
  },
  {
    "_id": "2017-01-01T13:00:00.000Z",
    "amount": 1
  },
  {
    "_id": "2017-01-02T13:00:00.000Z",
    "amount": 2
  }
]
// Timestamp value - milliseconds from epoch UTC - least space!
[
  {
    "_id": 1483189200000,
    "amount": 2
  },
  {
    "_id": 1483275600000,
    "amount": 1
  },
  {
    "_id": 1483362000000,
    "amount": 2
  }
]

// Force locale format to string via moment .format()
[
  {
    "_id": "2017-01-01T00:00:00+11:00",
    "amount": 2
  },
  {
    "_id": "2017-01-02T00:00:00+11:00",
    "amount": 1
  },
  {
    "_id": "2017-01-03T00:00:00+11:00",
    "amount": 2
  }
]

Một điều cần lưu ý ở đây là đối với một "máy khách" chẳng hạn như Angular, mỗi một định dạng trong số đó sẽ được chấp nhận bởi DatePipe của chính nó, nó thực sự có thể thực hiện "định dạng ngôn ngữ" cho bạn. Nhưng nó phụ thuộc vào nơi dữ liệu được cung cấp. Các thư viện "tốt" sẽ biết về việc sử dụng ngày UTC ở ngôn ngữ hiện tại. Trong trường hợp đó không phải là trường hợp, thì bạn có thể cần phải tự "xâu chuỗi".

Nhưng đó là một điều đơn giản và bạn nhận được sự hỗ trợ tối đa cho việc này bằng cách sử dụng một thư viện mà về cơ bản nó là thao tác của đầu ra từ một "giá trị UTC nhất định".

Điều chính ở đây là "hiểu bạn đang làm gì" khi bạn hỏi một điều chẳng hạn như tổng hợp theo múi giờ địa phương. Quá trình như vậy nên xem xét:

  1. Dữ liệu có thể được và thường xuyên được xem từ quan điểm của mọi người trong các múi giờ khác nhau.

  2. Dữ liệu thường được cung cấp bởi những người ở các múi giờ khác nhau. Kết hợp với điểm 1, đây là lý do tại sao chúng tôi lưu trữ ở UTC.

  3. Múi giờ thường có sự thay đổi "bù đắp" so với "Giờ tiết kiệm ánh sáng ban ngày" ở nhiều múi giờ trên thế giới và bạn nên tính đến điều đó khi phân tích và xử lý dữ liệu.

  4. Bất kể khoảng thời gian tổng hợp, đầu ra "nên" trên thực tế vẫn ở UTC, mặc dù được điều chỉnh để tổng hợp vào khoảng thời gian theo ngôn ngữ được cung cấp. Điều này khiến bản trình bày được ủy quyền cho một chức năng "khách hàng", giống như nó nên làm.

Miễn là bạn ghi nhớ những điều đó và áp dụng giống như danh sách ở đây trình bày, thì bạn đang làm tất cả những điều đúng đắn để xử lý việc tổng hợp ngày tháng và thậm chí lưu trữ chung đối với một ngôn ngữ nhất định.

Vì vậy, bạn "nên" làm điều này, và những gì bạn "không nên" làm là từ bỏ và chỉ cần lưu trữ "ngày miền địa phương" dưới dạng một chuỗi. Như đã mô tả, đó sẽ là một cách tiếp cận rất không chính xác và không gây ra vấn đề gì khác ngoài ứng dụng của bạn.

LƯU Ý :Một chủ đề mà tôi không đề cập đến ở đây là tổng hợp thành một "tháng" (hoặc thực sự là "năm") khoảng thời gian. "Tháng" là sự bất thường toán học trong toàn bộ quá trình vì số ngày luôn thay đổi và do đó yêu cầu một tập hợp logic hoàn toàn khác để áp dụng. Chỉ mô tả điều đó ít nhất cũng dài như bài đăng này, và do đó sẽ là một chủ đề khác. Đối với trường hợp chung là phút, giờ và ngày, thì phép toán ở đây là "đủ tốt" cho những trường hợp đó.

Danh sách đầy đủ

Đây coi như là một "trình diễn" để mày mò. Nó sử dụng chức năng cần thiết để trích xuất các ngày và giá trị bù đắp được bao gồm và chạy một đường dẫn tổng hợp trên dữ liệu được cung cấp.

Bạn có thể thay đổi bất kỳ thứ gì trong đây, nhưng có thể sẽ bắt đầu bằng localeinterval và sau đó có thể thêm dữ liệu khác nhau và start khác nhau và end ngày cho truy vấn. Nhưng phần còn lại của mã không cần thay đổi mà chỉ cần thực hiện thay đổi đối với bất kỳ giá trị nào trong số đó và do đó có thể chứng minh bằng cách sử dụng các khoảng thời gian khác nhau (chẳng hạn như 1 hour như đã hỏi trong câu hỏi) và các ngôn ngữ khác nhau.

Ví dụ:sau khi cung cấp dữ liệu hợp lệ thực sự yêu cầu tổng hợp ở "khoảng thời gian 1 giờ" thì dòng trong danh sách sẽ được thay đổi thành:

const interval = moment.duration(1,'hour').asMilliseconds();

Để xác định giá trị mili giây cho khoảng thời gian tổng hợp theo yêu cầu của các hoạt động tổng hợp đang được thực hiện vào các ngày.

const moment = require('moment-timezone'),
      mongoose = require('mongoose'),
      Schema = mongoose.Schema;

mongoose.Promise = global.Promise;
mongoose.set('debug',true);

const uri = 'mongodb://localhost/test',
      options = { useMongoClient: true };

const locale = 'Australia/Sydney';
const interval = moment.duration(1,'day').asMilliseconds();

const reportSchema = new Schema({
  createdAt: Date,
  amount: Number
});

const Report = mongoose.model('Report', reportSchema);

function log(data) {
  console.log(JSON.stringify(data,undefined,2))
}

function switchOffset(start,end,field,reverseOffset) {

  let branches = [{ start, end }]

  const zone = moment.tz.zone(locale);
  if ( zone.hasOwnProperty('untils') ) {
    let between = zone.untils.filter( u =>
      u >= start.valueOf() && u < end.valueOf()
    );
    if ( between.length > 0 )
      branches = between
        .map( d => moment.tz(d, locale) )
        .reduce((acc,curr,i,arr) =>
          acc.concat(
            ( i === 0 )
              ? [{ start, end: curr }] : [{ start: acc[i-1].end, end: curr }],
            ( i === arr.length-1 ) ? [{ start: curr, end }] : []
          )
        ,[]);
  }

  log(branches);

  branches = branches.map( d => ({
    case: {
      $and: [
        { $gte: [
          field,
          new Date(
            d.start.valueOf()
            + ((reverseOffset)
              ? moment.duration(d.start.utcOffset(),'minutes').asMilliseconds()
              : 0)
          )
        ]},
        { $lt: [
          field,
          new Date(
            d.end.valueOf()
            + ((reverseOffset)
              ? moment.duration(d.start.utcOffset(),'minutes').asMilliseconds()
              : 0)
          )
        ]}
      ]
    },
    then: -1 * moment.duration(d.start.utcOffset(),'minutes').asMilliseconds()
  }));

  return ({ $switch: { branches } });

}

(async function() {
  try {
    const conn = await mongoose.connect(uri,options);

    // Data cleanup
    await Promise.all(
      Object.keys(conn.models).map( m => conn.models[m].remove({}))
    );

    let inserted = await Report.insertMany([
      { createdAt: moment.tz("2017-01-01",locale), amount: 1 },
      { createdAt: moment.tz("2017-01-01",locale), amount: 1 },
      { createdAt: moment.tz("2017-01-02",locale), amount: 1 },
      { createdAt: moment.tz("2017-01-03",locale), amount: 1 },
      { createdAt: moment.tz("2017-01-03",locale), amount: 1 },
    ]);

    log(inserted);

    const start = moment.tz("2017-01-01", locale)
          end   = moment.tz("2018-01-01", locale)

    let pipeline = [
      { "$match": {
        "createdAt": { "$gte": start.toDate(), "$lt": end.toDate() }
      }},
      { "$group": {
        "_id": {
          "$add": [
            { "$subtract": [
              { "$subtract": [
                { "$subtract": [ "$createdAt", new Date(0) ] },
                switchOffset(start,end,"$createdAt",false)
              ]},
              { "$mod": [
                { "$subtract": [
                  { "$subtract": [ "$createdAt", new Date(0) ] },
                  switchOffset(start,end,"$createdAt",false)
                ]},
                interval
              ]}
            ]},
            new Date(0)
          ]
        },
        "amount": { "$sum": "$amount" }
      }},
      { "$addFields": {
        "_id": {
          "$add": [
            "$_id", switchOffset(start,end,"$_id",true)
          ]
        }
      }},
      { "$sort": { "_id": 1 } }
    ];

    log(pipeline);
    let results = await Report.aggregate(pipeline);

    // log raw Date objects, will stringify as UTC in JSON
    log(results);

    // I like to output timestamp values and let the client format
    results = results.map( d =>
      Object.assign(d, { _id: d._id.valueOf() })
    );
    log(results);

    // Or use moment to format the output for locale as a string
    results = results.map( d =>
      Object.assign(d, { _id: moment.tz(d._id, locale).format() } )
    );
    log(results);

  } catch(e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }
})()


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Làm thế nào để kết nối với mongodb bằng sailsjs v0.10?

  2. Tôi có nên sử dụng tùy chọn allowDiskUse trong môi trường sản phẩm không?

  3. Tổng quan về Xác thực lược đồ MongoDB

  4. MongoDB - Nhiều $ hoặc các phép toán

  5. Chuyển đổi chuỗi thành ngày tháng trong mongodb