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

Optic访问计划中op:reduce()后使用修饰函数及关联优化问询

MarkLogic Optic API 分组取首行优化方案

场景回顾

通过TDE存储含生效日期的数据,查询时过滤生效状态行后,同一parentUuid存在多行有效数据,业务要求取生效日期降序后的首行。现有方案通过op:reduce()标记首行再转字面量聚合,存在性能瓶颈且无法直接衔接后续Optic操作。


疑问1:关联时仅取右计划每个parent的首行(或关联前过滤)

完全可以通过**窗口函数op:rank()**实现,无需提前触发结果计算。在关联前先对右计划做分组排名过滤,从根源减少关联数据量:

(: 先处理右计划:按parentUuid分组,生效日期降序排名,仅保留每组首行 :)
let $rightPlan := op:from-view("your-schema", "your-right-view")
    => op:where(op:eq(op:col("isValid"), fn:true())) (: 过滤生效行 :)
    => op:window(
        op:partition-by(op:col("parentUuid")),
        op:order-by(op:desc(op:col("effectiveDate"))),
        op:rank("rowRank")
    )
    => op:where(op:eq(op:col("rowRank"), 1))

(: 关联主计划与处理后的右计划 :)
let $mainPlan := op:from-view("your-schema", "your-main-view")
    => op:join-inner($rightPlan, op:on(op:col("parentUuid"), op:col("parentUuid")))

这种方式让右计划在关联前就只保留每个parent的最新生效行,避免全量关联后再过滤冗余数据,性能远优于原方案。


疑问2:调整op:reduce()使用,保持Optic计划可用性

你当前方案调用op:result()将计划转为内存行集,再用op:from-literals()重构计划,这会断开Optic的查询优化链,导致后续操作无法利用MarkLogic的索引和分布式优化能力。

最优替代:用窗口函数替代op:reduce()

窗口函数是Optic原生支持的操作,全程保持计划可优化,后续可直接链式调用op:group-by()、op:where()等修饰函数:

let $optimizedPlan := $mainPlan
    => op:where(op:eq(op:col("isValid"), fn:true())) (: 过滤生效行 :)
    => op:window(
        op:partition-by(op:col("parentUuid")),
        op:order-by(op:desc(op:col("effectiveDate"))),
        op:rank("rowRank")
    )
    => op:where(op:eq(op:col("rowRank"), 1)) (: 保留每组首行 :)
    => op:group-by(
        op:col("parentUuid"),
        op:count("total", op:col("id")),
        op:max("latestDate", op:col("effectiveDate"))
        (: 直接在这里添加其他聚合操作 :)
    )
    => op:result()
return $optimizedPlan

若必须保留op:reduce()(不推荐)

如果因特殊场景需使用op:reduce(),不要调用op:result(),直接在Optic计划链中使用,后续仍可继续链式调用Optic函数:

let $reducePlan := $mainPlan
    => op:where(op:eq(op:col("isValid"), fn:true()))
    => op:order-by(op:col("parentUuid"), op:desc(op:col("effectiveDate")))
    => op:reduce(function($previous as map:map*, $row as map:map) as map:map* {
            let $prevUuid := if (fn:exists($previous)) then map:get($previous[fn:last()], "parentUuid") else ()
            let $currUuid := map:get($row, "parentUuid")
            let $isFirst := fn:not($prevUuid eq $currUuid)
            let $_ := map:put($row, "isFirst", $isFirst)
            return ($previous, $row)
        },
        () (: 初始值用空序列,避免多余空map :)
    )
    => op:where(op:eq(op:col("isFirst"), fn:true()))
    => op:group-by(op:col("parentUuid"), op:count("count", op:col("id")))
    => op:result()
return $reducePlan

但这种方式性能远不如窗口函数,因为op:reduce()是行级遍历,无法利用MarkLogic的分布式计算优化,数据量大时差距明显。


关键结论

  1. 优先用**窗口函数op:rank()**实现分组取首行,无论是关联前过滤还是主计划内过滤,都能保持Optic计划的可优化性,性能最优。
  2. 避免提前调用op:result()中断计划链,确保后续操作能利用MarkLogic的索引和查询优化能力。

内容的提问来源于stack exchange,提问作者nobleb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:53:06