【问题标题】:"Did you mean" feature on a dictionary database字典数据库上的“你是说”功能
【发布时间】:2011-02-13 10:36:20
【问题描述】:

我有一个 ~300.000 行表;其中包括技术术语;使用 PHP 和 MySQL + FULLTEXT 索引进行查询。但是当我搜索输入错误的术语时;例如“超文本”;自然没有结果。

我需要“补偿”小的书写错误并从数据库中获取最近的记录。我怎样才能完成这样的功能?我知道 Levenshtein 距离、Soundex 和 Metaphone 算法,但目前还没有一个可靠的想法来实现它来查询数据库。

谢谢

【问题讨论】:

    标签: php mysql search full-text-search fuzzy-search


    【解决方案1】:

    请参阅这篇文章了解如何implement Levenshtein distance in a MySQL stored function

    为了后人,作者的建议是这样做:

    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;
            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
    

    他还提供了一个 LEVENSHTEIN_RATIO 辅助方法,该方法将评估不同/总字符的比率,而不是直接的编辑距离。例如,如果是 60%,那么源词中五分之三的字符与目的词不同。

    CREATE FUNCTION LEVENSHTEIN_RATIO (s1 VARCHAR(255), s2 VARCHAR(255))
      RETURNS INT
        DETERMINISTIC
          BEGIN
            DECLARE s1_len, s2_len, max_len INT;
            SET s1_len = LENGTH(s1), s2_len = LENGTH(s2);
            IF s1_len > s2_len THEN SET max_len = s1_len; ELSE SET max_len = s2_len; END IF;
            RETURN ROUND((1 - LEVENSHTEIN(s1, s2) / max_len) * 100);
          END
    

    【讨论】:

    • 为了后代,这是 Jason Rust 的代码,它基于 Arnold Fribble 的代码,而 Arnold Fribble 的代码又部分基于 Joseph Gama 的工作。
    • 哦。不知怎的,我以为我提到了作者,但显然我没有。感谢您填补空白,@webbiedave。
    • 感谢 UDF,它非常有用。但是,如果我在 ~300k 行表上运行类似“SELECT * FROM table WHERE HAVING LEVENSHTEIN ('keyword', field) field) BETWEEN CHAR_LENGTH('keyword')-1 AND CHAR_LENGTH('keyword')+1 ),但它会在 35 秒内返回结果 :) 你(或其他人)有加快查询速度的想法吗?
    【解决方案2】:

    来自http://dev.mysql.com/doc/refman/5.0/en/udf-compiling.html的cmets

    现在我从 mysql udf 存储库下载包 http://empyrean.lib.ndsu.nodak.edu/~nem/mysql/

    wget http://empyrean.lib.ndsu.nodak.edu/~nem/mysql/udf/dludf.cgi?ckey=28
    
    ll
    
    tar -xzvf dludf.cgi\?ckey\=28
    
    gcc -shared -o libmysqllevenshtein.so mysqllevenshtein.cc -I/usr/include/mysql/
    
    mv libmysqllevenshtein.so /usr/lib
    
    mysql -uroot -pPASS
    
    mysql> use DATABASE
    
    mysql> CREATE FUNCTION levenshtein RETURNS INT SONAME 'libmysqllevenshtein.so';
    
    mysql> select levenshtein(w1.word,w2.word) as dist from word w1, word w2 where ETC........... order by dist asc limit  0,10;
    

    【讨论】:

      【解决方案3】:

      我建议您在查询输入上使用generate typo 变体。

      即hyperpext > { hyperpeext, hipertext, ... } 等

      其中一个肯定是正确的拼写(尤其是常见的拼写错误)

      确定最可能匹配的方法是在索引上查找每个匹配项,该索引会告诉您该术语的文档频率。 (有意义吗?)

      【讨论】:

        【解决方案4】:

        为什么不添加一个表格列来以替代形式(例如 Soundex)存储单词?这样,如果您的第一个 SELECT 未找到完全匹配的内容,您可以进行第二次搜索以查找匹配的替代形式。

        诀窍是对每个单词进行编码,以便拼写错误的变体最终转换为相同的替代形式。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-04-03
          • 2013-09-28
          • 2020-11-07
          • 1970-01-01
          • 1970-01-01
          • 2011-12-10
          • 2011-04-26
          • 1970-01-01
          相关资源
          最近更新 更多