javascript - varios - Encuentra el último documento del día durante los últimos 7 días
subconsultas en mongodb (5)
Agregue otra propiedad al esquema llamado dateAdded
o algo así.
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)
}
Estoy agregando entradas a un esquema cada hora con el fin de rastrear el crecimiento en el transcurso de los días, manteniendo un puntaje actual para el día actual. Ahora me gustaría poder obtener el registro más reciente de cada día de la semana pasada. Los resultados serían 6 registros alrededor de la medianoche durante 6 días previos y el 7mo será el más reciente para el día actual.
Aquí está mi esquema:
var schema = new Schema({
aid: { type: Number }
, name: { type: String }
, score: { type: Number }
, createdAt: { type: Date, default: Date.now() }
})
Editar
Intenté usar esta estática, pero saca exactamente el mismo registro 7 veces
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)
})
}
}
Si estuviera usando SQL, haría una subconsulta seleccionando MAXDATE y unirla a mi consulta principal para recuperar los resultados que deseo. De todos modos para hacer esto aquí?
Una solución es usar grupos () para agrupar registros por día. Es elegante, lento y puede bloquear (es decir, nada más se puede ejecutar al mismo tiempo), pero si el conjunto de registros no es demasiado grande, es bastante potente.
Grupo: http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Group
En cuanto a la mangosta, no estoy seguro de si admite group () directamente, pero puede usar la implementación node-mongodb-native, haciendo algo como esto (pseudo-código, en su mayoría):
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
})
}
Algunas comparaciones más entre grupos y mapa se reducen desde mi blog: http://j-query.blogspot.com/2011/06/mongodb-performance-group-vs-find-vs.html
No hay documentos sobre el comando de grupo en el controlador nativo, por lo que tendrá que leer el código fuente aquí: https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/ collection.js
También para ordenar, verifique https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort para obtener una sintaxis exacta
Editar: ¡Mejor idea!
Solo tiene una colección especial llamada "lastRequestOfDay" y haga que _id el día. Sobrescriba el valor con cada nueva solicitud. ¡Será súper fácil de consultar y rápido de escribir y siempre tendrá el último valor escrito cada día!
Nadie parece estar intentando "cerca de la medianoche". :) El problema que vi con el código original fue que se verificó por un tiempo mayor o igual a x días atrás ... que siempre devolverá la hora más reciente. Sin embargo, estoy confundido sobre por qué la solución de DeaDEnD devuelve el mismo registro 7 veces. Además, nunca fn
, pero esa no es realmente la mayor de tus preocupaciones, ¿o sí?
Intente agregar .lt(now - (now % oneday) - (i - 1) * oneday)
(suponiendo 0-indexado; si está indexado en 1, cámbielo para decir i - 2
)
Pruebe algo como esto:
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);
}
}
Supongamos que tenemos un sitio que realiza un seguimiento de los precios de las acciones. Cada pocos minutos, de 10 a.m. a 4 p.m., obtiene el último precio de una acción que almacena en MongoDB. Ahora, como parte de una aplicación de informes, queremos encontrar el precio de cierre de los últimos 30 días. Esto se puede lograr fácilmente usando un grupo.
No estoy familiarizado con Mongoose, sin embargo, he tratado de adaptar su ejemplo a su caso a continuación. Tenga en cuenta que cambié la propiedad createdAt
default
de un valor a una función y agregué una fecha de campo datestamp
a su esquema:
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()
);