ユーザーが他のユーザーをフォローできるシンプルなアプリに取り組んでいます。ユーザーは投稿にスターを付けることができます。また、ユーザーのフィードは、フォローしているユーザーがスターを付けた投稿で構成されます。実際にはかなり単純です。ただし、これはすべて Mongo と Meteor では複雑になります...
私が考えることができるこれをモデル化する基本的に2つの方法があります:
ユーザーには、ユーザーが
following
フォローする userIds の配列であるプロパティがあります。また、投稿には、starrers
この投稿にスターを付けた userId の配列であるプロパティがあります。この方法の良い点は、出版物が比較的単純であることです。Meteor.publish 'feed', (limit) -> Posts.find({starrers: {$in: Meteor.users.findOne(@userId).following}}, {sort: {date: -1}, limit:limit})
ユーザーが誰をフォローしているかを反応的に聞いているわけではありませんが、今のところそれほど悪くはありません。このアプローチの主な問題は、(1) 1000000 人が投稿にスターを付けると、個々のドキュメントが大きくなり、非効率になることです。もう 1 つの問題は、(2) ユーザーが別のユーザーをフォローし始めた時期や、ユーザーが投稿にスターを付けた時期などの情報を追跡するのが面倒なことです。
これを行うもう 1 つの方法は、さらに 2 つのコレクションを作成すること
Stars
ですFollows
。ユーザーが投稿にスターを付けると、プロパティuserId
とを含むドキュメントが作成されますpostId
。ユーザーが別のユーザーをフォローしている場合、プロパティuserId
とを持つドキュメントを作成しますfollowId
。Users
これにより、とのドキュメント サイズが小さくなるという利点が得られますPosts
が、特に Mongo は結合を処理しないため、クエリに関しては複雑になります。
さて、私はいくつかの調査を行いましたが、人々は2番目の選択肢が正しい方法であることに同意しているようです. 今私が抱えている問題は、効率的なクエリと公開です。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
すべての投稿に対して巨大なクエリを実行します。
もう 1 つの厄介な点は、サブスクライブした後にクライアントでフィードをクエリすることです。
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()
この作業をすべて 2 回行っているようなものです。まず、フィードを公開するためにサーバー上ですべての作業を行います。次に、クライアントでまったく同じロジックを実行して、それらの投稿を取得する必要があります...
ここでの私の質問は、すべての設計の問題です。投稿を凝視しているフォロワーに基づいて、このフィードを効率的に設計するにはどうすればよいですか? どのコレクション/コレクション スキーマを使用すればよいですか? 適切なパブリケーションを作成するにはどうすればよいですか? クライアントでフィードを照会するにはどうすればよいですか?