运行
op:group-by()
函数后是否可以使用op:where()
、op:reduce()
等光学修改器功能?我在 Optic API Dev Guide 中找不到关于这个问题的任何内容。我们在 TDE 中存储了一些包含生效日期的数据。在查询时,我们通过 op:where()
过滤器来查找活动且“有效”的行。对于“父级”,技术上可以有两行“有效”。本例的业务需求是按生效日期降序对行进行排序后获取第一行(即获取最近的“生效”)。
我不知道有什么方法可以强制连接操作仅使用右计划到左计划中的第一个匹配行。我当前实现此目的的“黑客”是执行过滤器、联接和排序,然后使用
op:reduce()
函数迭代行并检查前一行是否属于同一父行。如果它是同一个父级,那么我将“isFirst”标志设置为fn:false()
,否则为fn:true()
。然后,我将减少的 op:result()
传递到 op:from-literals()
中,以在“第一”行上执行我需要的聚合和计数。
这是这个“hacky”实现的伪代码:
let $reducePlan := $mainPlan
=> op:reduce(function($previous as map:map*, $row as map:map) as map:map* {
let $prevUuid := map:get($previous[fn:last()], "parentUuid")
let $currUuid := map:get($row, "parentUuid")
let $isFirst :=
if ($prevUuid eq $currUuid)
then fn:false()
else fn:true()
let $_ := map:put($row, "isFirst", $isFirst)
return (
$previous,
$row
)
},
map:map()
)
=> op:result()
let $literals := op:from-literals($reducePlan)
(: Get the "first" row for each parent :)
=> op:where(op:eq(op:col("isFirst"), fn:true()))
(:
Other aggregations and counts here...
:)
=> op:result()
return $literals
这会产生正确的结果,但当有很多父母和整体的黑客方法时,我担心它的表现。我通常会使用
op:group-by()
函数,但我需要对其他列执行聚合,这些列可能不都是相同的值。我无法更改生成视图的 XML 的格式或结构。
两个主要问题:
op:reduce()
的方式以保留光学计划以便之后能够运行其他修改器功能吗?不再有有效计划之后的任何事情都是后处理,并且不会扩展。
op:reduce()
通过管道传输到 op:from-literals()
可以有效地将索引中的所有内容吸入内存中。
我建议你采取不断修改计划的方法。对于您的用例,如果每个parentUuid的 effectiveDate 是唯一的,那么一种方法可以是:制定一个计划来隔离每个parentUuid的适当时间戳,然后使用它来过滤结果。
下面的示例执行以下操作:
生成约 60,000 条记录(随机使用 3 个唯一的 ParentUUid 和 inEffect 的随机状态)
在其中,种植当前也有效的最新记录Effect = true
xquery version "1.0-ml";
import module namespace op="http://marklogic.com/optic"
at "/MarkLogic/optic.xqy";
let $parentUuid-list := ("aa-bb-cc", "dd-ee-ff", "gg-hh-ii")
let $fake-view := op:from-literals((
for $parentUuid at $step in $parentUuid-list
let $base := $step * 10000
for $y in (1 to 9999)
let $id := $base + $y
return map:entry("id", $id)=>map:with("parentUuid", $parentUuid)=>map:with("inEffect", xdmp:random(3) = 1)=>map:with("effectiveDateTime", fn:current-dateTime() - xs:dayTimeDuration("PT" || xdmp:random(99999999) || "S")),
map:entry("id", 40001)=>map:with("parentUuid", "aa-bb-cc")=>map:with("inEffect", fn:true())=>map:with("effectiveDateTime", fn:current-dateTime()),
map:entry("id", 40002)=>map:with("parentUuid", "dd-ee-ff")=>map:with("inEffect", fn:true())=>map:with("effectiveDateTime", fn:current-dateTime()),
map:entry("id", 40003)=>map:with("parentUuid", "gg-hh-ii")=>map:with("inEffect", fn:true())=>map:with("effectiveDateTime", fn:current-dateTime()),
for $parentUuid at $step in $parentUuid-list
let $base := ($step * 9999) + 40000
for $y in (1 to 9999)
let $id := $base + $y
return map:entry("id", $id)=>map:with("parentUuid", $parentUuid)=>map:with("inEffect", xdmp:random(3) = 1)=>map:with("effectiveDateTime", fn:current-dateTime() - xs:dayTimeDuration("PT" || xdmp:random(99999999) || "S"))
))
let $filter-plan := $fake-view
=>op:where(op:eq(op:col("inEffect"), fn:true()))
=>op:group-by(op:col("parentUuid"), op:max(op:col("maxDate"), op:col("effectiveDateTime")))
let $result-plan := $fake-view
=> op:exists-join(
$filter-plan,
op:on(op:col("parentUuid"),op:col("parentUuid")),
op:eq(op:col("maxDate"), op:col("effectiveDateTime"))
)
return $result-plan => op:result()
这里的关键在于过滤计划。看起来像这样:
这 2 列足以使用过滤连接。
这可能不完全是您所需要的。然而,通过以某种方式创建密钥并确保聚合有一些独特的东西,使得这种模式成为我经常与光学一起使用的模式