Modeling and Publishing Follow-up Based Material with Meteor

I am working on a simple application where a User can follow other users. Users can post messages. And a custom feed consists of posts that have been tagged by users that they follow. Pretty simple. However, this all gets complicated in Mongo and Meteor ...

There are basically two ways to model this that I can think of:

  • User has a property following

    , which is an array of userIds that follows the user. In addition, the post has a property starrers

    that is an array of userIds that this post took. The good thing about this method is that posting is relatively straightforward:

    Meteor.publish 'feed', (limit) ->
      Posts.find({starrers: {$in: Meteor.users.findOne(@userId).following}}, {sort: {date: -1}, limit:limit})
    
          

    We don’t react to who is following the user, but at the moment it’s not that bad. The main problem with this approach is that (1) individual documents will become large and ineffective if 100,000 people become public. Another problem is that (2) it would be painful to keep track of information, such as when a user started following another user or when a user took a post.

  • Another way to do this is to have two more collections Stars

    and Follows

    . If a user posts a message, we create a document with properties userId

    and postId

    . If a user follows another user, we create a document with properties userId

    and followId

    . This gives us the advantage of smaller document sizes for Users

    and Posts

    , but tricky things when it comes to queries, especially since Mongo doesn't handle connections!

Now, I've done some research, and people seem to agree that the second choice is the right way. Now the problem I'm running into is efficient query and post. Based on Open the Meteor chapter on Advanced Publishing , I created a post that posts that are flagged by Follower users - sorted and restricted.

# 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)

      

While it works, it appears to be very limited in scope. In particular, we have to make a list of all featured posts by each follower. The size of this list will grow very quickly. Then we make a huge $in

request for all posts.

Another annoyance is the feed request on the client after signing:

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()

      

It's like we're doing this job twice. First, we do all the work on the server to publish the feed. Then we need to repeat the same logic again on the client to get these messages ...

My question here is a design question over everything. How can I efficiently design this feed based on persistent subscriber records? What collection / collection schemes should be used? How do I create a related post? How can I request a feed on the client?

+3


source to share


2 answers


So it turns out that Mongo and "non-relational" databases are simply not designed for relational data. So there is no solution with Mongo here. I ended up using Neo4j, but SQL will work fine too.



0


source


meteor add reywood:publish-composite



 Meteor.publishComposite('tweets', function(username) {  
  return {
    find: function() {
      // Find the current user following users
      return Relationships.find({ follower: username });
    },
    children: [{
      find: function(relationship) {
        // Find tweets from followed users
        return Tweets.find({user: relationship.following});
      }
    }]
  }
});

Meteor.publish('ownTweets', function(username) {  
  return Tweets.find({user: username});
});

      

0


source







All Articles