Mysql全文搜索,自然语言模式:order by“;“亲密度”;

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

我正在使用MYSQL的全文搜索功能(在MYSQL 5.6.33中)

如果我在自然语言模式下匹配一个邮政编码,输入一个字符,我会得到一些不错的结果,包括带有“正确”邮政编码的结果,但它们并不接近顶部

例如,有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;
仔细检查,这是因为搜索已经买回了所有在其
地址字符串
列中包含
“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以进一步排序。