jenssegers/laravel-mongodb更新嵌套数组子元素仅更新首项问题
MongoDB批量更新数组内所有匹配元素方案
问题场景
现有包含items数组字段的MongoDB文档,结构如下:
{ "bar" : "d bar", "items" : [ { "message" : "one", "display" : "true", "type" : "text" }, { "message" : "one2", "display" : "true", "type" : "text2" }, { "message" : "one2", "display" : "false", "type" : "text3" } ] }
需求为将items数组中所有message字段值为one2的元素,其message值更新为one22。原有更新语句仅能更新第一个匹配的数组元素,无法覆盖所有符合条件的项,原错误写法如下:
Bars::where('bar', 'd bar') ->where('items.message', 'one2') ->update(['items.$.message' => 'one22']);
问题原因
MongoDB默认的位置运算符$仅会匹配、更新数组中第一个满足查询条件的元素,这是之前写法只能修改单条匹配项的根本原因,该运算符本身不支持批量更新数组内所有匹配元素。
正确实现方式
MongoDB 3.6及以上版本(目前绝大多数生产环境均满足该版本要求)提供了过滤位置运算符$[<identifier>],搭配arrayFilters参数即可指定需要更新的数组元素范围,实现批量更新所有匹配项的效果。
对应你使用的Laravel MongoDB扩展(jenssegers/mongodb)的正确写法如下:
Bars::where('bar', 'd bar') ->update( // 定义更新逻辑,item为自定义的数组元素占位符 ['items.$[item].message' => 'one22'], // 定义数组元素过滤规则,仅匹配满足规则的元素执行更新 [ 'arrayFilters' => [ 'item.message' => 'one2' ] ] );
语法说明
$[item]为占位符标识,代表遍历items数组下的每一个元素,item这个名称可以自定义,只要和arrayFilters里的标识对应即可arrayFilters参数用来配置数组元素的匹配规则,只有符合规则的数组元素才会被执行更新操作,这里会命中所有message = one2的数组元素,不会只取第一条
低版本兼容方案
如果你的MongoDB版本低于3.6,不支持arrayFilters特性,只能通过先查询文档、代码层遍历修改数组所有匹配项、再整体保存文档的方式实现,该方案性能较差,优先建议升级MongoDB版本使用上述原生批量更新方案。
内容的提问来源于stack exchange,提问作者zelensky
相关产品推荐
相关产品推荐

