Найти последний документ дня за последние 7 дней - PullRequest
2 голосов
/ 29 декабря 2011

Я добавляю записи в схему каждый час, чтобы отслеживать рост в течение нескольких дней, сохраняя текущий счет за текущий день.Теперь я хотел бы иметь возможность получить самую последнюю запись за каждый день за прошедшую неделю.Результатом будет 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 и присоединив его кмой основной запрос, чтобы получить результаты, которые я хочу.В любом случае, чтобы сделать это здесь?

Ответы [ 5 ]

6 голосов
/ 07 января 2012

Кристина Ходоров дает подробный рецепт для этого точного задания в своей книге MongoDB: Полное руководство :

Предположим, у нас есть сайт, который отслеживает цены на акции. Каждые несколько минут с 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()

);
1 голос
/ 03 января 2012

Решением является использование 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
    })
}

В моем блоге приведено еще несколько сравнений между группами и картой: http://j -query.blogspot.com / 2011/06 / MongoDB-производительность группы VS-найти-vs.html

В родном драйвере нет документов по групповой команде, поэтому вам придется просмотреть исходный код здесь: 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 днем. Перезаписывайте значение при каждом новом запросе. Это будет очень легко запрашивать и быстро писать и всегда будет иметь последнее записанное значение каждый день!

0 голосов
/ 07 января 2012

Никто, кажется, не пытается "близко к полуночи". :) Проблема с исходным кодом, которую я видел, заключалась в том, что он проверялся на время, большее или равное x дней назад ... которое всегда будет возвращать самое последнее время. Я не понимаю, почему решение DeaDEnD возвращает одну и ту же запись 7 раз. Кроме того, вы никогда не звонили fn, но это не самая большая проблема, не так ли?

Попробуйте добавить .lt(now - (now % oneday) - (i - 1) * oneday) (при условии 0-индексирования; если он 1-индексированный, измените это на i - 2)

0 голосов
/ 07 января 2012

Попробуйте что-то вроде этого:

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);
  }
}
0 голосов
/ 29 декабря 2011

Добавьте другое свойство в схему с именем 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)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...