unam tipos teoria operaciones libro entre ejercicios ejemplos conjuntos clasificacion collections meteor publish-subscribe

collections - tipos - teoria de conjuntos pdf unam



Publicación/suscripción de subconjuntos múltiples de la misma colección de servidores (3)

¿No podría simplemente usar la misma consulta del lado del cliente cuando quiera mirar los artículos?

En un directorio lib:

enabledItems = function() { return Items.find({enabled: true}); } processedItems = function() { return Items.find({processed: true}); }

En el servidor:

Meteor.publish(''enabled_items'', function() { return enabledItems(); }); Meteor.publish(''processed_items'', function() { return processedItems(); });

En el cliente

Meteor.subscribe(''enabled_items''); Meteor.subscribe(''processed_items''); Template.enabledItems.items = function() { return enabledItems(); }; Template.processedItems.items = function() { return processedItems(); };

Si lo piensas bien, es mejor así como si insertas (localmente) un elemento que está habilitado y procesado, puede aparecer en ambas listas (a diferencia de si tienes dos colecciones separadas).

NOTA

Me di cuenta de que estaba un poco confusa, así que amplié un poco esto, espero que ayude.

EDITAR: esta pregunta, algunas de las respuestas y algunos de los comentarios contienen mucha desinformación. Vea cómo las colecciones, publicaciones y suscripciones de Meteor funcionan para una comprensión precisa de la publicación y la suscripción a múltiples subconjuntos de la misma colección de servidores.

¿Cómo se puede publicar diferentes subconjuntos (o "vistas") de una sola colección en el servidor como múltiples colecciones en el cliente?

Aquí hay un pseudo código para ayudar a ilustrar mi pregunta:

colección de items en el servidor

Supongamos que tengo una colección de items en el servidor con millones de registros. Supongamos también que:

  1. 50 registros tienen la propiedad enabled establecida en true , y;
  2. 100 registros tienen la propiedad processed establecida en true .

Todos los demás están configurados como false .

items: { "_id": "uniqueid1", "title": "item #1", "enabled": false, "processed": false }, { "_id": "uniqueid2", "title": "item #2", "enabled": false, "processed": true }, ... { "_id": "uniqueid458734958", "title": "item #458734958", "enabled": true, "processed": true }

Código del servidor

Publiquemos dos "vistas" de la misma colección de servidores. Uno enviará un cursor con 50 registros, y el otro enviará un cursor con 100 registros. Hay más de 458 millones de registros en esta base de datos ficticia del lado del servidor, y el cliente no necesita saber acerca de todos ellos (de hecho, enviarlos a todos probablemente llevaría varias horas en este ejemplo):

var Items = new Meteor.Collection("items"); Meteor.publish("enabled_items", function () { // Only 50 "Items" have enabled set to true return Items.find({enabled: true}); }); Meteor.publish("processed_items", function () { // Only 100 "Items" have processed set to true return Items.find({processed: true}); });

Codigo del cliente

Para admitir la técnica de compensación de latencia, nos vemos obligados a declarar una única colección de Items en el cliente. Debería hacerse evidente dónde se encuentra el defecto: ¿cómo se diferencia entre los Items para los Items enabled_items y los Items para los Items processed_items ?

var Items = new Meteor.Collection("items"); Meteor.subscribe("enabled_items", function () { // This will output 50, fine console.log(Items.find().count()); }); Meteor.subscribe("processed_items", function () { // This will also output 50, since we have no choice but to use // the same "Items" collection. console.log(Items.find().count()); });

Mi solución actual incluye el parche de monos _publishCursor para permitir el uso del nombre de la suscripción en lugar del nombre de la colección. Pero eso no hará ninguna compensación de latencia. Cada escritura tiene que ser ida y vuelta al servidor:

// On the client: var EnabledItems = new Meteor.Collection("enabled_items"); var ProcessedItems = new Meteor.Collection("processed_items");

Con el parche de mono en su lugar, esto funcionará. Pero acceda al modo fuera de línea y los cambios no aparecerán en el cliente de inmediato; tendremos que estar conectados al servidor para ver los cambios.

¿Cuál es el enfoque correcto?

EDITAR: Acabo de volver a visitar este hilo y me doy cuenta de que, tal como está, mis preguntas y respuestas y una plétora de comentarios llevan mucha información errónea.

Lo que se reduce a eso es que no entiendo bien la relación publicar-suscribir. Pensé que cuando publicabas un cursor, caía en el cliente como una colección separada de otros cursores publicados que se originaban en la misma colección de servidores. Esto simplemente no es como funciona. La idea es que tanto el cliente como el servidor tengan las mismas colecciones, pero es lo que está en las colecciones lo que difiere. Los contratos pub-sub negocian qué documentos terminan en el cliente. La respuesta de Tom es técnicamente correcta, pero me faltan algunos detalles para cambiar mis suposiciones. Respondí una pregunta similar a la mía en otro hilo SO basado en la explicación de Tom, pero teniendo en cuenta mi incomprensión original de la publicación de Meteor : Meteor publicar / suscribir estrategias para colecciones exclusivas del lado del cliente

Espero que esto ayude a aquellos que se encuentran con este hilo y salen más confundidos que nada.


Logré lograr algunos resultados preliminares prometedores abordando el problema con una única publicación / suscripción por colección, y aprovechando $or en la consulta de búsqueda.

La idea es proporcionar una envoltura alrededor de Meteor.Collection que te permita agregar "vistas", que son básicamente cursores. Pero lo que realmente está sucediendo es que estos cursores no se ejecutan individualmente ... sus selectores se extraen, se juntan $ y se ejecutan como una sola consulta y en un solo pub-sub.

No es perfecto, ya que un offset / límite no funcionará con esta técnica, pero en este momento minimongo no lo admite de todos modos.

Pero, en última instancia, lo que le permite hacer es declarar lo que parecen subconjuntos diferentes de la misma colección, pero bajo el capó son el mismo subconjunto. Hay un poco de abstracción al frente para hacer que se sientan limpiamente separados.

Ejemplo:

// Place this code in a file read by both client and server: var Users = new Collection("users"); Users.view("enabledUsers", function (collection) { return collection.find({ enabled: true }, { sort: { name: 1 } }); });

O si quieres pasar parámetros:

Users.view("filteredUsers", function (collection) { return collection.find({ enabled: true, name: this.search, { sort: { name: 1 } }); }, function () { return { search: Session.get("searchterms"); }; });

Los parámetros se dan como objetos, porque es un solo publicar / suscribir $ or''d juntos, necesitaba una forma de obtener los parámetros correctos ya que se mezclan.

Y para usarlo en una plantilla:

Template.main.enabledUsers = function () { return Users.get("enabledUsers"); }; Template.main.filteredUsers = function () { return Users.get("filteredUsers"); };

En resumen, aprovecho para tener el mismo código ejecutándose tanto en el servidor como en el cliente, y si el servidor no está haciendo algo, el cliente lo hará, o viceversa.

Y lo más importante, solo los registros que le interesan se envían al cliente. Todo esto se puede lograr sin una capa de abstracción simplemente haciendo $ o usted mismo, pero ese $ o se pondrá bastante feo a medida que se agreguen más subconjuntos. Esto solo ayuda a administrarlo con un código mínimo.

Escribí esto rápidamente para probarlo, disculpas por la duración y la falta de documentación:

test.js

// Shared (client and server) var Collection = function () { var SimulatedCollection = function () { var collections = {}; return function (name) { var captured = { find: [], findOne: [] }; collections[name] = { find: function () { captured.find.push(([]).slice.call(arguments)); return collections[name]; }, findOne: function () { captured.findOne.push(([]).slice.call(arguments)); return collections[name]; }, captured: function () { return captured; } }; return collections[name]; }; }(); return function (collectionName) { var collection = new Meteor.Collection(collectionName); var views = {}; Meteor.startup(function () { var viewName, view, pubName, viewNames = []; for (viewName in views) { view = views[viewName]; viewNames.push(viewName); } pubName = viewNames.join("__"); if (Meteor.publish) { Meteor.publish(pubName, function (params) { var viewName, view, selectors = [], simulated, captured; for (viewName in views) { view = views[viewName]; // Run the query callback but provide a SimulatedCollection // to capture what is attempted on the collection. Also provide // the parameters we would be passing as the context: if (_.isFunction(view.query)) { simulated = view.query.call(params, SimulatedCollection(collectionName)); } if (simulated) { captured = simulated.captured(); if (captured.find) { selectors.push(captured.find[0][0]); } } } if (selectors.length > 0) { return collection.find({ $or: selectors }); } }); } if (Meteor.subscribe) { Meteor.autosubscribe(function () { var viewName, view, params = {}; for (viewName in views) { view = views[viewName]; params = _.extend(params, view.params.call(this, viewName)); } Meteor.subscribe.call(this, pubName, params); }); } }); collection.view = function (viewName, query, params) { // Store in views object -- we will iterate over it on startup views[viewName] = { collectionName: collectionName, query: query, params: params }; return views[viewName]; }; collection.get = function (viewName, optQuery) { var query = views[viewName].query; var params = views[viewName].params.call(this, viewName); if (_.isFunction(optQuery)) { // Optional alternate query provided, use it instead return optQuery.call(params, collection); } else { if (_.isFunction(query)) { // In most cases, run default query return query.call(params, collection); } } }; return collection; }; }(); var Items = new Collection("items"); if (Meteor.isServer) { // Bootstrap data -- server only Meteor.startup(function () { if (Items.find().count() === 0) { Items.insert({title: "item #01", enabled: true, processed: true}); Items.insert({title: "item #02", enabled: false, processed: false}); Items.insert({title: "item #03", enabled: false, processed: false}); Items.insert({title: "item #04", enabled: false, processed: false}); Items.insert({title: "item #05", enabled: false, processed: true}); Items.insert({title: "item #06", enabled: true, processed: true}); Items.insert({title: "item #07", enabled: false, processed: true}); Items.insert({title: "item #08", enabled: true, processed: false}); Items.insert({title: "item #09", enabled: false, processed: true}); Items.insert({title: "item #10", enabled: true, processed: true}); Items.insert({title: "item #11", enabled: true, processed: true}); Items.insert({title: "item #12", enabled: true, processed: false}); Items.insert({title: "item #13", enabled: false, processed: true}); Items.insert({title: "item #14", enabled: true, processed: true}); Items.insert({title: "item #15", enabled: false, processed: false}); } }); } Items.view("enabledItems", function (collection) { return collection.find({ enabled: true, title: new RegExp(RegExp.escape(this.search1 || ""), "i") }, { sort: { title: 1 } }); }, function () { return { search1: Session.get("search1") }; }); Items.view("processedItems", function (collection) { return collection.find({ processed: true, title: new RegExp(RegExp.escape(this.search2 || ""), "i") }, { sort: { title: 1 } }); }, function () { return { search2: Session.get("search2") }; }); if (Meteor.isClient) { // Client-only templating code Template.main.enabledItems = function () { return Items.get("enabledItems"); }; Template.main.processedItems = function () { return Items.get("processedItems"); }; // Basic search filtering Session.get("search1", ""); Session.get("search2", ""); Template.main.search1 = function () { return Session.get("search1"); }; Template.main.search2 = function () { return Session.get("search2"); }; Template.main.events({ "keyup [name=''search1'']": function (event, template) { Session.set("search1", $(template.find("[name=''search1'']")).val()); }, "keyup [name=''search2'']": function (event, template) { Session.set("search2", $(template.find("[name=''search2'']")).val()); } }); Template.main.preserve([ "[name=''search1'']", "[name=''search2'']" ]); } // Utility, shared across client/server, used for search if (!RegExp.escape) { RegExp.escape = function (text) { return text.replace(/[-[/]{}()*+?.,//^$|#/s]/g, "//$&"); }; }

test.html

<head> <title>Collection View Test</title> </head> <body> {{> main}} </body> <template name="main"> <h1>Collection View Test</h1> <div style="float: left; border-right: 3px double #000; margin-right: 10px; padding-right: 10px;"> <h2>Enabled Items</h2> <input type="text" name="search1" value="{{search1}}" placeholder="search this column" /> <ul> {{#each enabledItems}} <li>{{title}}</li> {{/each}} </ul> </div> <div style="float: left;"> <h2>Processed Items</h2> <input type="text" name="search2" value="{{search2}}" placeholder="search this column" /> <ul> {{#each processedItems}} <li>{{title}}</li> {{/each}} </ul> </div> </template>


podrías hacer dos publicaciones separadas como esta ...

Publicaciones del servidor

Meteor.publish("enabled_items", function(){ var self = this; var handle = Items.find({enabled: true}).observe({ added: function(item){ self.set("enabled_items", item._id, item); self.flush(); }, changed: function(item){ self.set("enabled_items", item._id, item); self.flush(); } }); this.onStop(function() { handle.stop(); }); }); Meteor.publish("disabled_items", function(){ var self = this; var handle = Items.find({enabled: false}).observe({ added: function(item){ self.set("disabled_items", item._id, item); self.flush(); }, changed: function(item){ self.set("disabled_items", item._id, item); self.flush(); } }); this.onStop(function() { handle.stop(); }); });

Suscripciones de clientes

var EnabledItems = new Meteor.Collection("enabled_items"), DisabledItems = new Meteor.Collection("disabled_items"); Meteor.subscribe("enabled_items"); Meteor.subscribe("disabled_items");