skip to Main Content

I have user schema and invitation schema.
When user search for users I want to return array with object that have property invited.

const InvitationSchema = new Schema<IInvitation>(
    {
        sender: {
            type: Schema.Types.ObjectId,
            ref: 'user'
        },
        receiver: {
            type: Schema.Types.ObjectId,
            ref: 'user'
        }
    }
)
const IUserSchema = new Schema<IUser>(
    {
        email: {
            type: String,
        },
        invitedUsers: [
            {
                type: Schema.Types.ObjectId,
                ref: 'user'
            }
        ]
    }
)

Scenario of my problem:

  1. There are 3 users with ids – userId1, userId2, userId3
  2. User1 sends invitation to User2.
    So current db becomes:
user1:{
  id:userId1,
  invitedUsers:[userId2]
}

invitation1:{
  id:someId,
  sender:userId1,
  receiver:userId2
}
  1. Here User1 make a user search request to the server and receive:
users:[
  {
     id:userId2,
     invited:true
  },
  {
     id:userId3,
     invited:false
  },
]

I also want to paginated users but its not target of the current problem.

I tried some variants with aggregation $addFields but nothing works. The other way to do 10 queries more as the users per page are 10. This is bad idea of course.

2

Answers


  1. If you want to check if the invitedUsers array has some length, then return true, otherwise false, then this could be done with aggregate

    User.aggregate([
      {
        $project: {
          _id: 1,
          invitedUsers: {
            $cond: [
              { $gt: [{ $size:"$invitedUsers" }, 0] },
              true,
              false,
            ],
          }
        }
      }
    ])
    

    explore aggregate here

    Login or Signup to reply.
  2. This is a nice question. One option is to do it with $lookup:

    1. The first 4 steps here are regarding your pagination, returning only the number of documents you need, after sorting, skipping, limiting and remove the user who request the search.
    2. Then we use the $lookup` to add the details of the requestingUserId, including the list of invited user ids on each one of our selected documents.
    3. Now all is left is to check for each document if its own id is included at the invitedUsers we got from the requesting user.
    db.users.aggregate([
      {$match: {id: {$ne: requestingUserId}}},
      {$sort: {id: 1}},
      {$skip: 1},
      {$limit: 2},
      {$addFields: {invitedBy: requestingUserId }},
      {
        $lookup: {
          from: "users",
          localField: "invitedBy",
          foreignField: "id",
          as: "invited"
        }
      },
      {$set: {invited: {$first: "$invited"}}},
      {
        $project: {
          invited: {$cond: [{$in: ["$id", "$invited.invitedUsers"]}, true, false] },
          id: 1, _id: 0
        }
      }
    ])
    

    See how it works on the playground example

    You can also improve the performance by using the $lookup pipeline to bring only the part you need:

    db.users.aggregate([
      {$match: {id: {$ne: requestingUserId}}},
      {$sort: {id: 1}},
      {$skip: 1},
      {$limit: 2},
      {
        $lookup: {
          from: "users",
          let: {myId: "$id"},
          pipeline: [
            {$match: {id: "userId1"}},
            {$project: {res: {$cond: [{$in: ["$$myId", "$invitedUsers"]}, true, false]}}}
          ],
          as: "invited"
        }
      },
      {$project: {invited: {$first: "$invited.res"}, id: 1, _id: 0}}
    ])
    

    See how it works on the playground example – lookup-pipeline

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search