So với MySQL, MariaDB đã có nhiều thay đổi về cấu trúc CSDL và cách xử lý Plugin. Giờ đây, dù có file nguồn của Levenshtein cũng khó có thể biên dịch cho MariaDB.
— Update ——-
Khi biên dịch từ mã nguồn, chúng ta cần
libmysqlclient-dev
nhưng khi dùng MariaDB thì thư viện này không tương thích! Chúng ta tìm thư viện tương tự qua dòng lệnh
apt-cache search libmysqlclient default-libmysqlclient-dev - MySQL database development files (metapackage) libcrypt-mysql-perl - Perl module to emulate the MySQL PASSWORD() function libglpk40 - linear programming kit with integer (MIP) support libmariadbclient-dev-compat - MariaDB database development files (libmysqlclient compatibility)
Vì vậy, chúng ta cài đặt
apt-get install libmariadbclient-dev-compat
Sau đó biên dịch mã C bình thường.
sudo gcc -fPIC -I /usr/include/mysql/ -shared -o levenshtein.so levenshtein.c
A. Mã nguồn Levenshtein
Trong trường hợp 2, mở file nguồn damlevlim.c sửa setlocale thành
setlocale(LC_CTYPE, "en_US.UTF-8")
rồi chạy raspi-config để cài đặt locale en_US.UTF-8
Tiếp theo, trong file Makefile bỏ dòng
CPP_FLAGS_32 := -m32
Sau khi biên dịch theo hướng dẫn của file nguồn, tìm thư mục plugin của MariaDB để chép file .so vào
++ Tìm thư mục plugin của MariaDB
MariaDB [(none)]> SHOW VARIABLES LIKE 'plugin_dir'; +---------------+------------------------------------------------+ | Variable_name | Value | +---------------+------------------------------------------------+ | plugin_dir | /usr/lib/arm-linux-gnueabihf/mariadb18/plugin/ | +---------------+------------------------------------------------+ 1 row in set (0.00 sec)
Hoặc dùng lệnh tìm kiếm
find / -name plugin -type d /usr/share/phpmyadmin/js/jqplot/plugins /usr/share/phpmyadmin/templates/server/plugins /usr/share/phpmyadmin/libraries/plugins /usr/share/phpmyadmin/libraries/properties/plugins /usr/share/doc/dovecot-core/sieve/plugins /usr/lib/python2.7/dist-packages/certbot/plugins /usr/lib/gcc/arm-linux-gnueabihf/6/plugin /usr/lib/arm-linux-gnueabihf/colord-plugins /usr/lib/arm-linux-gnueabihf/mariadb18/plugin /usr/lib/arm-linux-gnueabihf/krb5/plugins /usr/lib/arm-linux-gnueabihf/libv4l/plugins
++ Sao chép file .so vào thư mục plugin
cp ./levenshtein.so /usr/lib/arm-linux-gnueabihf/mariadb18/plugin
++ Sau đó vào MariaDB để tạo các hàm tương ứng (tùy theo hướng dẫn của file nguồn).
— End Update ——–
B. Mã MySQL của hàm Levenshtein
Chúng ta có thể sử dụng một hàm LEVENSHTEIN hoàn toàn bằng ngôn ngữ MySQL, hỗ trợ Unicode UTF-8, tất nhiên là có chậm hơn như khi biên dịch từ C.
1. Hàm Levenshtein (s1, s2) trả lại khoảng cách tính theo số lượt thay đổi ký tự để s1 thành s2
-- Levenshtein function -- Source: https://openquery.com.au/blog/levenshtein-mysql-stored-function -- Levenshtein reference: http://en.wikipedia.org/wiki/Levenshtein_distance -- Arjen note: because the levenshtein value is encoded in a byte array, distance cannot exceed 255; -- thus the maximum string length this implementation can handle is also limited to 255 characters. DELIMITER $$ DROP FUNCTION IF EXISTS LEVENSHTEIN $$ CREATE FUNCTION LEVENSHTEIN (s1 VARCHAR(255) CHARACTER SET utf8, s2 VARCHAR(255) CHARACTER SET utf8) RETURNS INT DETERMINISTIC BEGIN DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT; DECLARE s1_char CHAR CHARACTER SET utf8; -- max strlen=255 for this function 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); END IF; WHILE (j <= s2_len) DO SET cv1 = CONCAT(cv1, CHAR(j)), j = j + 1; END WHILE; WHILE (i <= s1_len) DO SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = CHAR(i), j = 1; WHILE (j <= s2_len) DO SET c = c + 1, cost = IF(s1_char = SUBSTRING(s2, j, 1), 0, 1); SET c_temp = ORD(SUBSTRING(cv1, j, 1)) + cost; IF (c > c_temp) THEN SET c = c_temp; END IF; SET c_temp = ORD(SUBSTRING(cv1, j+1, 1)) + 1; IF (c > c_temp) THEN SET c = c_temp; END IF; SET cv0 = CONCAT(cv0, CHAR(c)), j = j + 1; END WHILE; SET cv1 = cv0, i = i + 1; END WHILE; RETURN (c); END $$ DELIMITER ;
2. Hàm levenshtein_ratio(s1, s2) tính tỉ lệ % sự tương tự nhau giữa 2 chuỗi s1 và s2.
DELIMITER $$ DROP FUNCTION IF EXISTS LEVENSHTEIN_RATIO $$ CREATE FUNCTION LEVENSHTEIN_RATIO (s1 VARCHAR(255) CHARACTER SET utf8, s2 VARCHAR(255) CHARACTER SET utf8) RETURNS INT DETERMINISTIC BEGIN DECLARE s1_len, s2_len INT; SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2); RETURN ROUND((levenshtein(s1, s2) / IF(s1_len > s2_len, s1_len, s2_len)) * 100); END $$ DELIMITER ;
3. Hàm Lev_Locate (s1, s, ratio) trả lại vị trí trên chuổi s tại đó s1 có (khoảng cách Levenshtein < ratio) với chuổi con của s
-- Lev_Locate function -- LNT <lnt@ly-le.info> -- DELIMITER $$ DROP FUNCTION IF EXISTS LEV_LOCATE $$ CREATE DEFINER=`root`@`localhost` FUNCTION `LEV_LOCATE`(`s1` VARCHAR(255) CHARSET utf8, `s` VARCHAR(255) CHARSET utf8, `ratio` INT(3) UNSIGNED) RETURNS int(11) DETERMINISTIC BEGIN DECLARE s1_len, s_len, p INT; SET s1_len = CHAR_LENGTH(s1), s_len = CHAR_LENGTH(s), p = 1; WHILE (s1_len + p <= s_len + 1) DO IF LEVENSHTEIN_RATIO(SUBSTRING(s, p, s1_len), s1) >= ratio THEN RETURN p; END IF; SET p = p + 1; END WHILE; RETURN 0; END$$ DELIMITER ;
Chú thích
- Các hàm MySQL chạy khá chậm, có thể tính bằng giây
- Tốt nhất là tìm cách biên dịch mã nguồn C của hàm Levenshtein cho MariaDB
- damlevlim (mã nguồn 2) dùng iconv để cắt dấu chuỗi UTF-8, bản chất vẫn là xử lý chuỗi ascii. Bản này chưa hoàn thiện, còn lỗi, code thừa và nhiều mã debug.
- Levenshtein đã hỗ trợ UTF-8, đang test hoàn thiện.
Chép vào thư mục plugin của MariaDB rồi tạo hàm qua câu lệnh:CREATE FUNCTION lev RETURNS INT SONAME 'levenshtein.so'; CREATE FUNCTION lev_k RETURNS INT SONAME 'levenshtein.so'; CREATE FUNCTION lev_ratio RETURNS REAL SONAME 'levenshtein.so'; CREATE FUNCTION lev_k_ratio RETURNS REAL SONAME 'levenshtein.so';