获取特定范围/半径内的所有行(文档术语矩阵)

Posted

技术标签:

【中文标题】获取特定范围/半径内的所有行(文档术语矩阵)【英文标题】:Get all rows within a specifiy range/radius (Document Term Matrix) 【发布时间】:2013-07-31 15:13:13 【问题描述】:

我在 mysql 中存储了一个文档术语矩阵,并希望获得如下查询的结果:

示例: 获取 token_id '1' 和 token_id '2'(但甚至可能超过 2)在 10 个单词范围内的所有行。

我的桌子:

dt_matrix_token_id int(11) PK AUTO_INCREMENT,
token_id int(11),
storage_data_id int(11),
position int(11)

所以基本上 token_id 描述了令牌,而 position 描述了令牌在原始文本中的哪个位置。

通过 token_id 选择行不是问题,问题在于我如何在查询中描述这两个词必须在特定的“半径/范围”内。

Select * FROM dt_matrix_token WHERE token_id IN(1,2) AND ???

??? 这是我卡住的地方,因为我怎么知道它应该查询找到的值?因为当结果包含 position = 12 的行时,所有其他有效行的 position >= 2 & position =

顺便说一句:是否类似于半径内的地理位置查询?

编辑:这是我对示例数据的实际进展:http://sqlfiddle.com/#!2/52f48/2

查询工作正常,但还没有完成,所以如果 2x token 1 在文档中匹配,它也是一个“有效”结果,这当然是错误的。只有当所有给定的令牌都是正确的。并且解决方案必须可以扩展到 3 个以上的令牌。

【问题讨论】:

您能否提供一些数据,以便我们了解问题所在。就目前而言,我不能将其视为地理定位解决方案。 我现在进行了编辑并粘贴了一个 sqlfiddle 链接,其中发布了示例数据 + 我的进度。 【参考方案1】:

我将从与 dt_matrix_token 表的第二个实例连接的 dt_matrix_token 表中的查询开始,其中两个实例都有一个 token_id在您感兴趣的值范围内,但它们不能具有相同的值。

它们还应该有一个匹配的storage_data_id(即它们在同一个文档中),并且第二个标记的位置必须大于或等于第一个。

SELECT mt1.dt_matrix_token_id, mt1.storage_data_id,
  mt1.token_id AS token_id1, mt2.token_id AS token_id2,
  mt1.position AS position1, mt2.position AS position2
FROM dt_matrix_token AS mt1
JOIN dt_matrix_token AS mt2
WHERE mt1.token_id IN (1,2,3) 
  AND mt2.token_id IN (1,2,3)
  AND mt1.token_id <> mt2.token_id
  AND mt1.storage_data_id = mt2.storage_data_id
  AND mt2.position >= mt1.position 

这为您提供了您关心的每一对连续的令牌。

现在,如果您按第一个表中的 dt_matrix_token_id 分组,并结合第二个表中的 token_id,您可以将该组结果缩小到每个 token_id 来自第二个表中的每个令牌。

当对第二张表的结果进行分组时,它是您关心的最小位置。由于第二个记号总是跟在第一个记号后面,因此您可以找到最接近第一个记号的位置。

SELECT mt1.dt_matrix_token_id, mt1.storage_data_id,
  mt1.token_id AS token_id1, mt2.token_id AS token_id2,
  mt1.position AS position1, MIN(mt2.position) AS position2
FROM dt_matrix_token AS mt1
JOIN dt_matrix_token AS mt2
WHERE mt1.token_id IN (1,2,3) 
  AND mt2.token_id IN (1,2,3)
  AND mt2.token_id <> mt1.token_id
  AND mt2.storage_data_id = mt1.storage_data_id
  AND mt2.position >= mt1.position 
GROUP BY mt1.dt_matrix_token_id, mt2.token_id

因此,现在,对于您关心的每个令牌实例,您都拥有与同一文档中跟随它的任何令牌最近的位置。

但你真正想要的是从第一个标记到它后面的任何标记的最大距离。所以你需要再次按dt_matrix_token_id分组,并计算到第二个位置最大值的距离(即每个token_id的最小值的最大值)。

SELECT dt_matrix_token_id, storage_data_id,
  MAX(position2)-position1 AS distance
FROM (
  SELECT mt1.dt_matrix_token_id, mt1.storage_data_id,
    mt1.position AS position1, MIN(mt2.position) AS position2
  FROM dt_matrix_token AS mt1
  JOIN dt_matrix_token AS mt2
  WHERE mt1.token_id IN (1,2,3) 
    AND mt2.token_id IN (1,2,3)
    AND mt2.token_id <> mt1.token_id
    AND mt2.storage_data_id = mt1.storage_data_id
    AND mt2.position >= mt1.position 
  GROUP BY mt1.dt_matrix_token_id, mt2.token_id
) AS temp
GROUP BY dt_matrix_token_id

但是,并非第一个表中的每个标记都将跟随您关心的所有其他标记。所以你需要确保每组结果的COUNT等于你关心的token数减一(第一个表中有1个token,第二个表中有n-1个token)。

您可以使用 HAVING 子句 - HAVING COUNT(*) = 3-1 执行此操作,其中该表达式中的 3 表示您正在搜索的令牌数。

现在,对于您关心的令牌的每个实例,然后是您关心的所有其他令牌(在同一个文档中),您拥有覆盖所有令牌的最短距离。

但是每个文档很可能会有多个结果,您真的只需要知道每种情况下最短的一个。所以现在您需要按 storage_data_id 进行分组并计算组中的最小距离。

SELECT storage_data_id, MIN(distance) AS distance
FROM (
  SELECT dt_matrix_token_id, storage_data_id,
    MAX(position2)-position1 AS distance
  FROM (
    SELECT mt1.dt_matrix_token_id, mt1.storage_data_id,
      mt1.position AS position1, MIN(mt2.position) AS position2
    FROM dt_matrix_token AS mt1
    JOIN dt_matrix_token AS mt2
    WHERE mt1.token_id IN (1,2,3) 
      AND mt2.token_id IN (1,2,3)
      AND mt2.token_id <> mt1.token_id
      AND mt2.storage_data_id = mt1.storage_data_id
      AND mt2.position >= mt1.position 
    GROUP BY mt1.dt_matrix_token_id, mt2.token_id
  ) AS temp
  GROUP BY dt_matrix_token_id
  HAVING COUNT(*) = 3-1
) AS temp
GROUP BY storage_data_id

这将为您提供包含您关心的所有标记的每个文档,以及覆盖所有这些标记的最小距离。要将结果限制在特定范围内的距离,您只需添加另一个 HAVING 子句即可。

HAVING distance <= 20

那么该查询的结果数应该告诉您有多少文档包含指定范围内您关心的所有标记。

【讨论】:

感谢您的回答!这个解决方案听起来不错。我会试一试,然后给予赏金。 @unr3al011 在接近赏金期结束之前,请随时保持悬赏开放。这样你会鼓励更多的答案,你可能会发现有人会提出比我更好的解决方案。只是不要忘记在它到期之前奖励给某人。 到目前为止,您的解决方案效果很好,唯一的问题是查询速度很慢。我还添加了索引。什么是最好的选择? pastebin.com/raw.php?i=fCqQFXBe 除了你已经拥有的索引之外,我建议在(storage_data_id,position) 上添加一个索引以加快连接速度。我还建议直接指定令牌 ID,而不是使用这些子查询来匹配令牌名称,因为这似乎会强制搜索比必要的更多行。

以上是关于获取特定范围/半径内的所有行(文档术语矩阵)的主要内容,如果未能解决你的问题,请参考以下文章

从 Laravel 中的坐标获取半径内的用户

使用 mariaDb,mySQL 获取以 km 为单位的半径内的所有空间类型点

JavaRedis通过中心经纬度与半径获取范围内的结果集(类似附近的人)

JavaRedis通过中心经纬度与半径获取范围内的结果集(类似附近的人)

获取半径内的位置

显示用户在PHP中当前位置的10公里半径范围内的用户