2011-12-29 5 views
2

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

ответ

6

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

Предположим, что у нас есть сайт, который отслеживает цены на акции. Каждые несколько минут от 10 до 4 часов, он получает самую последнюю цену за акции, , которую он хранит в MongoDB. Теперь, в рамках приложения для отчетности, , мы хотим найти цену закрытия за последние 30 дней. Это может быть легко выполняется с помощью группы.

Я не знаком с Mongoose, однако я попытался приспособить ее пример к вашему случаю ниже. Обратите внимание, я изменил свойство createdAtdefault от значения к функции и добавил дополнительное поле 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() 

); 
+0

Thanx для книги, отлично читал. Тем не менее, я продолжаю получать неопределенную ошибку 'ret', используя ваш пример, и обнаружил, что мангуста делает это по-другому в соответствии с [этой статьей] (http://wmilesn.com/2011/07/code/how-to-map-reduce -with-mongoose-mongodb-express-node-js /) (для любого, кто ищет ответ) –

0

Добавить другое свойство в схему с именем 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) 
} 
+0

я пытался что-то подобное, но не работает, см мой выбор выше –

+0

Ой извините. Забыл добавить предложение 'lt'. И вы также должны отредактировать передачу вашей схемы в функции 'Date.now' до значения по умолчанию' createdAt'. То, что вы делаете в настоящее время, - это передача результата 'Date.now()', который сделает все ваши документы одинаковыми поля 'createdAt'. – fent

+0

это не работает, он все равно возвращает ту же запись 7 раз, и это даже не последняя запись дня ... –

1

Решение состоит в том, чтобы использовать группу() для группировки записей по дням. Это причудливый, медленный и может быть блокирующим (что означает, что ничто другое не может работать одновременно), но если ваш набор записей не слишком велик, он довольно мощный.

Группа: http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group

Что касается мангуста, я не уверен, если он поддерживает группу() напрямую, но вы можете использовать узел-MongoDB-родной реализации, делая что-то вроде этого (псевдо-код, в основном):

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-performance-group-vs-find-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

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

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

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

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

Смежные вопросы