XSLT3算法Q:基于时间戳查找数据的最快方法——日期X之前的最近一次

XSLT3算法Q:基于时间戳查找数据的最快方法——日期X之前的最近一次,xslt,map,key,saxon,xslt-3.0,Xslt,Map,Key,Saxon,Xslt 3.0,我处理大量中小型文档(~2 meg)数据文件,并试图确定基于时间戳查找值的最快方法 如果我查找“查找时间戳X的数据”,这将很简单,但我通常希望“查找时间戳在日期X之前或日期X上的最新数据” 具体情况如下: 想象一下,你有一个由300所房子组成的集群,每个房子偶尔都会收到邮件。您正在监视他们收到的邮件类型。假设你关心15类邮件 令人感兴趣的问题是“在D日当天或之前,最新的一类邮件是什么?” A.被引用的数据文件具有以下格式: <data> <house house_ID = "

我处理大量中小型文档(~2 meg)数据文件,并试图确定基于时间戳查找值的最快方法

如果我查找“查找时间戳X的数据”,这将很简单,但我通常希望“查找时间戳在日期X之前或日期X上的最新数据”

具体情况如下: 想象一下,你有一个由300所房子组成的集群,每个房子偶尔都会收到邮件。您正在监视他们收到的邮件类型。假设你关心15类邮件

令人感兴趣的问题是“在D日当天或之前,最新的一类邮件是什么?”

A.被引用的数据文件具有以下格式:

<data>
 <house house_ID = "XXX" mail_category="YYY" timestamp="ZZZ"/>
 <house house_ID = "XXX" mail_category="YYY" timestamp="ZZZ"/>
 <house house_ID = "XXX" mail_category="YYY" timestamp="ZZZ"/>
 <house house_ID = "XXX" mail_category="YYY" timestamp="ZZZ"/>
 ...
</data>

...
B.数据文件不必排序。如果这对最佳实践有影响,请在回答中说明

C.尽管数据文件中追踪了约300所房屋,但我的工作只需要60所特定房屋的数据

有100个日期的信息,大多数房子在这100个日期中的3-20天收到邮件

电子邮件可以全天发送。因此,在某一天,一个人可以先得到1类,然后得到2类,最后在晚上得到8类

F.对于一份典型的数据文件,一个给定房屋的信息可能会被要求10次左右

这里有两条可能的道路,我对每一条都有自己的想法。我希望XSLT3超级程序员中的一个会有更好的选择

解决方案1:大型地图 映射通常是许多XSLT3速度问题的首选解决方案,但我不确定它们对这个问题的适应程度,因为似乎您必须创建一个巨大的映射,而其中大多数实际上并不需要

我尝试过的内容概述如下:

<xsl:variable name="sorted_data" select="saxon:sort(houses I want from data, by date)"/>
<xsl:variable name="dates" select="distinct-values($sorted_data/date:date(@timestamp))"/>
<xsl:variable name="mail.map.pieces" as="map(*)*">
 <xsl:for-each-group select="$sorted_data" group-by="$house_number">
   <xsl:iterate select="current-group">
      Use iteration to form one map for every possible date/house, reading data file once.
      map has form  map{concat($date'--'$house_number) := last_mail_type}
      Note that this internal piece requires a bit of extra computation because you need a map for _every_ date in $dates, but the set being iterated over only contains nodes for dates on which the house received mail.
   </xsl:iterate>
  </xsl:for-each-group>
</xsl:variable>

<xsl:variable name="mail.map" select="map:new($mail.map.pieces)"/>

使用迭代为每个可能的日期/房子形成一个地图,读取数据文件一次。
地图的形式为地图{concat($date'-'$house\u number):=last\u mail\u type}
请注意,这个内部部分需要一些额外的计算,因为您需要一个以$dates为单位的_every_date映射,但是迭代的集合只包含房屋接收邮件日期的节点。
问题是构建这个映射需要60*100个map{}命令,其中只有10%将被使用。也有几次打电话来处理失踪天数问题

解决方案2:小地图

使用“地图”的另一个选项是将给定房屋的所有邮件数据关联到该房屋ID,然后稍后进行搜索/筛选:

<xsl:variable name="sorted_data" select="saxon:sort(houses I want from data, by date)"/>
<xsl:variable name="dates" select="distinct-values($sorted_data/date:date(@timestamp))"/>
<xsl:variable name="mail.map.pieces" as="map(*)*">
 <xsl:for-each-group select="$sorted_data" group-by="$house_number">
  <xsl:sequence select="map{house_numer := current-group()}/>
 </xsl: for-each-group
</xsl:variable>
<xsl:variable name="mail.map" select="map:new($mail.map.pieces)"/>


对不起,你花了很多时间仔细思考这个问题,我真的很想同样认真地回答这个问题,但我没有时间

当然,映射和键的问题在于它们只进行相等匹配。我不知道您是否对使用扩展感兴趣,但它似乎是Saxon 9.5中引入的“范围键”的一个很好的例子:参见

这里有两个主要思想:首先,它允许将一个键用作映射,因此您可以迭代所有键值。其次,它提供了映射项的保证顺序,因此您可以按键顺序进行遍历


这将使您能够凭借一点独创性,构建一个地图,为特定一周的所有邮件传递编制索引,然后按日期顺序扫描这些邮件。我认为这可以为您的问题提供非常有效的解决方案。

是否允许创建条目为关键映射的映射?问题是,我不认为我能把事情分解成每周一次的事情,因为一个给定的房子可能几周甚至几周都收不到邮件。然而,我可以从房子的角度来分析问题。因此,如果我的键值类似于HHH--DDD,其中“HHH”是门牌号,“DDD”是日期,那么(为了避免每次都要扫描所有条目),我可以为每个房子制作一个范围键,然后使用地图调用我感兴趣的范围键。换句话说,为了避免说map:keys($map)[.gt 30-0000-00-00][.le concat($house,'-',$date)][last()],我可以使用这个外部映射调用与house$house关联的范围映射,极大地减少了必须扫描的关键条目的数量。因此,如果$big.map是其条目是各个范围键相等的映射的映射(key=house number),那么我将查看:map:key(map:get($big,map,$house.number))[.lt$date as string][last()](很抱歉,我意识到我的术语太草率了……当我说“range key”或“range map”时,我指的是使用基于range key构建的saxon:key-map()函数构建的地图。)这一切在理论上看起来都是可行的。请随意尝试,如果遇到麻烦,请在saxonica.plan.io与我们联系,继续前进。