【发布时间】: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