2011-08-16 18 views
6

* Básicamente estoy tratando de ordenar objetos por su puntuación en la última hora.MongoDB MapReduce actualización en el lugar cómo

Estoy tratando de generar una suma de votos por hora para los objetos en mi base de datos. Los votos están integrados en cada objeto. El esquema de objetos se ve así:

{ 
    _id: ObjectId 
    score: int 
    hourly-score: int <- need to update this value so I can order by it 
    recently-voted: boolean 
    votes: { 
     "4e4634821dff6f103c040000": { <- Key is __toString of voter ObjectId 
      "_id": ObjectId("4e4634821dff6f103c040000"), <- Voter ObjectId 
      "a": 1, <- Vote amount 
      "ca": ISODate("2011-08-16T00:01:34.975Z"), <- Created at MongoDate 
      "ts": 1313452894 <- Created at timestamp 
     }, 
     ... repeat ... 
    } 
} 

Esta pregunta está realmente relacionado con una pregunta que hice un par de días atrás Best way to model a voting system in MongoDB

¿Cómo puedo (puedo?) Ejecutar un comando MapReduce para hacer lo siguiente :

  1. sólo se ejecutan en objetos con votó recientemente-= true o por hora-score> 0.
  2. Calcular la suma de los votos creados en la última hora.
  3. Actualizar puntuación por hora = la suma calculada anteriormente, y recientemente votada = falsa.

También leí here que puedo realizar una MapReduce en el esclavo DB ejecutando db.getMongo(). SetSlaveOk() antes del comando M/R. ¿Podría ejecutar el reductor en un esclavo y actualizar el DB maestro?

¿Son posibles las actualizaciones in situ con Mongo MapReduce?

Respuesta

10

Definitivamente puede hacer esto. Voy a dirigir sus preguntas de a una por vez:

1. Puede especificar una consulta junto con su map-reduce, que filtra el conjunto de objetos que se pasarán a la fase del mapa. En la consola mongo, esto se vería así (asumiendo m y r son los nombres de las funciones del asignador y reductor, respectivamente):

> db.coll.mapReduce(m, r, {query: {$or: [{"recently-voted": true}, {"hourly-score": {$gt: 0}}]}}) 

2. Paso # 1 le permitirá utilizar el asignador de todos los documentos con al menos un voto en la última hora (o con recently-voted establecido en verdadero), pero no todos los votos habrán sido en la última hora. Por lo que usted necesita para filtrar la lista en su mapeador, y sólo emitirá esos votos que desea contar:

function m() { 
    var hour_ago = new Date() - 3600000; 
    this.votes.forEach(function (vote) { 
    if (vote.ts > hour_ago) { 
     emit(/* your key */, this.vote.a); 
    } 
    }); 
} 

Y para reducir:

function r(key, values) { 
    var sum = 0; 
    values.forEach(function(value) { sum += value; }); 
    return sum; 
} 

3. Para actualizar la tabla de las cuentas por hora , puede usar la opción reduceOutput para map-reduce, que llamará a su reductor tanto con los valores emitidos como con el valor previamente guardado en la colección de salida (si corresponde). El resultado de ese pase se guardará en la colección de salida. Esto se parece a:

> db.coll.mapReduce(m, r, {query: ..., out: {reduce: "output_coll"}}) 

Además de volver a reducir la producción, puede utilizar merge que sobrescribirá documentos en la colección de salida con los de nueva creación (pero dejando tras de sí ningún documento con un _id diferente a los _id s creados por su trabajo de mr), replace, que es efectivamente una operación de soltar y crear y es el valor predeterminado, o use {inline: 1}, que devolverá los resultados directamente al shell o al controlador. Tenga en cuenta que al usar {inline: 1}, sus resultados deben encajar en el tamaño permitido para un solo documento (16 MB en versiones recientes de MongoDB). (4.) Puede ejecutar trabajos de reducción de mapa en secundarios ("esclavos"), pero como los secundarios no pueden aceptar escrituras (eso es lo que los hace secundarios), solo puede hacer esto al usar salida en línea.

+1

Ok, todo tiene sentido hasta el paso 3. No entiendo muy bien cómo tomar la suma reducida de puntaje por hora y actualizar los objetos relevantes en la colección. Supongamos que tenemos una colección de comentarios con el esquema en mi pregunta y me gustaría actualizar todos los puntajes por hora de comentarios relevantes a las nuevas sumas reducidas. El puntaje por hora es un parámetro en objetos de comentario, no algo en una colección separada. ¿Como podría hacerlo? – Marc

+0

Ah, ya veo, ¿desea actualizar el campo 'hourly-score' para que sea la suma de las cantidades de votos (campo' votes.a') con la salida del mapa-reduce? No creo que puedas hacer esto en línea con map-reduce, pero definitivamente puedes hacer un paso de post-procesamiento que use la salida map-reduce para actualizar cualquier colección que desees. – dcrosta

+0

Hmm ok. Desde una base de datos relacional, esta es una tarea bastante simple. Básicamente, todo lo que me gustaría hacer es ordenar los objetos por puntuación en la última hora. En un relacional, mantendría los puntajes en una tabla separada. Luego selecciono de la tabla de objetos, unir tabla de puntajes, sumar los puntajes en la última hora y ordenar por esa suma. Quizás cualquiera a. mongo simplemente no es bueno para esto o b. ¿Organizo mis objetos/votos incorrectamente? – Marc

Cuestiones relacionadas