Merging collections in Meteor

前端 未结 3 1823
被撕碎了的回忆
被撕碎了的回忆 2020-12-10 15:20

Imagine you have multiple collections you want displayed on a single social news feed, for example Posts and Users (new signups). How would you rea

相关标签:
3条回答
  • 2020-12-10 15:51

    I came across this problem and I was wondering if a mixed solution would be better (or at least more efficient):

    The solution proposed by David works great but, as he says, problems could arise when dealing with big collections.

    What about keeping a local collection (just in the client), merge both collections into that local one when template is created and then register an observer (or maybe an observer for each remote collection) to keep the local collection up to date?

    0 讨论(0)
  • 2020-12-10 15:53

    Idea 1 seems to work out of the box. I created a new meteor project and change it as follows:

    test.js:

    Users = new Meteor.Collection("users");
    Posts = new Meteor.Collection("posts");
    
    if (Meteor.isClient) {    
      Template.hello.array = function () {
          var a = Users.find().fetch()
              .concat(Posts.find().fetch());
          return _.sortBy(a, function(entry) { return entry.votes; });      
      };
    }
    
    if (Meteor.isServer) {
      Meteor.startup(function () {
          // code to run on server at startup
          Users.insert({name: "user1", votes: 1});
          Users.insert({name: "user2", votes: 4});
          Users.insert({name: "user3", votes: 8});
          Users.insert({name: "user4", votes: 16});
    
          Posts.insert({name: "post1", votes: 2});
          Posts.insert({name: "post2", votes: 4});
          Posts.insert({name: "post3", votes: 6});
          Posts.insert({name: "post4", votes: 8});
    
      });
    }
    

    test.html:

    <head>
      <title>test</title>
    </head>
    
    <body>
      {{> hello}}
    </body>
    
    <template name="hello">
      {{#each array}}
      <div>{{votes}} {{name}}</div>
      {{/each}}
    
    </template>
    

    This gives the expected list:

    1 user1
    2 post1
    4 user2
    4 post2
    6 post3
    8 user3
    8 post4
    16 user4
    

    Then I did Users.insert({name: "new", votes: 5} in the console and got (reactively):

    1 user1
    2 post1
    4 user2
    4 post2
    5 new
    6 post3
    8 user3
    8 post4
    16 user4
    
    0 讨论(0)
  • 2020-12-10 16:10

    By far the easiest solution is to merge them on the client in a template helper. For example:

    Template.dashboard.helpers({
      peopleAndPosts: function() {
        var people = People.find().fetch();
        var posts = Posts.find().fetch();
        var docs = people.concat(posts);
        return _.sortBy(docs, function(doc) {return doc.createdAt;});
      }
    });
    

    and the template (assuming people and posts have a name):

    <template name="dashboard">
      <ul>
        {{#each peopleAndPosts}}
        <li>{{name}}</li>
        {{/each}}
      </ul>
    </template>
    

    This will be reactive because the helper is a reactive context so any changes to People or Posts will cause the returned array to be recomputed. Be aware that because you are not returning a cursor, any changes to either collection will cause the entire set to render again. This won't be a big deal if the length of the returned array is relatively short.

    0 讨论(0)
提交回复
热议问题