在光接入计划中的 op:reduce() 函数之后使用修饰符函数

问题描述 投票:0回答:1

运行

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 的格式或结构。

两个主要问题:

  1. 有没有办法将连接限制为第一个匹配行或将连接之前的正确计划限制为仅具有“父级”的第一行?
  2. 我可以更改执行
    op:reduce()
    的方式以保留光学计划以便之后能够运行其他修改器功能吗?
xquery marklogic marklogic-optic-api
1个回答
2
投票

不再有有效计划之后的任何事情都是后处理,并且不会扩展。

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 列足以使用过滤连接。

这可能不完全是您所需要的。然而,通过以某种方式创建密钥并确保聚合有一些独特的东西,使得这种模式成为我经常与光学一起使用的模式

© www.soinside.com 2019 - 2024. All rights reserved.