0

Given the following Mongo collections:

  • A large collection of articles which hold the ID of the user who wrote them.
  • A large collection of users, each of which can 'follow' a large number of other users.

My objective is to get a cursor to all the articles written by any one of the authors that a given user is following.

Currently I have to do this in several steps:

  1. Find() the authors that the user is following
  2. Turn this result into an array of IDs
  3. Find the articles written by any of the users in that array (using $in).

Step 2 is highly inefficient. I'm currently fetching the entire result, reforming it into an array, and then sending it back to the database as part of the query. All this has to be be done in Javascript (running on Meteor/Node.js).

Is there a better solution?

tarmes
  • 15,366
  • 10
  • 53
  • 87
  • What's the format of your user / following data? There may be a way to do an aggregation query that'd make this easier. – Will Shaver Dec 02 '14 at 21:16
  • Currently, rather than embedding the followers in each user doc, I have a separate collection containing 'followerId', 'followeeId', etc. with one row per connection. This can change however... – tarmes Dec 02 '14 at 21:46

2 Answers2

0

To get an array of all your users in the followers collection, use the $addToSet aggregate command.

db.followers.aggregate([{ $match: { followerId: 'abcd' } },
     {$group: {_id: 0, following: {$addToSet: '$followeeId'} } }]);

This will give you results like:

{ id: 0, following: ['xxx', 'yyy', 'zzz'] }
Will Shaver
  • 12,471
  • 5
  • 49
  • 64
  • Hi Will. That's certainly an improvement over what I'm doing in term's of creating the array, but it still requires me to take that result out of the database, then pass it back in as a parameter to the $in operator. If there are thousands of followees then that's not great. Can I directly pass this result to the next find operation? – tarmes Dec 03 '14 at 07:51
  • Sadly, I believe an $in query is the best you can do with mongodb. Happy to be wrong if someone else has another suggestion. – Will Shaver Dec 03 '14 at 16:47
0

I suppose that you want a cursor on client side and so what you need is joining two collection

You can use one of this packages to do it: https://hackpad.com/Meteor-reactive-join-publication-packages-MpSFfsZrMxa

I'm using smartPublish and it would be like this

Server:

Meteor.smartPublish("articles", function () {

    this.addDependency("MyJoinCollection","followerId",function(obj){
        //return a cursor of articles written by the followee found
        return Articles.find({author:obj.followeeId})
    })

    // return a cursor corresponding to all the followee of the current user
    return MyJoinCollection.find({followerId:this.userId})
})

Client

Meteor.subscribe("articles")
//returns all articles from all users that current user is following
Articles.find()

And if you want a specific user you have to give it as a parameter to the subscription/publish

user3636214
  • 605
  • 4
  • 12
  • I'm not asking how to do a reactive join. I'm wondering if there's a way to do my search entirely in Mongo without sending the intermediate array back and forth to my application code. – tarmes Dec 03 '14 at 13:15
  • Sorry, your problem looks similar to those who need joins. I don't know any way without creating any intermediate array to get a meteor cursor to the end. On server side I use cursor.forEach and underscore to manage data. Maybe [this](http://stackoverflow.com/questions/4067197/mongodb-and-joins) will help you. – user3636214 Dec 03 '14 at 13:58
  • Perhaps it's a terminology thing, but I'm not looking to doing a join - that is, I don't want fields from multiple collections in a single result. I need to search for a bunch of IDs in one collection, then use that result in another search operation. It's seems unnecessary to send back the intermediate result... – tarmes Dec 03 '14 at 14:18