You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的索引高效执行范围查询(建议给这两个字段加联合索引)
  • 结构统一:结果直接返回在主查询集合中,无需额外处理关联关系
  • 兼容性强:支持任意复杂的基础过滤条件,只要基础查询能正确匹配目标节点,就能无缝对接递归逻辑

示例验证

  1. 仅传入name=adjust时:子查询匹配到Adjustment节点的lft/rgt,主查询返回Windows、Glass unit、Adjustment三个节点
  2. 传入parentId=null&name=adjust&recursiveSearch=true时:基础过滤先匹配到parent_id为null的Windows节点,主查询仅返回Windows本身

内容的提问来源于stack exchange,提问作者Стас Бро

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:12:16