如何使用jenssegers/laravel-mongodb的Eloquent ORM查询MongoDB指定记录并过滤嵌套JSON字段(无需PHP数组函数)
如何用Laravel MongoDB Eloquent过滤JSON数组中的指定属性
问题背景
我正在用MongoDB配合jenssegers/laravel-mongodb包和Eloquent ORM开发,现在有个需求:查询一条指定ID的记录,并且只保留其JSON数组字段里disabled为true的项,最后提取这些项的_id。现有数据示例如下:
{ _id: ObjectId('630de2aa0ec78d9dfd0d23a2'), label: 'testLabel', jsonField: [ { _id: ObjectId('630de2aa0ec78d9d77777777'), disabled: true }, { _id: ObjectId('630de2aa0ec78d9d88888888'), disabled: false }, { _id: ObjectId('630de2aa0ec78d9d99999999'), disabled: true } ] }
目前我是用PHP的array_filter和array_column实现的,但想换成纯Eloquent查询的方式,该怎么做?
解决方案:两种纯Eloquent/数据库端实现方式
方法1:用MongoDB聚合管道(推荐,性能更优)
直接利用MongoDB的聚合框架,让所有过滤和提取操作都在数据库层面完成,不用把整个数组拉到PHP里处理:
$result = TestCollection::raw(function ($collection) { return $collection->aggregate([ // 第一步:匹配目标记录 ['$match' => ['_id' => new MongoDB\BSON\ObjectId('630de2aa0ec78d9dfd0d23a2')]], // 第二步:过滤jsonField中disabled为true的元素 ['$project' => [ 'jsonField' => [ '$filter' => [ 'input' => '$jsonField', 'as' => 'item', 'cond' => ['$eq' => ['$$item.disabled', true]] ] ] ]], // 第三步:提取过滤后元素的_id字段,转为数组 ['$project' => [ 'ids' => ['$map' => [ 'input' => '$jsonField', 'as' => 'item', 'in' => '$$item._id' ]], '_id' => 0 // 不需要返回原记录ID ]] ]); }); // 最终得到过滤后的_id数组 $filteredIds = $result->toArray()[0]['ids'] ?? [];
如果你的_id在模型里是字符串类型(而非ObjectId),直接去掉new MongoDB\BSON\ObjectId(),用字符串匹配即可。
方法2:用Eloquent查询构造器+投影
如果你更习惯Eloquent的链式语法,可以用select配合MongoDB的投影表达式,再用集合的pluck方法提取字段:
$record = TestCollection::where('_id', '630de2aa0ec78d9dfd0d23a2') ->select([ 'jsonField' => [ '$filter' => [ 'input' => '$jsonField', 'as' => 'item', 'cond' => ['$eq' => ['$$item.disabled', true]] ] ] ]) ->first(); // 从过滤后的jsonField里提取_id数组 $filteredIds = $record->jsonField->pluck('_id')->toArray();
这种写法更贴近Laravel的常规用法,同样是在数据库端完成过滤,只是最后用Eloquent集合的方法提取字段,代码更简洁。
为什么这两种方法更好?
相比PHP端用array_filter和array_column,这两种方法都是让MongoDB直接处理数据过滤和提取,减少了网络传输的数据量,尤其是当jsonField里的元素很多时,性能提升会很明显。
内容的提问来源于stack exchange,提问作者Lounis
相关产品推荐
相关产品推荐

