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

时间:2013-07-31 15:13:13

标签: mysql indexing geolocation

我在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描述了令牌和位置描述了原始文本中令牌的位置。

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

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

??? 这是我卡住的地方,因为我怎么能告诉它会查询找到的值?因为当结果包含position = 12的行时,所有其他有效行的位置应为> = 2& position =< 22

BTW:它是否与半径内的地理位置查询相似?

编辑:以下是样本数据的实际进展:http://sqlfiddle.com/#!2/52f48/2

查询工作正常,但尚未完成,因此如果2x令牌1在文档中匹配,则它也是“有效”结果,这当然是错误的。当有所有给定的令牌时它唯一正确。并且解决方案必须可扩展到3+令牌。

1 个答案:

答案 0 :(得分:2)

我将从 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 ,则将该结果集缩小到每个表中的一个<来自第二个表的em> 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的结果等于你关心的标记数减一(第一个表中有1个标记,第二个表中有n-1个标记)。

您可以使用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

然后,该查询的结果数量应该告诉您有多少文档包含您在指定范围内关注的所有标记。