Rafael
Rafael

Reputation: 1325

MongoDB relational query

I'm new with mongo and still trying to drift my head away from the mySQL methodology and dive it into mongo, so a few things are still blur on how i'm suppose to do and i was hopping you guys could shed me some light.

Let me explain the problem. I'm using nodeJS and the mongodb native.

Say i have a collection of users (i've used simpler _id just to illustrate, i know its an objectID, but consider just for the sake of the example)

{
     _id: 1,
     name: 'John',
     likes: ['2','3','4'] 
},
{
     _id: 2,
     name: 'Michelle'
     likes: ['1','4']
},
{
     name: 'Sabrina'
     _id: 3,
     likes: []
},
{
     name: 'Simone'
     _id: 4,
     likes: ['1','2', '3']
}

note that John liked Michell, Sabrina, and Simone, that Michelle liked John and Simone, Sabrina didn't like anyone, and Simone liked John, Michelle and Sabrina.

How do i run a query for John to find out who also liked him back based on his array of likes? I need something that would return Michelle if id = 1, or returns John and Michelle if id = 4. Only the ones that the id i'm fetching is in the other users like.

let me try to simplify.

I need to fetch John and go through all his likes and check id 2 likes me back? Yes id 3 likes me back? No

return [John];

or fetch Simone and go through all her likes and check id 1 likes me back? Yes id 2 likes me back? Yes id 3 likes me back? No

return [John, Michelle]

Any help would be appreciated :) thanks

Upvotes: 5

Views: 9476

Answers (4)

Flavien Volken
Flavien Volken

Reputation: 21299

You can now use $lookup:

Usage:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/

Upvotes: 1

phillro
phillro

Reputation: 106

With your document structure it will take two trips.

However generally when using a nosql db like mongo, one should denormalize. Instead of avoiding repetitive data, repeating in mongo is good and standard practice.

try:

{
     _id: 1,
     name: 'John',
     likes: ['2','3','4'],
     liked_by: ['2''4'] 
},
{
     _id: 2,
     name: 'Michelle',
     likes: ['1','4'],
     liked_by: ['1''4'] 
}

Upvotes: 1

jsdw
jsdw

Reputation: 5554

Here we go:

Something like this should do the trick using the mongodb-native driver in nodejs:

var people = db.collection("users");
people.findOne({name: "John"}, function(err, result)
  if(err) throw err; //..or however you want to handle an error.
  people.find({_id: {$in: result.likes}, likes:result._id}).toArray(function(err, others)
    {
    if(err) throw err; //...again, your call how to handle a potential error.
    //finds all people who are in likes array AND have Johns ID in their likes array.
    });
  });

Basically, first you retrieve the record for John. Next, you use his likes array to retrieve all users with matching id's :)

Note that this can be optimised slightly by opting to only retrieve the liked field for John, as in:

people.findOne({name: "John"}, {"likes": 1}, function(err, result)
  {
  //.....
  });

Also note that if the likes array is particularly massive, you may want to retrieve values one at a time rather than all at once using the .toArray() method, by instead using the cursor returned from find() and working with items one at a time, as in:

//....
var cursor = people.find({_id: {$in: result.likes}, likes:result._id});
cursor.each(function(err, item)
    {
    if(err) throw err; //...
    if(item != null)
    //do stuff with each person returned given likes array
    });
//....

I'm afraid I havnt tested the above, so apologies if I made any mistakes!

I find the manual for the mongo-native node.js driver to be invaluable; it covers most things pretty well (although some of the code seems a little "older" than other bits):

http://mongodb.github.io/node-mongodb-native/api-articles/nodekoarticle1.html

Upvotes: 5

Bret Copeland
Bret Copeland

Reputation: 24000

It has to be done in two queries (since Mongo is non-relational). The fist query would be to get the person (I'm sure you've figured out how to do that). The second would answer who likes him/her back.

var userId = 1;
people.findOne({ _id: userId }, { likes: 1 }, function (err, person) {
    if (err)
        throw err; // <-- obviously handle the error better than this

    people.find({ _id: { $in: person.likes }, likes: userId }).toArray(
        function (err, likers) {
            //now you have your list of people who liked the user back
        }
    );
});

Upvotes: 4

Related Questions