Mysql全文搜索,自然语言模式:order by“;“亲密度”;
我正在使用MYSQL的全文搜索功能(在MYSQL 5.6.33中) 如果我在自然语言模式下匹配一个邮政编码,输入一个字符,我会得到一些不错的结果,包括带有“正确”邮政编码的结果,但它们并不接近顶部 例如,有10所学校的邮政编码为“BN2 1TL”。我故意将其拼错为“BN2 1TM”,并进行如下搜索:Mysql全文搜索,自然语言模式:order by“;“亲密度”;,mysql,pattern-matching,full-text-search,levenshtein-distance,Mysql,Pattern Matching,Full Text Search,Levenshtein Distance,我正在使用MYSQL的全文搜索功能(在MYSQL 5.6.33中) 如果我在自然语言模式下匹配一个邮政编码,输入一个字符,我会得到一些不错的结果,包括带有“正确”邮政编码的结果,但它们并不接近顶部 例如,有10所学校的邮政编码为“BN2 1TL”。我故意将其拼错为“BN2 1TM”,并进行如下搜索: SELECT record_id, address_string, MATCH (address_string) AGAINST ("BN2 1TM" IN NATURAL LANGUAGE
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;
仔细检查,这是因为搜索已经买回了所有在其地址字符串列中包含“BN2”
或“1TM”
的结果,并且它们的分数完全相同,因此是随机顺序的
这是完全合理的行为,但如果我能将“亲密度”考虑在内,那就太好了,这意味着,对于“BN2 1TM”
上的搜索,“BN2 1TL”
的得分将高于“BN2 3PQ”
。有办法做到这一点吗
编辑:我记得这种亲密度在技术上被称为“Levenshtein距离”,这是一个参考值,用于确定将一个字符串转换为另一个字符串需要多少替换。所以我想我的问题可能是“我能不能让MYSQL全文版自然语言模式评分来考虑Levenshtein距离”首先,MYSQL全文版在开放式搜索方面不如Lucene这样的专用系统好
有一种称为Levenshtein距离的算法,它计算字符转换的数量——距离——将一个字符串转换为另一个字符串
因此,将“BN2 1TM”更改为“BN2 1MT”(换位)的距离为2。将其更改为“BN2 1TX”的距离为1
Levenshtein距离对于短语来说不是非常有用,除非它们几乎完全相同。将“apachesphinx”更改为“MySQL FULLTEXT”,则距离为14,即较长字符串的长度。但它对邮政编码、零件号和其他短结构单词很有用
您可以尝试这样的方法,首先获取最接近的值
SELECT city, county, postcode
FROM table
ORDER BY levenshtein(postcode, 'BN2 1MT') ASC
然后,您只需要一个存储函数来计算Levenshtein距离。(这不是全文版。)
从,这里是这样一个存储函数。但是要注意,它速度不快,而且不能使用索引。因此,如果在执行此操作之前缩小搜索范围,您将获得更好的性能
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 ;
分隔符$$
创建函数levenshtein(s1 VARCHAR(255),s2 VARCHAR(255))
返回整数
确定性
开始
声明s1_len、s2_len、i、j、c、c_temp、cost INT;
声明s1_char;
--最大strlen=255
声明cv0,cv1 VARBINARY(256);
设置s1长度=字符长度(s1),
s2_len=字符长度(s2),
cv1=0x00,
j=1,
i=1,
c=0;
如果s1=s2,则
返回0;
如果s1_len=0,则
返回s2_len;
如果s2_len=0,则
返回s1_len;
其他的
而j是一个有趣的问题。如果您能解释一下为什么要放弃Lucene而改为MySQL全文版,可能会对我们有所帮助。大多数像你这样有问题的人在遇到像你这样的问题时会放弃MySQL,转而使用Lucene。请回答您的问题。@O.Jones我无意粗鲁,但实际上我不想谈论我使用MySQL全文的动机,因为这与问题无关。@O.Jones我已经删除了对Lucene的引用,因为它可能(而且显然是这样)分散了问题的注意力。你需要,然后你可能可以做一个orderBy。@vivek_23我刚才在编辑中说了同样的话,很有趣。我想我需要按比赛分数排序,然后是Levenshtein函数。MYSQL中是否存在这种情况?非常感谢。我认为你是对的,我认为一个很好的方法是获得正常的全文结果。然后,如果第一名有“平局”,得分方面,我可以在平局的第一名结果上运行Levenshtein以进一步排序。