简体   繁体   English

PHP更新MongoDB文档子数组的多个元素

[英]PHP updating multiple elements of sub-array of MongoDB document

I have the following document structure.我有以下文档结构。 I am trying to update specific values inside the holes sub-array:我正在尝试更新holes 子数组内的特定值:

圆形文件

Each holes Array element is an Object representing the score on a golf hole, with various properties (fields).每个holes Array 元素都是一个对象,代表一个高尔夫球洞的得分,具有各种属性(字段)。 I am trying to provide the ability to update the holeGross field ONLY for each score.我试图提供holeGross每个分数更新holeGross字段的能力。 On my PHP website, this is using a POST form, which pushes an Array of scores and the round._id value too, as:在我的 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] => )

I have had some assistance on the MongoDB forums, with MongoDB-native (ie shell) syntax which I have tested in Compass and works:我在 MongoDB 论坛上得到了一些帮助,使用了我在 Compass 中测试过的 MongoDB 原生(即 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"] } }
          ]
      }
    }
  }
}}]

Using this as a basis, I tried converting to PHP-equivalent, as follows:以此为基础,我尝试转换为 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();
        
}

But this errors with:但是这个错误:

Fatal error: Uncaught MongoDB\\Driver\\Exception\\BulkWriteException: The dollar ($) prefixed field '$map' in 'holes.$map' is not valid for storage.致命错误:未捕获的 MongoDB\\Driver\\Exception\\BulkWriteException:'holes.$map' 中以美元 ($) 为前缀的字段 '$map' 对存储无效。 in /var/www/html/vendor/mongodb/mongodb/src/Operation/Update.php:228 Stack trace: #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('60cb07d14bd1690...', Array) #5 {main} thrown in /var/www/html/vendor/mongodb/mongodb/src/Operation/Update.php on line 228在 /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('60cb07d14bd1690...', Array) #5 {main} 在第 228 行的 /var/www/html/vendor/mongodb/mongodb/src/Operation/Update.php 中抛出

I am getting very confused as to the equivalent syntax, or even if the same methods are available in the PHP driver?我对等效的语法感到非常困惑,或者即使 PHP 驱动程序中提供了相同的方法?

Can anyone point out the syntax issues / errors?谁能指出语法问题/错误?

Finally, could I simply replace the entire holes sub-array, and provide that as a whole var to the updateOne() call?最后,我可以简单地替换整个holes子数组,并将其作为一个整体updateOne()提供给updateOne()调用吗? This would send much more data and is not the most efficient obviously...这会发送更多的数据,显然不是最有效的......

*** UPDATE *** *** 更新 ***

@Joe was of great help in the final answer. @Joe 在最终答案中提供了很大帮助。 @Joe provided a single line covering what was the cause of the issue. @Joe 提供了一行,涵盖了问题的原因。

For posterity, I thought to repost the final syntax of my function for others to be able to follow:对于后代,我想重新发布我的函数的最终语法,以便其他人能够遵循:

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();
        
}

Of note, also needed to cast the $_POST array's fields to Int's as they are converted to Strings, so using the '$toInt' method to perform this on the DB as well.值得注意的是,在将 $_POST 数组的字段转换为字符串时,还需要将它们转换为 Int,因此也需要使用 '$toInt' 方法在 DB 上执行此操作。 Lots of extra square braces...许多额外的方括号...

Also this is not an upsert so just returning the modified count instead.此外,这不是一个upsert所以只返回修改后的计数。

The update part, the second argument to updateOne, must be an array in order to use aggregation operators. update 部分是 updateOne 的第二个参数,必须是一个数组才能使用聚合运算符。

Perhaps use也许用

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

Edit编辑

There is a pair of brackets missing from the second arrayElemAt expression:第二个arrayElemAt表达式中缺少一对括号:

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

should be应该

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM