php - PHP更新MongoDB文档子数组的多个元素
问题描述
我有以下文档结构。我正在尝试更新孔子数组中的特定值:
每个holes
Array 元素都是一个表示高尔夫球洞得分的对象,具有各种属性(字段)。我试图提供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
只返回修改后的计数。
解决方案
更新部分,updateOne 的第二个参数,必须是一个数组才能使用聚合运算符。
也许使用
$updateOne = $collection->updateOne($match,[$set]);
编辑
arrayElemAt
第二个表达式中缺少一对括号:
[ 'holeGross' => [ '$arrayElemAt' => $scoresArray, '$$this' ]]
应该
[ 'holeGross' => [ '$arrayElemAt' => [ $scoresArray, '$$this' ]]]
推荐阅读
- javascript - history.push() 不能在 firebase 的身份验证中使用 react
- java - 如何强制执行一致的 Java 代码格式?
- html - 如何让两个浮动图像彼此相邻以与上方和下方的图像对齐?
- node.js - 盖茨比在 mozjpeg 上构建失败
- google-analytics - 通过 API 检索 Google Analytics 4 事件参数
- ruby - 什么是 Ruby IO 文本文件模式“t”?
- python - 在 Python 中使用 random 模块时,我们在什么情况下指定静态种子值?
- c# - Task.Delay(millis) 持续阻塞所有线程 100-500ms
- postgresql - 如何使用用户名和密码从 Azure AD 检索到的令牌对 Azure PostgreSQL 进行身份验证?
- java - 在制作自定义数据类型的数组时调用哪个构造函数?