我正在开发一个用户可以关注其他用户的简单应用程序。用户可以加星为帖子用户的提要由他们关注的用户加星标的帖子组成。其实很简单。然而,这一切在 Mongo 和 Meteor 中变得复杂......
我能想到的基本上有两种建模方法:
用户有一个属性 ,
following
它是用户遵循的用户 ID 数组。此外,一个帖子有一个属性 ,starrers
它是一个用户 ID 的数组,该数组已为该帖子加注星标。这种方法的好处是出版物相对简单:Meteor.publish 'feed', (limit) -> Posts.find({starrers: {$in: Meteor.users.findOne(@userId).following}}, {sort: {date: -1}, limit:limit})
我们不会被动地倾听用户关注的人,但现在还不算太糟糕。这种方法的主要问题是(1)如果 1000000 人为一个帖子加注星标,单个文档将变得庞大且效率低下。另一个问题是(2)跟踪信息会很痛苦,例如用户何时开始关注另一个用户或用户何时为帖子加注星标。
另一种方法是再创建两个集合,
Stars
并且Follows
. 如果用户为帖子加注星标,那么我们会创建一个带有属性userId
和postId
. 如果一个用户关注另一个用户,那么我们创建一个带有属性userId
和followId
.Users
这为我们提供了和文档大小较小的优势Posts
,但在查询方面却很复杂,尤其是因为 Mongo 不处理连接!
现在,我做了一些研究,人们似乎同意第二个选择是正确的选择。现在我遇到的问题是有效地查询和发布。基于关于 Advanced Publications 的 Discover Meteor 章节,我创建了一个出版物,该出版物发布由用户的关注者加星标的帖子 - 排序和限制。
# a helper to handle stopping observeChanges
observer = (sub, func) ->
handle = null
sub.onStop ->
handle?.stop?()
() ->
handle?.stop?()
handle = func()
Meteor.publish 'feed', (limit) ->
sub = this
userId = @userId
followIds = null
eventIds = null
publishFollows = observer sub, () ->
followIds = {}
Follows.find({userId:userId}).observeChanges
added: (id, doc) ->
followIds[id] = doc.followId
sub.added('follows', id, doc)
publishStars()
removed: (id) ->
delete followIds[id]
sub.removed('follows', id)
publishStars()
publishStars = observer sub, () ->
eventIds = {}
Stars.find({userId: {$in: _.keys(followIds)}).observeChanges
added: (id, doc) ->
eventIds[id] = null
sub.added('stars', id, doc)
publishEvents()
removed: (id) ->
delete eventIds[id]
sub.removed('stars', id)
publishEvents()
publishEvents = observer sub, () ->
Events.find({_id: {$in: _.keys(eventIds)}}, {sort: {name:1, date:-1}, limit:limit}).observeChanges
added: (id, doc) ->
sub.added('events', id, doc)
changed: (id, fields) ->
sub.changed('events', id, fields)
removed: (id) ->
sub.removed('events', id)
虽然这行得通,但在规模上似乎非常有限。特别是,我们必须编制每个关注者的每个已加星标的帖子的列表。此列表的大小将很快增长。$in
然后我们对所有帖子进行大量查询。
另一个烦恼是在我们订阅后查询客户端上的提要:
Meteor.subscribe("feed", 20)
posts = null
Tracker.autorun ->
followers = _.pluck(Follows.find({userId: Meteor.userId()}).fetch(), "followId")
starredPostIds = _.pluck(Stars.find({userId: {$in: followers}}).fetch(), "postId")
posts = Posts.find({_id: {$in: starredPostIds}}, {sort: {date: -1}, limit: 20}).fetch()
就好像我们把所有这些工作都做了两次。首先,我们在服务器上完成所有工作以发布提要。然后我们需要在客户端再次执行完全相同的逻辑来获取这些帖子......
我的问题是关于一切的设计问题。如何根据关注者的帖子有效地设计此提要?我应该使用什么集合/集合模式?我应该如何创建合适的出版物?如何在客户端查询提要?