2012-05-12 7 views
41

Tengo problemas para entender este ejemplo completo desde the docs ... Intenté ejecutarlo de varias maneras para poder observar cómo funciona, etc.¿Cómo funciona el ejemplo de conteo de mensajes en Meteor?

¿Cómo se suscribe a esto? ¿Podemos incluir el código del lado del cliente necesario para que esto funcione?

¿Hay una colección llamada messages-count? ¿Es Room una colección de mensajes? ¿Podemos incluir las definiciones de colección en el ejemplo?

¡Cualquier consejo sobre esto sería genial!

NOTA: este es el código tal como apareció cuando esta pregunta se publicó inicialmente (mayo de 2012). Es más simple ahora.

// server: publish the current size of a collection 
Meteor.publish("messages-count", function (roomId) { 
    var self = this; 
    var uuid = Meteor.uuid(); 
    var count = 0; 

    handle = Room.find({room_id: roomId}).observe({ 
    added: function (doc, idx) { 
     count++; 
     self.set("messages-count", uuid, "count", count); 
     self.flush(); 
    }, 
    removed: function (doc, idx) { 
     count--; 
     self.set("messages-count", uuid, "count", count); 
     self.flush(); 
    } 
    // don't care about moved or changed 
    }); 

    // remove data and turn off observe when client unsubs 
    self.onStop(function() { 
    handle.stop(); 
    self.unset("messages-count", uuid, "count"); 
    self.flush(); 
    }); 
}); 

Respuesta

51

Gracias por pedirme que escriba una explicación más clara. Aquí hay un ejemplo más completo con mis comentarios. Hubo algunos errores e inconsistencias que he limpiado. La próxima versión de documentos usará esto.

Meteor.publish es bastante flexible. No se limita a publicar colecciones existentes de MongoDB para el cliente: podemos publicar todo lo que queramos. Específicamente, Meteor.publish define un conjunto de documentos a los que un cliente puede suscribirse. Cada documento pertenece a algún nombre de colección (una cadena), tiene un campo único _id y luego tiene algún conjunto de atributos JSON. A medida que cambian los documentos en el conjunto, el servidor enviará los cambios a cada cliente suscrito, manteniendo al cliente actualizado.

Vamos a definir un conjunto de documentos aquí, llamado "counts-by-room", que contiene un único documento en una colección llamada "counts". El documento tendrá dos campos: un roomId con el ID de una habitación, y count: el número total de mensajes en esa habitación. No hay una colección real de MongoDB llamada counts. Este es solo el nombre de la colección que nuestro servidor Meteor enviará al cliente y que se almacenará en una colección del lado del cliente llamada counts.

Para hacer esto, nuestra función de publicación toma un parámetro roomId que vendrá del cliente, y observa una consulta de todos los mensajes (definidos en otra parte) en esa habitación. Podemos utilizar la forma más eficiente observeChanges de observar aquí una consulta, ya que no necesitaremos el documento completo, solo el conocimiento de que se ha agregado o eliminado uno nuevo. Cada vez que se agrega un nuevo mensaje con el roomId que nos interesa, nuestra devolución de llamada incrementa el recuento interno y luego publica un nuevo documento para el cliente con ese total actualizado. Y cuando se elimina un mensaje, disminuye el recuento y envía al cliente la actualización.

Cuando llamemos por primera vez al observeChanges, se ejecutará un número de added devoluciones de llamada de inmediato, por cada mensaje que ya exista. Luego, los cambios futuros se activarán cada vez que se agreguen o eliminen mensajes.

Nuestra función de publicación también registra un controlador onStop para limpiar cuando el cliente cancela la suscripción (ya sea manualmente o al desconectarse). Este controlador elimina los atributos del cliente y elimina la ejecución observeChanges.

Una función de publicación se ejecuta cada vez que un nuevo cliente se suscribe a "counts-by-room", por lo que cada cliente tendrá un observeChanges ejecutándose en su nombre.

// server: publish the current size of a collection 
Meteor.publish("counts-by-room", function (roomId) { 
    var self = this; 
    var count = 0; 
    var initializing = true; 

    var handle = Messages.find({room_id: roomId}).observeChanges({ 
    added: function (doc, idx) { 
     count++; 
     if (!initializing) 
     self.changed("counts", roomId, {count: count}); // "counts" is the published collection name 
    }, 
    removed: function (doc, idx) { 
     count--; 
     self.changed("counts", roomId, {count: count}); // same published collection, "counts" 
    } 
    // don't care about moved or changed 
    }); 

    initializing = false; 

    // publish the initial count. `observeChanges` guaranteed not to return 
    // until the initial set of `added` callbacks have run, so the `count` 
    // variable is up to date. 
    self.added("counts", roomId, {count: count}); 

    // and signal that the initial document set is now available on the client 
    self.ready(); 

    // turn off observe when client unsubscribes 
    self.onStop(function() { 
    handle.stop(); 
    }); 
}); 

Ahora, en el cliente, podemos tratar esto como una suscripción típica de Meteor. Primero, necesitamos un Mongo.Collection que contendrá nuestro documento de cuentas calculadas. Como el servidor está publicando en una colección llamada "counts", pasamos "counts" como el argumento al constructor Mongo.Collection.

// client: declare collection to hold count object 
Counts = new Mongo.Collection("counts"); 

Luego, podemos suscribirnos. (En realidad, puede suscribirse antes de declarar la recopilación: Meteor pondrá en cola las actualizaciones entrantes hasta que haya un lugar donde ubicarlas). El nombre de la suscripción es "counts-by-room" y requiere un argumento: el ID de la habitación actual. Envolví esto dentro de Deps.autorun para que como Session.get('roomId') cambie, el cliente se dará de baja automáticamente del recuento de la habitación anterior y volverá a suscribirse al recuento de la nueva sala.

// client: autosubscribe to the count for the current room 
Tracker.autorun(function() { 
    Meteor.subscribe("counts-by-room", Session.get("roomId")); 
}); 

Por último, tenemos el documento en Counts y podemos utilizarlo como cualquier otra colección Mongo en el cliente. Cualquier plantilla que haga referencia a estos datos se volverá a dibujar automáticamente cada vez que el servidor envíe un nuevo recuento.

// client: use the new collection 
console.log("Current room has " + Counts.findOne().count + " messages."); 
+2

Borrar as bell! ¡Muchas gracias por tomarse el tiempo de aclarar esto para mí! –

+2

Tenga en cuenta que 'self.flush();' dentro de 'added' empujará esa suscripción al cliente a medida que se rellena la colección. Imagine que tiene 1,000,000 de "Mensajes" en ese "room_id". Se le enviarán 1,000,000 de suscripciones desde el recuento 1 y hasta el recuento de 1,000,000. ¡Esto bloqueará tu navegador por bastante tiempo! Sin mencionar la cantidad de datos volando sobre el cable ... – matb33

+0

@ matb33, ¿hay una mejor solución para el problema de descarga? –

0

acaba de encontrar una solución al problema en el que self.flush() es el envío de miles de actualizaciones al cliente - sólo tiene que utilizar _.debounce cuando se cuentan:

count = 0 
throttled_subscription = _.debounce => 
    @set 'items-count', uuid, count: count 
    @flush() 
, 10 
handle = Items.find(selector).observe 
    added: => 
    count++ 
    throttled_subscription() 
    removed: => 
    count-- 
    throttled_subscription() 

Esto sólo fijará el recuento y purgar la suscripción después de 10 ms sin cambios.

Gracias a @possibilities en #meteor por la pista.

+1

La desventaja de esta solución es que el meteorito descarga toda la colección en el servidor, por lo que si está utilizando una conexión remota relativamente lenta a su mongoDB, habrá un retraso notable después de que se inicie la aplicación (al menos si tiene 10k documentos en tu DB me gusta) –

+1

encontró una solución - https://gist.github.com/3925008 –

2

Como dijo Leonhardt Wille, la desventaja de esta solución es que el meteorito descarga toda la colección de elementos del servidor de Mongo solo para contarlos. Su solución en gist.github.com/3925008 es mejor, pero el contador no se actualizará cuando se inserten nuevos elementos.

Aquí está mi solución reactiva

Colecciones:

Players = new Meteor.Collection("players"); 
PlayersCounts = new Meteor.Collection("players_counts") 

Servidor:

Meteor.publish("players_counts", function(){ 
    var uuid = Meteor.uuid() 
    var self = this; 

    var unthrottled_setCount = function(){ 
     cnt = Players.find({}).count() 
     self.set("players_counts", uuid, {count: cnt}) 
     self.flush() 
    } 

    var setCount = _.throttle(unthrottled_setCount, 50) 

    var handle = Meteor._InvalidationCrossbar.listen({collection: "players"}, function(notification, complete){ 
     setCount(); 
     complete(); 
    }) 

    setCount(); 
    self.complete() 
    self.flush() 

    self.onStop(function(){ 
     handle.stop(); 
     self.unset("players_counts", uuid, ["count"]); 
     self.flush(); 
    }); 
}); 

Cliente:

Meteor.subscribe("players_counts") 

Template.leaderboard.total = function(){ 
    var cnt = PlayersCounts.findOne({}) 
    if(cnt) { 
     return cnt.count; 
    } else { 
     return null; 
    } 
} 
+0

A partir de Meteor 0.6.6.3 (quizás antes) este código falla: 'Excepción de sub CfuTiQGacmWo5xMsb TypeError: No se puede llamar al método 'escuchar' de indefinido' – russellfeeed

+1

Sólo FYI, este es el código pre Meteor 0.6. Ver @debergalis 'respuesta actualizada anterior. –

Cuestiones relacionadas