0

I have the following model and I want to query a specific user on _id field and populate the inbox.messages array with the necessary data that matches the corresponding _id field in the users model and more importantly i also want to group each message by the 'from' field and return that result

const UserSchema = new Schema({
   username: {
        type: String,
        required: true,
    },

    blockedUsers: {
        users: [
            {
                userId: {type: Schema.Types.ObjectId, ref: 'User', required: true },
            }
        ]
    },
    favorites: {
        users: [
            {
                userId: {type: Schema.Types.ObjectId, ref: 'User', required: true },
            }
        ]
    },
    profileViews: {
        views: [
            {
                userId: {type: Schema.Types.ObjectId, ref: 'User', required: true },
                date: {type: Date}
            }
        ]
    },
    inbox: {
        messages: [
            {
                messageId: {type: Schema.Types.ObjectId},
                from: {type: Schema.Types.ObjectId, ref: 'User', required: true },
                content: {type: String, required: true},
                date: {type: Date}
            }
        ]
    },
    images: {
        "imagePaths": [
            {   
                imageId: {type: Schema.Types.ObjectId},
                path: { type: String, required: true},
                date: {type: Date}
            }
        ],
    }
})

what I have so far

  let incomingId = '5e29fd75fdfd5320d0e42bc4';
  let myUser = await User.aggregate([
           { $match: {"_id": mongoose.Types.ObjectId(incomingId) }},
           { $lookup: { }}
         ])

Not sure exactly what to put in the $lookup field or if this is even correct.

As a sample I would like the documents to look like:

[
  {
    "from": "5e240f7480a24e07d832c7bd",
    "username":"hable0",
    "images": {
      imagePaths: [
         'images/2020-09-24-Z_34234342_12.jpg'
      ],
     },
    "inbox": {
      "messages": [
        {
          "messageId": "5e2a110a21c64d63f451e39e",
          "content": "Message content",
          "date": "2020-01-23T21:32:58.126Z"
        },
        {
          "messageId": "5e2a111321c64d63f451e3a0",
          "content": "Message content",
          "date": "2020-01-23T21:33:07.378Z"
        },
        {
          "messageId": "5e2a112321c64d63f451e3a2",
          "content": "Message content",
          "date": "2020-01-23T21:33:23.036Z"
        }
      ]
    }
  }
]
1
  • Can you edit your question to include some sample documents to test along with your expected JSON output? Commented Jan 24, 2020 at 8:54

1 Answer 1

0

You could try the following pipeline with aggregate().

  • Find the document that matches the id
  • Unwind inbox.messages
  • Group by from field
  • Perform a $lookup to get another document
  • Perform a $unwind to destruct the array
  • Specify fields to be included in the output
let myUser = await User.aggregate([
  {
    $match: { "_id": mongoose.Types.ObjectId(incomingId) }
  },
  {
    $unwind: "$inbox.messages"
  },
  {
    $group: {
      _id: { from: "$inbox.messages.from" },
      messages: {
        $push: {
          messageId: "$inbox.messages.messageId"
          // Add more info of the message here as needed
        }
      }
    },
  },
  {
    $lookup: {
      from: "User",
      localField: "_id.from",
      foreignField: "_id",
      as: "extraUserInfo"
    }
  },
  {
    $unwind: "$extraUserInfo"
  },
  {
    $project: {
      _id: 0,
      from: "$_id.from",
      inbox: { messages: "$messages" },
      username: "$extraUserInfo.username",
      images: "$extraUserInfo.images"
    }
  }
]);

Sample output:

{
  "from": "user1",
  "inbox": {
    "messages": [{
      "messageId": "message1-from-user1"
    }]
  },
  "username": "user1-username",
  "images": {
    "imagePaths": ["image-path-user1"]
  }
} {
  "from": "user2",
  "inbox": {
    "messages": [{
      "messageId": "message1-from-user2"
    }, {
      "messageId": "message2-from-user2"
    }, {
      "messageId": "message3-from-user2"
    }]
  },
  "username": "user2-username",
  "images": {
    "imagePaths": ["image-path-user2"]
  }
} {
  "from": "user3",
  "inbox": {
    "messages": [{
      "messageId": "message1-from-user3"
    }, {
      "messageId": "message2-from-user3"
    }]
  },
  "username": "user3-username",
  "images": {
    "imagePaths": ["image-path-user3"]
  }
}

Hope this answers part of your question. Though I'm not very clear how you would like to populate the messages array with the user info who sent the messages. But you can perform a $lookup() with a pipeline after $group() operation to attach additional info from the sender to the result.

Read more about $unwind, $group, $project and $lookup.

Sign up to request clarification or add additional context in comments.

7 Comments

Yes this is starting to look like what I want but I there is also a username field on the User model and imagePaths field, I would like to grab these as well and put the with information. Could you give an example of performing a $lookup? I still don't quite understand it.
@WoodsD I updated my answer to include a $lookup. Let me know if that works for you.
now I get an empty array. Where before I was getting the results just as you displayed them
Oh maybe, there was a typo. I was performing a lookup on Users instead of User. Please help me check all the variable names to make sure they're correct since I don't have a full setup like yours. Updated the answer btw.
I checked and changed Users from User. That didn't help either could it be because this is a self-reference. Should I think about adjusting the document structure and putting this data into a separate collection?
|

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.