2010-04-28 19 views
14

Tôi có một bảng hàng ~ 300.000; bao gồm các thuật ngữ kỹ thuật; được truy vấn bằng cách sử dụng các chỉ mục PHP và MySQL + FULLTEXT. Nhưng khi tôi tìm kiếm một cụm từ sai; ví dụ "siêu văn bản"; tự nhiên không đưa ra kết quả."Ý của bạn là" tính năng trên cơ sở dữ liệu từ điển

Tôi cần "biên dịch" các lỗi viết nhỏ và nhận bản ghi gần nhất từ ​​cơ sở dữ liệu. Làm thế nào tôi có thể thực hiện như vậy feaure? Tôi biết (thực sự, học được ngày hôm nay) về khoảng cách Levenshtein, thuật toán Soundex và Metaphone nhưng hiện tại không có ý tưởng vững chắc để thực hiện điều này để truy vấn cơ sở dữ liệu.

Trân trọng. (Xin lỗi về tiếng Anh nghèo của tôi, tôi đang cố gắng để làm hết sức mình)

Trả lời

11

Xem bài viết này cho cách bạn có thể implement Levenshtein distance in a MySQL stored function.

Đối với hậu thế, gợi ý của tác giả là để làm điều này:

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 

Ông cũng cung cấp một phương pháp helper LEVENSHTEIN_RATIO đó sẽ đánh giá tỷ lệ tổng ký tự/khác nhau, chứ không phải là một chỉnh sửa khoảng cách thẳng. Ví dụ, nếu nó là 60%, thì ba phần năm các ký tự trong từ nguồn khác với từ đích.

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 
+0

Ngoài ra với hậu thế, đây là mã của Jason Rust dựa trên mã của Arnold Fribble, lần lượt, một phần dựa trên công việc của Joseph Gama. – webbiedave

+0

D'oh. Bằng cách nào đó tôi nghĩ rằng tôi đã đề cập đến tác giả, nhưng rõ ràng là tôi đã không. Cảm ơn bạn đã điền vào các khoảng trống, @webbiedave. –

+1

Cảm ơn UDF, nó rất hữu ích. Nhưng nếu tôi chạy một truy vấn như "SELECT * FROM bảng WHERE HAVING LEVENSHTEIN ('từ khóa', trường) <3 (hoặc hơn)" trên một bảng hàng ~ 300k, nó (rõ ràng) mất độ tuổi để hoàn thành. Tôi cũng đã thử giảm các hàng tìm kiếm bên trong (sử dụng WHERE CHAR_LENGTH ('field') GIỮA CHAR_LENGTH ('keyword') - 1 AND CHAR_LENGTH ('keyword') + 1) nhưng nó trả về kết quả trong whopping 35 seconds :) Bạn (hoặc những người khác) có ý tưởng tăng tốc truy vấn này? – Hazar

1

Từ ý kiến ​​của http://dev.mysql.com/doc/refman/5.0/en/udf-compiling.html

bây giờ tôi tải về các gói từ kho 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; 
-1

Tại sao không thêm một cột bảng để lưu trữ các từ trong thay thế của nó (ví dụ, Soundex) hình thức? theo cách đó, nếu SELECT đầu tiên của bạn không tìm thấy kết quả khớp chính xác, bạn có thể thực hiện tìm kiếm thứ hai để tìm các biểu mẫu thay thế phù hợp.

Bí quyết là mã hóa từng từ để các biến thể sai chính tả cuối cùng được chuyển đổi thành cùng một dạng thay thế.

0

Tôi đề nghị bạn generate typo các biến thể trên đầu vào truy vấn.

tức hyperpext> {hyperpeext, hipertext, ...} vv

Một trong số đó chắc chắn là đúng chính tả (đặc biệt đối với lỗi chính tả phổ biến)

Cách bạn xác định các trận đấu rất có thể là để thực hiện tra cứu cho từng chỉ mục cho biết tần suất tài liệu của thuật ngữ. (ý nghĩa?)

Các vấn đề liên quan