【问题标题】:Find last document of the day for the last 7 days查找过去 7 天当天的最后一个文档
【发布时间】:2012-01-29 17:53:56
【问题描述】:

我每小时向架构添加条目,以便跟踪几天内的增长,同时保持当天的当前分数。现在我希望能够提取过去一周每天的最新记录。结果将是前 6 天午夜或附近的 6 条记录,而当天最晚的第 7 条记录。

这是我的架构:

var schema = new Schema({
  aid: { type: Number }
, name: { type: String }
, score: { type: Number }
, createdAt: { type: Date, default: Date.now() }
})

编辑

我尝试过使用这个静态,但它会拉出完全相同的记录 7 次

schema.statics.getLastWeek = function(name, fn) {
  var oneday = 60 * 60 * 24
    , now = Date.now()
    , docs = []

  for (var i = 1; i <= 7; i++) {
    this.where('name', new RegExp(name, 'i'))
    .where('createdAt')
    .gte(now - (i * oneday))
    .desc('createdAt')
    .findOne(function(err,doc){
      docs.push(doc)
    })
  }
}

如果我使用 SQL,我会执行一个选择 MAXDATE 的子查询并将其连接到我的主查询中,以便检索我想要的结果。无论如何要在这里做?

【问题讨论】:

    标签: javascript node.js mongodb mongoose


    【解决方案1】:

    似乎没有人试图“接近午夜”。 :) 我在原始代码中看到的问题是它检查了大于或等于 x 天前的时间......这将始终返回最近的时间。不过,我很困惑为什么 DeaDEnD 的解决方案会返回 7 次相同的记录。另外,你从来没有给fn打过电话,但这并不是你最关心的问题,不是吗?

    尝试添加.lt(now - (now % oneday) - (i - 1) * oneday)(假设索引为0;如果索引为1,则将其更改为i - 2

    【讨论】:

      【解决方案2】:

      Kristina Chodorow gives a detailed recipe for this exact task in her book MongoDB: The Definitive Guide:

      假设我们有一个跟踪股票价格的网站。每隔几 从上午 10 点到下午 4 点,它可以获取股票的最新价格, 它存储在 MongoDB 中。现在,作为报告应用程序的一部分, 我们想找到过去 30 天的收盘价。这可以是 使用组很容易完成。

      我对 Mongoose 不熟悉,但我已尝试将她的示例改编为以下您的案例。请注意,我将 createdAt default 属性从一个值更改为一个函数,并在您的架构中添加了一个额外的字段 datestamp

      var oneday = 24 * 60 * 60;
      
      var schema = new Schema({
        aid: { type: Number }
      , name: { type: String }
      , score: { type: Number }
      
        // default: is a function and called every time; not a one-time value!
      , createdAt: { type: Date, default: Date.now }
      
        // For grouping by day; documents created on same day should have same value
      , datestamp: { type: Number
                   , default: function () { return Math.floor(Date.now() / oneday); }
                   }
      });
      
      
      schema.statics.getLastWeek = function(name, fn) {
          var oneweekago = Date.now() - (7 * oneday);
      
          ret = this.collection.group({
                // Group by this key. One document per unique datestamp is returned.
                key: "datestamp"
                // Seed document for each group in result array.
              , initial: { "createdAt": 0 }
                // Update seed document if more recent document found.
              , reduce: function(doc, prev) {
                    if (doc.createdAt > prev.createdAt) {
                      prev.createdAt = doc.createdAt;
                      prev.score = doc.score;
      
                      // Add other fields, if desired:
                      prev.name = doc.name;
                    }
                // Process only documents created within past seven days
              , condition: { "createdAt" : {"$gt": oneweekago} }
              }});
      
         return ret.retval;
      
         // Note ret, the result of group() has other useful fields like:
         // total "count" of documents,
         // number of unique "keys",
         // and "ok" is false if a problem occurred during group()
      
      );
      

      【讨论】:

      • 感谢这本书,非常棒。但是,我使用您的示例不断收到ret undefined 错误,并发现猫鼬根据this article 以不同的方式执行此操作(对于任何寻找答案的人)
      【解决方案3】:

      试试这样的:

      schema.statics.getLastWeek = function(name, fn) {
        var oneday = 60 * 60 * 24
          , now = Date.now()
          , docs = []
         , that = this
      
        function getDay(day){
           that.where('name', new RegExp(name, 'i'))
           .where('createdAt')
           .gte(now - (day * oneday))
           .desc('createdAt')
           .findOne(function(err,doc){
           docs.push(doc)
          })
      
        }
      
        for (var i = 1; i <= 7; i++) {
          getDay(i);
        }
      }
      

      【讨论】:

        【解决方案4】:

        一种解决方案是使用 group() 按天对记录进行分组。它很花哨、很慢并且可能会阻塞(意味着没有其他东西可以同时运行),但如果你的记录集不是太大,它非常强大。

        群组:http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

        至于 mongoose,我不确定它是否直接支持 group(),但您可以使用 node-mongodb-native 实现,通过执行以下操作(主要是伪代码):

        schema.statics.getLastWeek = function(name, cb) {
            var keys = {} // can't remember what this is for
            var condition = {} // maybe restrict to last 7 days
            var initial = {day1:[],day2:[],day3:[],day4:[],day5:[],day6:[],day7:[]}
            var reduce = function(obj, prev) {
                // prev is basically the same as initial (except with whatever is added)
                var day = obj.date.slice(0,-10) // figure out day, however it works
                prev["day" + day].push(obj) // create grouped arrays
                // you could also do something here to sort by _id
                // which is probably also going to get you the latest for that day 
                // and use it to replace the last item in the prev["day" + 1] array if
                // it's > that the previous _id, which could simplify things later
            }
            this.collection.group(keys, condition, initial, reduce, function(err, results) {
              // console.log(results) 
              var days = results // it may be a property, can't remember
              var lastDays = {}
              days.forEach(function(day) {
                  // sort each day array and grab last element
        
                  lastDays[day] = days[day].sort(function(a, b) {
                    return a.date - b.date // check sort syntax, you may need a diff sort function  if it's a string
                  }).slice(-1) // i think that will give you the last one
              })
              cb(lastDays) // your stuff
            })
        }
        

        我的博客中的组和 map reduce 之间的更多比较: http://j-query.blogspot.com/2011/06/mongodb-performance-group-vs-find-vs.html

        本机驱动程序中没有关于 group 命令的文档,因此您必须在此处查看源代码: https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js

        也用于排序,请检查https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort 以了解确切的语法

        编辑:更好的主意!!!

        只需有一个名为“lastRequestOfDay”的特殊集合并将_id 设为当天。 用每个新请求覆盖该值。它将非常容易查询和快速编写,并且每天都会写入最后一个值!

        【讨论】:

          【解决方案5】:

          将另一个属性添加到名为 dateAdded 或其他名称的架构中。

          schema.statics.getLastWeek = function(name, fn) {
          var oneday = 60 * 60 * 24
            , now = Date.now()
            , docs = []
          
          for (var i = 0; i < 7; i++) {
            this.where('name', new RegExp(name, 'i'))
            .where('createdAt')
            .lt(now - (i * oneday))
            .gte(now - ((i + 1) * oneday))
            .desc('createdAt')
            .findOne(function(err,doc){
              // might not always find one
              docs.push(doc)
            })
          }
          return fn(null, docs)
          }
          

          【讨论】:

          • 对不起。忘记添加 lt 子句。您还应该编辑您的架构,将函数Date.now 传递到默认值createdAt。您目前正在做的是传递Date.now() 的结果,这将使您的所有文档都具有相同的createdAt 字段。
          • 这个不行,还是返回了7次相同的记录,甚至不是当天最新的记录...
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-09
          • 1970-01-01
          • 2014-03-08
          相关资源
          最近更新 更多