Meteor publish/subscribe strategies for unique cli

2019-01-10 07:09发布

问题:

Using Meteor, I'm wondering how best to handle different client-side collections that share the same server-side database collection. Consider the following example: I have a User collection, and on my client-side I have a list of users that are friends and I have a search feature that performs a query on the entire users database, returning a list of usernames that match the query.

On the Publish server-side method, I have two queries against the same collection that return different sets of documents. Should this data go into two separate collections on the client-side? Or should all of the User documents that match both queries end up in the same collection? If the latter, would I then duplicate code used for both the server-side and client-side query?

On the server:

Meteor.publish('searchResults', function(query){
  var re = new RegExp(query, 'i')
  return Users.find({ 'name' : {$regex: re}})
})

On the client:

Session.set('searchQuery', null)

Meteor.autosubscribe(function(){
  Meteor.subscribe('searchResults', Session.get('searchQuery'))
})

Template.search.events = {
  'keyup #user-search' : function(e){
    Session.set('searchQuery', e.target.value)
  }
}

_.extend(Template.search, {

  searchResults: function() {
    var re = new RegExp(Session.get('searchQuery'), 'i')
    return Users.find({ 'name' : {$regex: re}})
  }
})

This seems like a plausible solution, but not an optimal one. What if I wanted to create a new client-side collection that consisted of search results from multiple server-side Collections?

回答1:

In a shared area:

function getSearchUsers(query) {
  var re = new RegExp(query, "i");
  return Users.find({name: {$regex: re}});
}

function getFriendUsers() {
  return Users.find({friend: true});    // or however you want this to work
}

On the server:

Meteor.publish("searchUsers", getSearchUsers);
Meteor.publish("friendUsers", getFriendUsers);

On the client:

Template.search.onCreated(function () {
   var self = this;
   self.autorun(function () {
     self.subscribe("searchUsers", Session.get("searchQuery"));
   });
});

Template.friends.onCreated(function () {
  this.subscribe("friendUsers");
});

Template.search.helpers({
  searchResults: function () {
    return getSearchUsers(Session.get("searchQuery"));
  }
});

Template.friends.helpers({
  results: function () {
    return getFriendUsers();
  }
});

The key takeaway from this is that what happens behind the scenes when the data is getting transferred over the wire isn't obvious. Meteor appears to combine the records that were matched in the various queries on the server and send this down to the client. It's then up the client to run the same query again to split them apart.

For example, say you have 20 records in a server-side collection. You then have two publishes: the first matches 5 records, the second matches 6, of which 2 are the same. Meteor will send down 9 records. On the client, you then run the exact same queries you performed on the server and you should end up with 5 and 6 records respectively.



回答2:

I'm a little bit late to the party, but there is a way to actually have separate collections on the client for subsets of one server collection. In this example i have a server collection called entities which holds information about polygonsand rectangles.
Shared code (lib folder):

// main collection (in this example only needed on the server
Entities = new Meteor.Collection('entities');
// partial collections
RectEntities = new Mongo.Collection('rectEntities');
PolyEntities = new Mongo.Collection('polyEntities');

Client code:

// this will fill your collections with entries from the Entities collection
Meteor.subscribe('rectEntities');
Meteor.subscribe('polyEntities');

Remember that the name of the subscription needs to match the name of the publication (but not the name of the collection itself)
Server code:

Meteor.publish('rectEntities', function(){
    Mongo.Collection._publishCursor( Entities.find({shapeType: 'rectangle'}), this, 'rectEntities'); 
    this.ready();
});

Meteor.publish('polyEntities', function(){
    Mongo.Collection._publishCursor( Entities.find({shapeType: 'polygon'}), this, 'polyEntities'); 
    this.ready();
});

Thanks to user728291 for the much simpler solution using _publishCursor()!
The third argument of the _publishCursor() function is the name of your new collection.
Source: http://docs.meteor.com/#/full/publish_added



回答3:

use publish-composite package

// main collection
Entities = new Meteor.Collection('entities');

// partial collections only client side
RectEntities = new Mongo.Collection('rectEntities');
PolyEntities = new Mongo.Collection('polyEntities');

// server publish
Meteor.publishComposite("rectEntities", function(someParameter) {
    return {
            collectionName:'rectEntities',
            find: function() {
                return Entities.find({shapeType: 'rectangle'});
            },
            children: []
    }
});
Meteor.publishComposite("polyEntities", {
        collectionName:'polyEntities',
        find: function() {
            return Entities.find({shapeType: 'polygon'});
        },
        children: []
});

source : http://braindump.io/meteor/2014/09/20/publishing-to-an-alternative-clientside-collection-in-meteor.html