首页 > 解决方案 > PHP更新MongoDB文档子数组的多个元素

问题描述

我有以下文档结构。我正在尝试更新孔子数组中的特定值:

圆形文件

每个holesArray 元素都是一个表示高尔夫球洞得分的对象,具有各种属性(字段)。我试图提供holeGross仅为每个分数更新字段的能力。在我的 PHP 网站上,这是使用 POST 表单,它也推送一个分数数组和round._id值,如:

Array ( [holeScoreHidden] => Array ( [1] => 7 [2] => 7 [3] => 7 [4] => 8 [5] => 7 [6] => 7 [7] => 7 [8] => 7 [9] => 7 ) [roundId] => 60c642db09080f1b50331b2d [submit] => )

我在 MongoDB 论坛上获得了一些帮助,使用了我在 Compass 中测试过的 MongoDB-native(即 shell)语法并且可以工作:

[{$match: {
  _id: ObjectId('60c916684bd16901f36efb3a')
}}, {$set: {
  holes: {
    $map: {
      input: { $range: [0, { $size: "$holes"}]},
      in: {
        $mergeObjects: [
            { $arrayElemAt: ["$holes", "$$this"] },
            { holeGross: { $arrayElemAt: [[9,8,7,6,5,4,3,2,1], "$$this"] } }
          ]
      }
    }
  }
}}]

以此为基础,我尝试转换为 PHP 等效项,如下所示:

function setRoundScores($roundId, $scoresArray) {
        
    $collection = $client->golf->roundTest;
        
    $match = [ '_id' => new MongoDB\BSON\ObjectID( $roundId ) ];
        
    $set = [
        '$set' => [
            'holes' => [
                '$map' => [
                    'input' => [
                        '$range' => [ 0, [ '$size' => '$holes']],
                        'in' => [
                            '$mergeObjects' => [
                                [ '$arrayElemAt' => [ '$holes', '$$this' ]],
                                [ 'holeGross' => [ '$arrayElemAt' => $scoresArray, '$$this' ]]
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ];
        
    $updateOne = $collection->updateOne($match,$set);
        
    return $updateOne->getUpsertedId();
        
}

但这与以下错误有关:

致命错误:未捕获的 MongoDB\Driver\Exception\BulkWriteException:'holes.$map' 中的美元 ($) 前缀字段 '$map' 对存储无效。在 /var/www/html/vendor/mongodb/mongodb/src/Operation/Update.php:228 堆栈跟踪:#0 /var/www/html/vendor/mongodb/mongodb/src/Operation/Update.php(228 ): MongoDB\Driver\Server->executeBulkWrite('golf.roundTest', Object(MongoDB\Driver\BulkWrite), Array) #1 /var/www/html/vendor/mongodb/mongodb/src/Operation/UpdateOne.php (117): MongoDB\Operation\Update->execute(Object(MongoDB\Driver\Server)) #2 /var/www/html/vendor/mongodb/mongodb/src/Collection.php(1075): MongoDB\Operation\ UpdateOne->execute(Object(MongoDB\Driver\Server)) #3 /var/www/html/functions.php(803): MongoDB\Collection->updateOne(Array, Array) #4 /var/www/html/ updateRound.php(19): setRoundScores('

我对等效语法感到非常困惑,或者即使 PHP 驱动程序中提供了相同的方法?

谁能指出语法问题/错误?

最后,我可以简单地替换整个holes子数组,并将其作为一个整体 var 提供给updateOne()调用吗?这将发送更多数据,显然不是最有效的......

*** 更新 ***

@Joe 在最终答案中提供了很大帮助。@Joe 提供了一行内容,涵盖了问题的原因。

对于后代,我想重新发布我的函数的最终语法,以便其他人能够遵循:

function setRoundScores($roundId, $scoresArray) {
        
    $client = new MongoDB\Client($_ENV['MDB_CLIENT');

    $collection = $client->golf->round;

    $match = [ '_id' => new MongoDB\BSON\ObjectID( $roundId ) ];
        
    $set = [
        '$set' => [
            'holes' => [
                '$map' => [
                    'input' => [
                        '$range' => [ 0, [ '$size' => '$holes']]
                    ],
                    'in' => [
                        '$mergeObjects' => [
                            [ '$arrayElemAt' => [ '$holes', '$$this' ]],
                            [ 'holeGross' => [ '$toInt' => [ '$arrayElemAt' => [ $scoresArray, '$$this' ]]]]
                        ]
                    ]
                        
                ]
            ]
        ]
    ];
        
    $updateOne = $collection->updateOne($match, [$set]);
        
    return $updateOne->getModifiedCount();
        
}

值得注意的是,还需要将 $_POST 数组的字段转换为 Int,因为它们被转换为字符串,因此也需要使用 '$toInt' 方法在 DB 上执行此操作。很多额外的方括号...

这也不是upsert只返回修改后的计数。

标签: phparraysmongodb

解决方案


更新部分,updateOne 的第二个参数,必须是一个数组才能使用聚合运算符。

也许使用

$updateOne = $collection->updateOne($match,[$set]);

编辑

arrayElemAt第二个表达式中缺少一对括号:

[ 'holeGross' => [ '$arrayElemAt' => $scoresArray, '$$this' ]]

应该

[ 'holeGross' => [ '$arrayElemAt' => [ $scoresArray, '$$this' ]]]

推荐阅读