【问题标题】:Meteor: How to publish cursor that is depending on cursor of other collectionMeteor:如何发布依赖于其他集合的光标的光标
【发布时间】:2013-12-29 13:47:49
【问题描述】:

我的 Meteor 项目中有以下数据结构:
- 具有一组属于用户(作者)的列表 ID 的用户
- 列表实际上包含列表的所有数据

现在我正在尝试将用户的所有列表发布到客户端。这是一个简单的例子:

if (Meteor.isClient) {
    Lists = new Meteor.Collection("lists");

    Deps.autorun(function() {
        Meteor.subscribe("lists");
    });

  Template.hello.greeting = function () {
    return "Test";
  };

  Template.hello.events({
    'click input' : function () {
      if (typeof console !== 'undefined')
        console.log(Lists.find());
    }
  });
}

if (Meteor.isServer) {
    Lists = new Meteor.Collection("lists");

    Meteor.startup(function () {
        if ( Meteor.users.find().count() === 0 ) {
               Accounts.createUser({        //create new user
                   username: 'test',
                   email: 'test@test.com',
                   password: 'test'
               });

               //add list to Lists and id of the list to user
               var user = Meteor.users.findOne({'emails.address' : 'test@test.com', username : 'test'});
               var listid = new Meteor.Collection.ObjectID().valueOf();
               Meteor.users.update(user._id, {$addToSet : {lists : listid}});
               Lists.insert({_id : listid, data : 'content'});
        }
     });


    Meteor.publish("lists", function(){
        var UserListIdsCursor = Meteor.users.find({_id: this.userId}, {limit: 1}).lists;
        if(UserListIdsCursor!=undefined){
            var UserListIds = UserListIdsCursor.fetch();

            return Lists.find({_id : { $in : UserListIds}});
        }
    });

    Meteor.publish("mylists", function(){
        return Meteor.users.find({_id: this.userId}, {limit: 1}).lists;
    });


//at the moment everything is allowed
Lists.allow({
    insert : function(userID)
    {
        return true;
    },
    update : function(userID)
    {
        return true;
    },
    remove : function(userID)
    {
        return true;
    }
});

}

但发布列表无法正常工作。任何想法如何解决这一问题?我还发布了“mylists”以保证用户可以访问“lists”字段。

【问题讨论】:

    标签: javascript mongodb meteor


    【解决方案1】:

    解决方案

    Lists = new Meteor.Collection('lists');
    
    if (Meteor.isClient) {
      Tracker.autorun(function() {
        if (Meteor.userId()) {
          Meteor.subscribe('lists');
          Meteor.subscribe('myLists');
        }
      });
    }
    
    if (Meteor.isServer) {
      Meteor.startup(function() {
        if (Meteor.users.find().count() === 0) {
          var user = {
            username: 'test',
            email: 'test@test.com',
            password: 'test'
          };
    
          var userId = Accounts.createUser(user);
          var listId = Lists.insert({data: 'content'});
          Meteor.users.update(userId, {
            $addToSet: {lists: listId}
          });
        }
      });
    
      Meteor.publish('lists', function() {
        check(this.userId, String);
        var lists = Meteor.users.findOne(this.userId).lists;
        return Lists.find({_id: {$in: lists}});
      });
    
      Meteor.publish('myLists', function() {
        check(this.userId, String);
        return Meteor.users.find(this.userId, {fields: {lists: 1}});
      });
    }
    

    变化

    1. 在客户端和服务器之外声明Lists集合(无需声明两次)。
    2. 确保用户在订阅时已登录。 (性能增强)。
    3. 插入测试用户时,使用所有插入函数都返回 id 的事实(减少代码)。
    4. 确保发布时用户已登录。
    5. 简化lists发布功能。
    6. 修复了myLists 发布功能。发布需要返回一个游标、游标数组或虚假值。您不能返回一个 id 数组(此代码无论如何都无法访问,因为您需要执行 fetchfindOne)。重要提示 - 这将发布另一个具有 lists 字段的用户文档。在客户端,它将与现有的用户文档合并,因此只有登录的用户才会拥有lists。如果您希望所有用户都在客户端上拥有该字段,那么我建议您将其添加到用户配置文件中。

    注意:如本文所述,如果附加了其他列表项,它们将不会被发布,因为 lists 发布功能只会在用户登录时重新运行。要使其正常工作,您将需要一个 @987654321 @。

    【讨论】:

    • 谢谢!反应性对我来说很重要。所以我会看看你发布的例子和 publish-with-relations 包
    • 好的,我尝试使用 publish-with-relations-package(Tom 在 GitHub 上发布的那个)发布数据。 Meteor.publish("users", function(){ return Meteor.users.find({_id : this.userId}); }); Meteor.publish('lists', function(id) { Meteor.publishWithRelations({ handle: this, collection: lists, filter: _id, mappings: [{ key: 'lists', collection: Meteor.users }] }); }); 我在服务器上遇到异常:“来自 sub RKMRo5fCswxinmvB5 ReferenceError 的异常:未定义列表”虽然它确实存在
    • 你需要使用Lists(你的集合实例)而不是lists。如果它仍然不起作用,我建议为此提出一个新问题,以便我们正确格式化代码。
    • 谢谢,但没用。我按照你的建议开了一个新问题:stackoverflow.com/questions/20871496/…
    【解决方案2】:

    这里真正的问题是架构。

    不要存储“此用户拥有这些列表”,例如,针对用户集合。存储“此列表归此用户所有”

    通过更改您的示例以在每个 List 上包含一个 ownerId 字段,然后发布变得容易 - 并且反应灵敏。

    它还消除了对 myLists 发布的需要,因为您只需在客户端查询您的列表。

    编辑:如果您的架构还包括每个 List 上的 userIds 字段,那么发布对于非所有者来说也是微不足道的。

    解决方案

    Lists = new Meteor.Collection('lists');
    
    if (Meteor.isClient) {
      Deps.autorun(function() {
        if (Meteor.userId()) {
          Meteor.subscribe('lists.owner');
          Meteor.subscribe('lists.user');
        }
      });
    }
    
    if (Meteor.isServer) {
      Lists._ensureIndex('userIds');
      Lists._ensureIndex('ownerId');
    
      Meteor.startup(function() {
        if (Meteor.users.find().count() === 0) {
          var user = {
            username: 'test',
            email: 'test@test.com',
            password: 'test'
          };
    
          var userId = Accounts.createUser(user);
          var listId = Lists.insert({data: 'content', ownerId: userId});
        }
      });
    
      //XX- Oplog tailing in 0.7 doesn't support $ operators - split into two publications -
      //      or change the schema to include the ownerId in the userIds list
      Meteor.publish('lists.user', function() {
        check(this.userId, String);
        return Lists.find({userIds: this.userId});
      });
      Meteor.publish('lists.owner', function() {
        check(this.userId, String);
        return Lists.find({ownerId: this.userId});
      });
    }
    

    【讨论】:

    • 我之所以选择这个模式,是因为我在每个列表中都有一个字段,其中包含多个允许查看此列表的用户 ID(以及一个称为所有者的字段)。上面的例子只是我的代码的简化。考虑到性能,我选择了这个模式。如果我按照您建议的方式进行操作,我总是必须检查所有列表以检查是否允许用户查看列表。
    • 最好还是存储userIds 的列表,以访问列表而不是用户。发布将与 lists 发布完全相同(因为 mongodb 将相等视为存在于数组中的值)。此外 - 随着用户拥有越来越多的列表,这将更好地扩展 - 因为列表集合可以由 ownerIduserIds 索引。
    • 我忘了说我还将用户/所有者的 ID 保存在一个列表中(我没有将它放在上面的示例中)。所以我两者都做。当然这会产生冗余数据,但我认为这将是实现这种场景的最佳性能方式:1)获取属于我的用户 ID 的所有列表(从我的个人资料中获取所有 id 并将它们从列表集合中获取)2 ) 显示列表的所有用户(获取列表的所有用户 ID 并将它们从用户集合中取出)。
    • 我会说场景 (1) 最好通过 Lists 上的索引来实现(对于少量数据,如果没有它,它可能同样快) - 而不是复制数据。我还建议避免与关系一起发布 - 您实际上是在不需要它们的地方制作额外的游标。 (我在示例中添加了_ensureIndex 的使用并添加了$or 条件)
    • 数据量少是什么意思?想象一下大约有 100 个用户。 List 集合中处理了大量更改,每次进行更改时,my-lists-query 都需要再次运行(因为反应性)。
    【解决方案3】:

    Meteor.users.find() 返回一个包含许多项目的光标,但您正尝试使用 .lists 访问

    Meteor.users.find({_id: this.userId}, {limit: 1}).lists;
    

    您需要改用 findOne。

    Meteor.users.findOne({_id: this.userId}).lists;
    

    此外,您在存储在用户集合中的数组上运行.fetch()。如果这是一个 ._id 字段数组,则不需要获取。

    您也不能在第二次发布中执行.lists,因为它是您必须检查客户端列表的游标,因此只需单独使用 Meteor.users.find(..),因为您只能发布游标。

    【讨论】:

    • 我用 find 而不是 findOne 因为我读过它保证反应性。感谢您的第二次提示
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 2012-09-17
    相关资源
    最近更新 更多