Mysql全文搜索,自然语言模式:按“亲密度”排序
Posted
技术标签:
【中文标题】Mysql全文搜索,自然语言模式:按“亲密度”排序【英文标题】:Mysql Fulltext search, natural language mode: order by "closeness" 【发布时间】:2019-08-01 16:31:20 【问题描述】:我正在使用 mysql 的全文搜索功能(在 Mysql 5.6.33 中)。
如果我在 NATURAL LANGUAGE 模式下进行 MATCH,对于带有一个字符拼写错误的邮政编码,我会得到一些不错的结果,包括使用“正确”邮政编码的结果,但它们不在顶部附近。
例如,有 10 所学校的邮政编码为 "BN2 1TL"
。我故意把它拼错为"BN2 1TM"
并进行如下搜索:
SELECT record_id, address_string,
MATCH (address_string) AGAINST ("BN2 1TM" IN NATURAL LANGUAGE MODE) AS score
FROM schools
WHERE MATCH (address_string) AGAINST ("BN2 1TM" IN NATURAL LANGUAGE MODE) > 0
ORDER BY score DESC;
仔细观察,这是因为搜索已经回购了所有在其address_string
列中具有"BN2"
或"1TM"
的结果,并且它们都具有完全相同的分数,因此是随机顺序的,有效. .
这是完全合理的行为,但如果我能得到考虑“亲密度”的分数,那就太好了,这意味着,对于在 "BN2 1TM"
上的搜索,"BN2 1TL"
的得分将高于 @987654332 @。有没有办法做到这一点?
编辑:我记得这种接近在技术上称为“Levenshtein 距离”,它是对Levenshtein algorithm 的引用,用于确定将一个字符串转换为另一个字符串需要多少替换。所以我想我的问题可能是 “我可以通过 MYSQL FULLTEXT NATURAL LANGUAGE MODE 评分来考虑 Levenshtein 距离”?
【问题讨论】:
有趣的问题。如果您解释为什么要放弃 Lucene 而使用 MySQL FULLTEXT,这可能会对我们有所帮助。大多数遇到像您这样的问题的人在遇到像您这样的问题时会放弃 MySQL 转而使用 Lucene。请edit您的问题。 @O.Jones 我并不是要粗鲁,但实际上我更希望 not 讨论一下我使用 MySQL FULLTEXT 的动机,因为它不是与问题相关。 @O.Jones 我已经删除了对 Lucene 的引用,因为它可能(并且确实显然)分散了问题的重点。 你需要levenshtein distance,然后你就可以下单了。 @vivek_23 我刚刚在编辑中说了同样的话,很有趣。我想我需要按 MATCH 分数排序,然后是 Levenshtein 函数。这在MYSQL中是否存在? 【参考方案1】:首先,MySQL 全文在开放式搜索方面不如 Lucene 等专用系统好。
有一种算法,称为 Levenshtein distance,它计算字符转换的数量——距离——将一个字符串变为另一个字符串。
因此,将“BN2 1TM”更改为“BN2 1MT”(换位)的距离为 2。将其更改为“BN2 1TX”的距离为 1。
Levenshtein 距离对于短语来说并不是非常有用,除非它们几乎完全相同。将“Apache Sphinx”更改为“MySQL FULLTEXT”会给出 14 的距离,即较长字符串的长度。但它对于邮政编码、零件编号和其他简短的结构化单词很有用。
您可以尝试这样的方法来首先获取最接近的值。
SELECT city, county, postcode
FROM table
ORDER BY levenshtein(postcode, 'BN2 1MT') ASC
然后,您只需要一个存储函数来计算 Levenshtein 距离。 (这不是 FULLTEXT 内置的。)
来自this source,这是一个这样的存储函数。但请注意,它速度不快,而且不能使用索引。因此,如果您可以在此之前缩小搜索范围,您将获得更好的性能。
DELIMITER $$
CREATE FUNCTION levenshtein( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
DECLARE s1_char CHAR;
-- max strlen=255
DECLARE cv0, cv1 VARBINARY(256);
SET s1_len = CHAR_LENGTH(s1),
s2_len = CHAR_LENGTH(s2),
cv1 = 0x00,
j = 1,
i = 1,
c = 0;
IF s1 = s2 THEN
RETURN 0;
ELSEIF s1_len = 0 THEN
RETURN s2_len;
ELSEIF s2_len = 0 THEN
RETURN s1_len;
ELSE
WHILE j <= s2_len DO
SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
END WHILE;
WHILE i <= s1_len DO
SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
WHILE j <= s2_len DO
SET c = c + 1;
IF s1_char = SUBSTRING(s2, j, 1) THEN
SET cost = 0; ELSE SET cost = 1;
END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
IF c > c_temp THEN SET c = c_temp; END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
IF c > c_temp THEN
SET c = c_temp;
END IF;
SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
END WHILE;
SET cv1 = cv0, i = i + 1;
END WHILE;
END IF;
RETURN c;
END$$
DELIMITER ;
【讨论】:
非常感谢。我认为您是对的,我认为一个好的方法是正常获得 FULLTEXT 结果。然后,如果第一名出现“平局”,在得分方面,我可以在并列第一的结果上运行 Levenshtein 以进一步排序。以上是关于Mysql全文搜索,自然语言模式:按“亲密度”排序的主要内容,如果未能解决你的问题,请参考以下文章