2012-07-03 9 views
7

Digamos que tengo un sitio web como digg.com Tengo un montón de artículos y la gente puede votar sobre los artículos que les gusta.MongoDB Schema Design - Votando en las publicaciones

Quiero poder consultar para obtener los artículos con más votos dentro de un tiempo determinado (última hora, último día, la semana pasada) ordenado por el número de votos.

Como es habitual en MongoDB, hay varias formas de implementar esto, pero no estoy seguro de cuál es el correcto.

  • Un documento de mensajes, que contiene una matriz de Votos - Votos en sí son documentos que contienen ID de usuario, nombre de usuario y la fecha de la votación:
{ 
    "_id": "ObjectId(xxxx)", 
    "title": "Post Title", 
    "postdate": "21/02/2012+1345", 
    "summary": "Summary of Article", 

    "Votes": [ 
     { 
      "userid":ObjectId(xxxx), 
      "username": "Joe Smith", 
      "votedate": "03/03/2012+1436" 
     }, 
      ] 
    } 
  • Una colección votaciones separadas, que contienen detalles de un individuo voto y una referencia al puesto que fue votado en:
{ 
    "_id": "ObjectId(xxxx)", 
    "postId": ObjectId(xxxx), 
    "userId": ObjectId(xxxx), 
    "votedate": "03/03/2012+1436" 
} 

El primero es más Documentey pero no tengo idea de cómo consultar el conjunto de votos para obtener los documentos con más votos en las últimas 24 horas.

Me estoy inclinando hacia el segundo, ya que sería más fácil consultar el número de votos agrupados por votación, creo, pero no estoy seguro de qué tan bien funcionaría. Así es como lo harías en las bases de datos relacionales, pero no parece muy documentado, pero no estoy seguro si es un problema, ¿o sí?

¿O utilizo una combinación de los dos? También haría este tipo de consulta agregada en tiempo real, cada carga de página. ¿O acabo de ejecutar la consulta decir una vez por minuto y almacenar los resultados en una colección de resultados de la consulta?

¿Cómo implementaría este esquema?

+0

pregunta relacionada: http://stackoverflow.com/questions/ 9296793/eficiente de documentos de formato-a-Store-votos-en-mongo-db –

+0

también relacionado: http://stackoverflow.com/questions/7046462/best-way-to-model-a-voting-system-in -mongodb – wmassingham

Respuesta

9

La forma más común de realizar un seguimiento de los recuentos de votos en general sería mantener el número de votos en el documento de correos y actualizarlos atómicamente cuando se empuja un nuevo valor a la matriz de votos.

Como se trata de una única actualización, tiene la garantía de que el recuento coincidirá con la cantidad de elementos del conjunto.

Si el número de agregaciones es fijo y el sitio está muy ocupado, podría ampliar este paradigma e incrementar contadores adicionales, como uno por mes, día y hora, pero eso podría salirse de control muy rápidamente. Por lo tanto, podría usar el nuevo (disponible en la versión 2.1.2 dev, estará en producción en la versión 2.2. Es más fácil de usar que Map/Reduce y le permitirá hacer los cálculos que desee de manera muy sencilla, especialmente si tenga cuidado de almacenar sus fechas de votación como ISODate() tipo.

tubería típica para consulta de agregación para más votados este mes podría ser algo como esto:

today = new Date(); 
thisMonth = new Date(today.getFullYear(),today.getMonth()); 
thisMonthEnd = new Date(today.getFullYear(),today.getMonth()+1); 

db.posts.aggregate([ 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$unwind: "$Votes" }, 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$group: { _id: "$title", votes: {$sum:1} } }, 
    {$sort: {"votes": -1} }, 
    {$limit: 10} 
]); 

Esto limita la entrada a la tubería de mensajes que tienen votos, haciendo coincidir las fechas de votación para el mes que usted está contando , "desenrolla" el conjunto para obtener un documento por voto y luego hace un "grupo por" equivalente sumando todos los votos para cada título (supongo que el título es único). Luego ordena descendiendo por número de votos y limita la salida a los primeros diez.

También tienen la capacidad de agregar votos por día (por ejemplo) para ese mes para ver qué días son los más activos para la votación:

db.posts.aggregate([ 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$unwind: "$Votes" }, 
    {$match: { "Votes.votedate": {$gte:thisMonth, $lt:thisMonthEnd} } }, 
    {$project: { "day" : { "$dayOfMonth" : "$Votes.votedate" } } }, 
    {$group: { _id: "$day", votes: {$sum:1} } }, 
    {$sort: {"votes": -1} }, 
    {$limit: 10} 
]); 
+0

si termina almacenando votos en su propia colección en lugar de insertarlos en publicaciones, entonces no necesitaría el paso "$ unwind", el resto de la agregación se mantendría básicamente igual. –

+0

tenga en cuenta que no es un accidente que $ $ coincidan dos veces con el mes deseado. First $ Match se deshace de las publicaciones que no tienen ningún voto en el mes deseado, pero el segundo $ match (después de $ unwind) se asegura de que solo conservemos los votos que ocurrieron en ese mes antes de que los contabilicemos. El primer $ match está ahí para reducir el número total de documentos que estamos ingresando a la cartera, no es estrictamente necesario, excepto por el rendimiento. –

+0

Estoy tratando de usar la solución para documentos referenciados, pero no está funcionando. En mi prueba, tengo 6 publicaciones, y solo una de ellas tiene un voto único. Todos los demás no tienen registros en la colección 'posts_votes'. Si ejecuto '{$ group: {_id:" $ votes.post_id ", votes: {$ sum: 1}}}' obtengo un registro con un _id de nulo. Si cambio '$ votes.post_id' a' $ title', devuelve las 6 publicaciones cada una con un solo voto (debería ser solo una publicación con un voto, todas las demás tienen cero). También lo intenté con la adición de la matriz '$ project' que crea una variable usando' $ votes.post_id' – Nathan

0

El esquema que elija depende en gran medida de su caso de uso ... Si espera una gran cantidad de votos/comentarios y desea procesarlos independientemente de la publicación a la que pertenecen, puede mantenerlos en una colección separada con ID posterior como la "clave foriegn" .. Sin embargo, si desea cargar todos los votos cuando carga una publicación en particular y los votos en sí mismos no tienen ningún significado sin la publicación que los alberga, entonces vaya a la inserción (en su caso, el primer) enfoque.

+0

Usted puede tratar de MapReduce con el enfoque más documentey para consultar la califican de matriz para obtener los documentos con el mayor número de votos en las últimas 24 horas ... Desde mapreduce pasa a ser una operación pesada, es mejor para ejecutarlo sólo ocasionalmente y usa resultados almacenados en caché –

Cuestiones relacionadas