2011-06-27 25 views
9

Utilizo mongodb para la plataforma de mi blog, donde los usuarios pueden crear sus propios blogs. Todas las entradas de todos los blogs están en una colección de entradas. El documento de una entrada se parece a:Mongodb: Seleccione las primeras N filas de cada grupo

{ 
    'blog_id':xxx, 
    'timestamp':xxx, 
    'title':xxx, 
    'content':xxx 
} 

Como dice la pregunta, ¿hay alguna manera de seleccionar, por ejemplo, 3 últimas entradas para cada blog?

Respuesta

1

La única manera de hacer esto en mongo básica si se puede vivir con dos cosas:

  • un campo adicional en su documento de entrada, vamos a llamarlo "edad"
  • Una nueva entrada de blog de tomar una actualización adicional

Si es así, así es como se hace:

  1. Tras la creación de un ne w introduzca su inserción normal y luego ejecute esta actualización para aumentar la antigüedad de todas las publicaciones (incluida la que acaba de insertar para este blog):

    db.entries.update ({ID_blog: BLOG_ID}, {edad: { $ inc: 1}}, false, true)

  2. al consultar, utilizar la siguiente consulta que devolverá los más recientes 3 entradas para cada blog:

    db.entries.find ({edad: {$ LTE: 3}, fecha y hora: {$ GTE: STARTOFMONTH, $ lt:. EndOfMonth}}) tipo ({blog_id: 1, edad: 1})

Tenga en cuenta que esta solución es realmente concurrente segura (no hay entradas con edades duplicadas).

+0

Entendemos. No pensé en nada de eso. Una actualización adicional al crear una nueva publicación no sería un problema. Sin embargo, cuando el usuario elimine una publicación, tendremos que actualizar el campo "edad" de todas las demás publicaciones. Esa actualización puede limitarse solo cuando la publicación eliminada tenga 'age' <= 3. ¿Me estoy perdiendo algo? – Tacaza

+0

Sí, no debe limitar esa actualización a la edad <3 porque terminará con edades duplicadas. Las actualizaciones in situ son extremadamente rápidas, por lo que no deberían ser un problema. Una eliminación significa eliminar la entrada y disminuir la edad en 1, donde edad> deleted_post.age. Buena suerte. –

+0

Eso tiene sentido. ¡Gracias por tu sugerencia! – Tacaza

0

Es posible con el grupo (agregación), pero esto creará un escaneo de tabla completa.

¿Realmente necesita exactamente 3 o puede establecer un límite ... por ejemplo: máx. 3 publicaciones de la última semana/mes?

+0

Idealmente deseo seleccionar exactamente 3, pero un máximo de 3 mensajes desde el mes pasado puede ser lo suficientemente bueno si no puedo encontrar una solución a excepción desnormalización de datos. ¿Podría darme un ejemplo de cómo se puede hacer esto, por favor? De todos los mapas del mongodb, reduzco los tutoriales que he leído, solo muestran cómo calcular estadísticas (agregación) ... – Tacaza

1

Esta respuesta utilizando MapReduce por drcosta de otra pregunta hizo el truco

In mongo, how do I use map reduce to get a group by ordered by most recent

mapper = function() { 
    emit(this.category, {top:[this.score]}); 
} 

reducer = function (key, values) { 
    var scores = []; 
    values.forEach(
    function (obj) { 
     obj.top.forEach(
     function (score) { 
      scores[scores.length] = score; 
     }); 
    }); 
    scores.sort(); 
    scores.reverse(); 
    return {top:scores.slice(0, 3)}; 
} 

function find_top_scores(categories) { 
    var query = []; 
    db.top_foos.find({_id:{$in:categories}}).forEach(
    function (topscores) { 
     query[query.length] = { 
     category:topscores._id, 
     score:{$in:topscores.value.top} 
     }; 
    }); 
    return db.foo.find({$or:query}); 
8

Es necesario ordenar primero los documentos de la colección por los campos blog_id y timestamp, y luego hacer una inicial grupo que crea una matriz de documentos originales en orden descendente. Después de eso, puede cortar la matriz con los documentos para devolver los primeros 3 elementos.

La intuición puede ser seguido en este ejemplo:

db.entries.aggregate([ 
    { '$sort': { 'blog_id': 1, 'timestamp': -1 } }, 
    {  
     '$group': { 
      '_id': '$blog_id', 
      'docs': { '$push': '$$ROOT' }, 
     } 
    }, 
    { 
     '$project': { 
      'top_three': { 
       '$slice': ['$docs', 3] 
      } 
     } 
    } 
]) 
+0

gracias, brillante –

Cuestiones relacionadas