Levenshtein UTF-8 cho MariaDB

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

  1. Không hổ trợ UTF-8: đâyđây .Cần bỏ hết dấu trước khi so sánh.
  2. Hổ trợ UTF-8: đây

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@lyle.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';

Download

Comments Off on Levenshtein UTF-8 cho MariaDB

Filed under Software

Comments are closed.