XSLT3算法问:基于时间戳查找数据的最快方法 - 最近在日期X之前

时间:2013-09-16 11:08:47

标签: xslt map key saxon xslt-3.0

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

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

以下是具体内容: 想象一下,你有一个由300个房屋组成的集群,每个房屋偶尔会收到邮件。您正在监控他们获得的邮件类型。假设您关注的是15类邮件。

感兴趣的问题是“在D日前或之前送到房子的最新邮件类别是什么?”

一个。被引用的数据文件具有以下形式:

<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中。数据文件不一定要排序。如果这对最佳做法产生影响,请在答案中注明。

℃。即使在数据文件中跟踪了约300个房屋,我也只需要60个特定房屋的数据用于我的工作。

d。信息存在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)"/>

问题是构建此映射需要60 * 100 map {}命令,其中只有10%将被使用。还有几个电话来处理失踪日问题。

解决方案2:小地图

使用地图的另一个选项是将给定房屋的所有邮件数据与该house_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)"/>

然后,为了回答给定日期的问题,您需要在与该房屋相关的少量数据中进行选择:

日期之前的最新邮件类别d为house x = map:get($ mail.map,x)[current()/ date le d] [last()] / @ mail_category

这显然需要较少的工作来创建地图,但由于额外的过滤,检索数据每次需要更多的工作。还有一个问题是“大地图”解决方案允许我将房屋/日期直接连接到我想要的值[邮件类型],而这种方法要求我将键值连接到节点,所以会有是从该节点读出邮件类别信息的额外费用。

这比解决方案1的最后一个优势是,它很容易涵盖“在时间T 之前或之前的最新邮件类型是什么”的替代问题[所以而不是基于日期它基于实际时间戳。]

解决方案3:密钥 另一个选择是使用密钥,将给定房屋的所有邮件键入其house_id。从理论上讲,这应该与“小地图”选项非常相似。您使用密钥只检索所需房屋的邮件,然后过滤以选择最近但在所需日期之前或之后的邮件。

然而,建筑部分存在差异。这些地图需要一个for-each-group操作,然后是每个房子的一个map操作。密钥的构造花费的时间更少。

另一方面,密钥仅适用于文档模式。如果原始文档没有排序,那么我需要对文档进行排序并在内存中创建一个新文档以进行处理。我不能简单地在排序的节点序列上构建密钥。我不知道在内存中创建这个文档的相对成本,但我想这不仅仅是在解决方案2中构建地图所需的时间。

如果原始文档已经排序,那么密钥可能更快?

1 个答案:

答案 0 :(得分:0)

对不起,您已经非常仔细地考虑并制定了这个问题,我真的想给予同等的关注和回答,但我没有时间。

当然,地图和键的问题在于它们只进行相等匹配。我不知道你是否对使用扩展感兴趣,但对于Saxon 9.5中引入的“范围键”来说,它看起来很好:见http://www.saxonica.com/documentation/index.html#!functions/saxon/key-map

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

这应该允许您通过一点点聪明才能构建一个地图,该地图可以为特定周的所有邮政投递编制索引,然后按日期顺序扫描这些邮件。我认为这可以为你的问题提供一个非常有效的解决方案。