PHP Mongo Library没有更新一些文档

I recently started working on Mongo db. This is my sample document in mongo db in DB homeconfig and collection data

{
    "_id": {},
    "user_id": "user",
    "rooms": [
           {
            "name": "abhi",
            "metadata": {
                "user_id": "user",
                "is_expired": "false",
                "expired_on": null
            }
        }
    ],
    "sections": [
           {
            "name": "section1",
            "metadata": {
                "user_id": "user",
                "room": "abhi",
                "is_expired": "false",
                "expired_on": null
            }
        }
    ],
    "devices": [
           {
            "id": "aur.01.Jpzp0o",
            "metadata": {
                "section_less": "true",
                "label": "aa",
                "is_active": null,
                "is_loading": null,
                "is_dimmable": null,
                "type": "aura",
                "sub_device": "aur.01",
                "is_favourite": "false",
                "dimming": null,
                "energy_usage": 0,
                "usage_today": 0,
                "avg_usage": 0,
                "ir_control": null,
                "user_id": "user",
                "room": "abhi",
                "section": null,
                "is_expired": "false",
                "expired_on": null
            }
        }
    ]
}

The JSON shown above is just an example of my documents. The actual one is quite big and I cannot reveal the original copy.

Below is the function I am using to update the sets in the document.

function removeSectionsFromMongo($user_id, $data, $date)
{              
    $collection = (new MongoConnect())->connect('homeconfig', 'data');        
    foreach ($data as $key ) {         
        $update = $collection->updateOne(['user_id'=>$user_id, 'sections' => ['$elemMatch' => ['name' => $key['name'], 'metadata.is_expired' => 'false' ]]],
                ['$set'=>['sections.$.metadata.is_expired'=>'true', 'sections.$.metadata.expired_on'=>$date]]
            );
        if($update->getModifiedCount() == 1)
        {
            echo "Section <".$key['name']."> removed from room <".$key['metadata']['room'].">."."
";
        }
    }        
}

Variable $data is an array shown below:

Array
(
    [0] => Array
        (
            [name] => Appliances
            [metadata] => Array
                (
                    [user_id] => CubicalPrime
                    [room] => Dhruv
                    [is_expired] => false
                    [expired_on] => 
                )

        )

    [1] => Array
        (
            [name] => Bathroom
            [metadata] => Array
                (
                    [user_id] => CubicalPrime
                    [room] => Dhruv
                    [is_expired] => false
                    [expired_on] => 
                )

        )

    [2] => Array
        (
            [name] => Others
            [metadata] => Array
                (
                    [user_id] => CubicalPrime
                    [room] => Dhruv
                    [is_expired] => false
                    [expired_on] => 
                )

        )

)

When I execute the function, it should update all the sections in my document matching in the array $data passed.

However, It is updating the documents. But when the size of document grows, sometimes few object(s) in the sections not getting update. i.e. the is_expired field is not updating to true & expired_on is not updating withe the date passed.

When I run the same function again, the whole updation takes place in second time.

I'm echoing only when the query result says modified count equal to 1. That means the query is executing properly. But few documents are not getting update.

    if($update->getModifiedCount() == 1)
    {
        echo "Section <".$key['name']."> removed from room <".$key['metadata']['room'].">."."
";
    }

I'm trying to figure out why the document is not getting updated in first time but gets update in second time!!

Help me what's wrong with my code?

After a little big digging into my code, I added one more condition for the validation for updating the records.

function removeSectionsFromMongo($user_id, $data, $date)
{              
    $collection = (new MongoConnect())->connect('homeconfig', 'data');        
    foreach ($data as $key ) {         
        $update = $collection->updateOne(['user_id'=>$user_id, 'sections' => ['$elemMatch' => ['name' => $key['name'], 'metadata.is_expired' => 'false', 'metadata.room' => $key['metadata']['room'] ]]],
                ['$set'=>['sections.$.metadata.is_expired'=>'true', 'sections.$.metadata.expired_on'=>$date]]
            );
        if($update->getModifiedCount() == 1)
        {
            echo "Section <".$key['name']."> removed from room <".$key['metadata']['room'].">."."
";
        }
    }        
}

See this in the above code 'metadata.room' => $key['metadata']['room'].

Now the records are being updated as usual. Also I am now using bulkWrite() as suggested by chridam in other answer for reducing the load on server. See below code:

function removeSectionsFromMongo($user_id, $data, $date, $collection, $debug)
{            
    $bulkOps = array();      
    $sections = array(); $rooms = array(); 
    foreach ($data as $key ) {  
        $update = [
            'updateOne' => [
                [
                    'user_id' => $user_id, 
                    'sections' => [ 
                        '$elemMatch' => [
                            'name' => $key['name'], 
                            'metadata.user_id' => $user_id, 
                            'metadata.room' => $key['metadata']['room'],
                            'metadata.is_expired' => 'false' 
                        ]
                    ]
                ], 
                [
                    '$set' => [
                        'sections.$.metadata.is_expired' => 'true', 
                        'sections.$.metadata.expired_on' => $date
                    ]
                ]
            ]
        ];          
        array_push($bulkOps, $update);
        if($debug)
        {
            array_push($sections, $key['name']);        
            array_push($rooms, $key['metadata']['room']);                 
        }                          
    } 
    $result = $collection->bulkWrite($bulkOps);

    if($result->getModifiedCount() == count($data))
    {
        if($debug)
        {
            for($i = 0; $i < count($sections); $i++)
            {
                echo "Section -".$sections[$i]."- removed from room -".$rooms[$i]."-."."
";
            }
        }
        return true;
    }
    return false;
}//end removeSectionsFromMongo()

Still I am not getting why my previous code was not working with a few validation for records. Is it a bug in Mongo Library or something else!!

If anyone figure out what was the issue, please let me know.

You could use the bulkWrite() API to leverage your updates as with the current implementation you are sending asynchronous updates in a loop, hence why you are having some documents being updated and some not.

The bulkWrite() API has methods that can allow your update operations to be executed as a batch on the server hence the efficiency as you don't send each update operation with each iteration to the server, just once say in 500 operations.

The following demonstrates the approach:

function removeSectionsFromMongo($user_id, $data, $date) {
    $bulkOps = array();      
    foreach ($data as $key ) {  
        $update = [
            'updateOne' => [
                [
                    'user_id' => $user_id, 
                    'sections' => [ 
                        '$elemMatch' => [
                            'name' => $key['name'], 
                            'metadata.is_expired' => 'false' 
                        ]
                    ]
                ], 
                [
                    '$set' => [
                        'sections.$.metadata.is_expired' => 'true', 
                        'sections.$.metadata.expired_on' => $date
                    ]
                ]
            ]
        ];          
        array_push($bulkOps, $update);        
    } 
    $operation = new BulkWrite($databaseName, $collectionName, $bulkOps);
    $result = $operation->execute($primaryServer);
    echo $result->getModifiedCount();
}