Laravel树形结构查询求助:获取匹配节点及所有父节点方案
解决方案:利用嵌套集lft/rgt字段特性优化查询
嵌套集的核心是每个节点的lft(左值)和rgt(右值):父节点的lft一定小于所有子节点的lft,rgt一定大于所有子节点的rgt。基于这个特性,我们可以用子查询+范围匹配实现需求,既避免大量ID的IN条件,又能把目标节点直接返回在主查询结果中。
具体实现步骤
1. 构建基础过滤查询
先按业务需求写出基础的过滤逻辑(比如按name、parentId筛选),后续基于这个查询获取匹配节点的范围值。
2. 关联子查询筛选目标节点
通过子查询提取所有匹配节点的lft和rgt,然后主查询筛选出满足lft <= 匹配节点.lft AND rgt >= 匹配节点.rgt的节点——这些节点就是所有匹配节点及其所有祖先。
代码示例
$query = Category::query(); // 处理基础过滤条件 if ($request->has('name')) { $query->where('name', 'like', "%{$request->name}%"); } if ($request->has('parentId')) { $parentId = $request->parentId === 'null' ? null : $request->parentId; $query->where('parent_id', $parentId); } // 开启递归搜索时调整查询逻辑 if ($request->boolean('recursiveSearch')) { // 克隆基础查询,获取所有匹配节点的lft和rgt $matchingNodes = $query->clone()->select('lft', 'rgt'); // 主查询改为筛选所有在匹配节点范围中的节点(祖先+自身) $query = Category::whereExists(function ($q) use ($matchingNodes) { $q->fromSub($matchingNodes, 'matching') ->whereRaw('categories.lft <= matching.lft') ->whereRaw('categories.rgt >= matching.rgt'); }); } // 执行查询,结果直接包含所有匹配节点及其父节点 $results = $query->get();
方案优势
- 性能更优:避免了大数量级ID的IN子句,数据库可利用
lft和rgt的索引高效执行范围查询(建议给这两个字段加联合索引) - 结构统一:结果直接返回在主查询集合中,无需额外处理关联关系
- 兼容性强:支持任意复杂的基础过滤条件,只要基础查询能正确匹配目标节点,就能无缝对接递归逻辑
示例验证
- 仅传入
name=adjust时:子查询匹配到Adjustment节点的lft/rgt,主查询返回Windows、Glass unit、Adjustment三个节点 - 传入
parentId=null&name=adjust&recursiveSearch=true时:基础过滤先匹配到parent_id为null的Windows节点,主查询仅返回Windows本身
内容的提问来源于stack exchange,提问作者Стас Бро
相关产品推荐
相关产品推荐

