Home > Mobile >  mongoDB - add custom field by another document field, with pagination
mongoDB - add custom field by another document field, with pagination

Time:06-16

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.

CodePudding user response:

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

CodePudding user response:

This is a nice question. There is a "trick" you can do here 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

  • Related