【问题标题】:XSLT3 Algorithm Q: Fastest way to find data based on timestamps -- Most recent before date XXSLT3 算法 Q:基于时间戳查找数据的最快方法——日期 X 之前的最新
【发布时间】:2013-09-16 11:08:47
【问题描述】:

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

如果我正在查找“查找时间戳 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 个特定房屋的数据。

D.存在 100 个日期的信息,大多数房屋会在这 100 个日期中的 3-20 个日期收到邮件。

E.邮件可以全天投递。所以在某一天,一个人可以先得到第 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:小地图

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

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

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

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

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

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

但是,构造部分存在差异。这些地图需要针对每个组进行一次操作,然后对每个房屋进行一次地图操作。我预计构建密钥所需的时间会更少。

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

如果原文档已经排序,那么key可能会更快?

【问题讨论】:

    标签: xslt map key saxon xslt-3.0


    【解决方案1】:

    抱歉,您已经非常仔细地考虑和时间来提出这个问题,我真的很想给予同样的关注和关注来回答它,但我没有时间。

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

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

    这应该可以让您稍微巧妙地构建一个地图,例如为特定一周的所有邮政投递建立索引,然后按日期顺序扫描这些。我认为这可以为您的问题提供非常有效的解决方案。

    【讨论】:

    • 是否允许创建条目为键映射的映射?问题是我认为我无法将事情分解成每周的部分,因为给定的房子可能几周或几周都不会收到邮件。但是,我可以在房屋方面进行分解。因此,如果我的键值类似于 HHH---DDD,其中“HHH”是门牌号,“DDD”是日期,那么(为了避免每次都扫描所有条目),我基本上可以为每个房子,然后使用地图调用我感兴趣的范围键。
    • 换句话说,为了避免不得不说 map:keys($map)[. gt 30-0000-00-00][。 le concat($house,'---',$date)][last()],我可以使用这个外部映射来调用与 house $house 关联的范围映射,从而大大减少了关键条目的数量必须扫描。
    • 因此,如果 $big.map 是其条目是单个范围键设备映射的映射(键 = 门牌号),那么我将查看:map:key(map:get ($big,map, $house.number))[. lt $date-as-string][last()] (对不起,我意识到我对术语很草率......当我说“范围键”或“范围图”时,我的意思是使用构建的地图saxon:key-map() 函数建立在范围键上。)
    • 这一切在理论上看起来都是可行的。随意尝试,如果遇到麻烦,请通过 saxonica.plan.io 与我们联系以继续前进。
    猜你喜欢
    • 2019-12-23
    • 1970-01-01
    • 2018-07-14
    • 2010-12-14
    • 1970-01-01
    • 2011-05-01
    • 2020-01-02
    • 2013-11-15
    • 1970-01-01
    相关资源
    最近更新 更多