0

I'm developing a Chat application where I'm saving the conversation list in the DB like this;

Conversations Collection:

{ 
   members: [ "123", "456" ]
   ...rest
}

and User Collection:

{
   _id: ObjectId( "123" ), name: "anyone"
},
{
   _id: ObjectId( "456" ), name: "someone"
}

I want the result to be like:

{
   members: [
              {_id: ObjectId( "123" ), name: "anyone"},
              {_id: ObjectId( "456" ), name: someone"}
            ]
}

I know aggregation with lookup is the rescue but can't find a way how to fetch all ids from that member's array because in the future it can be 20 to 30 ids. If it is a simple field then I'm able to fetch but with an array, I can't.

I have tried this

db.conversations.aggregate([
  {
    "$lookup": {
      "from": "users",
      "localField": "members",
      "foreignField": "_id",
      "as": "members_details"
    }
  }
])

but it returns members_details: [ ]

0

4 Answers 4

1

You could use the following query to accomplish what you want. You'll need to use $lookup since you are wanting to gather data from a different collection then the one you are currently querying.

You can check out a live demo here

Here is an updated live demo

Database

db={
  "conversations": [
    {
      members: [
        123,
        456
      ]
    }
  ],
  "users": [
    {
      "_id": 123,
      "name": "foo"
    },
    {
      "_id": 456,
      "name": "bar"
    }
  ]
}

Query

db.conversations.aggregate([
  {
    "$lookup": {
      "from": "users",
      "localField": "members",
      "foreignField": "_id",
      "as": "members"
    }
  },
  {
    $project: {
      _id: 0,
      members: 1
    }
  }
])

Result

[
  {
    "members": [
      {
        "_id": 123,
        "name": "foo"
      },
      {
        "_id": 456,
        "name": "bar"
      }
    ]
  }
]

Update

See new live demo here

New Query

db.conversations.aggregate([
  {
    $unwind: "$members"
  },
  {
    "$lookup": {
      "from": "users",
      "as": "membersFlat",
      "let": {
        memberObjectId: {
          "$toObjectId": "$members"
        }
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$$memberObjectId",
                "$_id"
              ]
            }
          }
        }
      ]
    }
  },
  {
    $group: {
      _id: null,
      members: {
        $push: {
          "_id": {
            $first: "$membersFlat._id"
          },
          "name": {
            $first: "$membersFlat.name"
          }
        }
      }
    }
  },
  {
    $project: {
      _id: 0
    }
  }
])

New Result

[
  {
    "members": [
      {
        "_id": ObjectId("124578987898787845658574"),
        "name": "foo"
      },
      {
        "_id": ObjectId("124578986532124578986532"),
        "name": "bar"
      }
    ]
  }
]
Sign up to request clarification or add additional context in comments.

5 Comments

This will not work because in the users collection _id represents as a ObjectId type not a string. already tried this solution not worked. Anyway thanks
I have updated your demo check - mongoplayground.net/p/bfZENqF8s3c DB will be like this
I went ahead and updated to demo again to show how you can use ObjectId. mongoplayground.net/p/T4Cjf3-G9mq
Thanks, @matt but I can't make members array as an ObjectId of the array because it is coming from frontend and directly saved to DB
Check out the update to my answer. See here for live demo mongoplayground.net/p/-P1IcxG4JZZ
0

Since your members id is string in conversation collection, you can convert it to object id and the join to users collection,

  • $addFields to update members array
  • $map to iterate loop of members array
  • $toObjectId to convert string object id type to object id type
db.conversations.aggregate([
  {
    $addFields: {
      members: {
        $map: {
          input: "$members",
          in: {
            $toObjectId: "$$this"
          }
        }
      }
    }
  },
  {
    $lookup: {
      from: "users",
      localField: "members",
      foreignField: "_id",
      as: "members"
    }
  }
])

Playground

Comments

0

You can use a normal .find() like this:

const members = [
    ObjectId('4ed3ede8844f0f351100000c'),
    ObjectId('4ed3f117a844e0471100000d'), 
    ObjectId('4ed3f18132f50c491100000e')
]

const docs = await model.find({
    '_id': { $in: members }
}).exec()

2 Comments

I want to query on conversation collection and try to get the user object. How a normal find will work?
sorry, i misunderstood the question
0

Install mongoose-autopopulate. And in your model code


import {Schema, model} from 'mongoose';

const modelSchema = new Schema({
   ...
   chats: [{type: Schema.Types.ObjectId, ref: "Chat", autopopulate: true}]
})

modelSchema.plugin(require('mongoose-autopopulate'));
...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.