我正在使用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"在他们的address_string列中的结果,而且他们都有完全相同的分数,所以是随机的,有效的。.
这是完全合理的行为,但如果我能把“亲密度”考虑进去,那就太好了,也就是说,搜索"BN2 1TM""BN2 1TL"的得分会比"BN2 3PQ"高。有办法吗?
编辑:我记得这种亲密在技术上被称为“Levenshtein距离”,它是一个Levenshtein algorithm的引用,用于确定将一个字符串转换为另一个字符串需要多少替换。所以我想我的问题可能是“我能不能让MYSQL全文自然语言模式评分考虑到Levenshtein距离”?

最佳答案

首先,MySQL-fulltext在开放式搜索方面不如Lucene这样的专用系统。
有一个叫做Levenshtein距离的算法,它计算字符转换的次数——距离——把一个字符串转换成另一个字符串。
因此,将“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距离。(这不是全文本。)
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 ;

10-04 21:35