Use value of find inside a projector in one request

Viewed 31

Here is an example of my data :

{
    "_id": ObjectId("5fa7d0e31abef4382b15efd0"),
    "users":
    [
        {
            "_id": ObjectId("5fa667194f22083924eeca9e"),
            "messageId": ObjectId("5fa7d15a1abef4382b15efd2")
        },
        {
            "_id": ObjectId("5fa673a50af228412b0ccf2b"),
            "messageId": ObjectId("5fa7d15a1abef4382b15efd1")
        }
    ],
    "messages":
    [
        {
            "_id": ObjectId("5fa7d0e31abef4382b15efcf"),
            "message": "Hi!",
            "userId": ObjectId("5fa667194f22083924eeca9e")
        },
        {
            "_id": ObjectId("5fa7d15a1abef4382b15efd1"),
            "message": "Hello!",
            "userId": ObjectId("5fa673a50af228412b0ccf2b")
        },
        {
            "_id": ObjectId("5fa7d15a1abef4382b15efd2"),
            "message": "How are you?",
            "userId": ObjectId("5fa667194f22083924eeca9e")
        }
    ]
}

I try to get the position of a message in the array "messages" determined by the messageId located in the array "users". Here is what I tried :

db.chat.find({ 'users._id': ObjectId("5fa667194f22083924eeca9e") },
             { 'users': 1,
               'index':
               {
                    '$indexOfArray':
                    [
                        '$messages._id',
                        '$users.0.toRead'
                    ]
                },
                'messages.$': 1
              })

I know that it works if I use :

db.chat.find({ 'users._id': ObjectId("5fa667194f22083924eeca9e") },
             { 'users': 1,
               'index':
               {
                    '$indexOfArray':
                    [
                        '$messages._id',
                        ObjectId("5fa7d15a1abef4382b15efd2")
                    ]
                },
                'messages.$': 1
              })

But I can't guess the id of the message before sending the request and I would like to make only one request and no bulk requests. Is it possible to do so ? I would like to do this index for both users. Should I copy the $indexToArray twice or there is another way ?

1 Answers

Using the aggregation framework it is indeed possible to achieve the desired result as you can leverage the operators $map to map the users array and include the new index field with the $mergeObjects operator. You can still use the $indexOfArray operator to get the position of the message id. Of course you would require an $addFields pipeline step to project this newly mapped users field and the other document property messages.

The following pipeline demonstrates the above operation:

db.chat.aggregate([
 { '$match': { 'users._id': ObjectId("5fa667194f22083924eeca9e") } },
 { '$addFields': {
    'users': {
        '$map': {
            'input': '$users',
            'as': 'u',
            'in': {
                '$mergeObjects': [
                    '$$u',
                    { 'index': { 
                        '$indexOfArray': ['$messages._id', '$$u.messageId']
                    } }
                ]
            }
        }
    } 
 } }
])
Related