突出显示搜索结果:正则表达式字符排序规则?

问题描述 投票:0回答:3

当我运行全文 MySQL 查询时,由于 Unicode 字符排序规则,我将获得与以下所有内容相匹配的结果,无论我查询哪个:

saka, sakā, śāka, ṣaka
等。

我遇到的困难是突出显示搜索结果中的匹配项。使用标准正则表达式,我只能匹配并突出显示结果中的原始查询词——而不是所有整理的匹配项。

如何解决这个问题?我最初想到了这些方法:

  • 创建一个正则表达式模式,根据所有可能的变体分析目标结果。很容易就会变成一只臃肿的怪物。
  • 创建结果的标准化版本,在那里找到匹配项,并使用字符串位置作为突出显示的基础。

然而,与常规搜索结果突出显示相比,这两种方法都会产生大量的处理开销。第一种方法会产生巨大的 CPU 开销;第二个可能会消耗更少的 CPU,但为了结果而消耗至少两倍的 RAM。有什么建议吗?

附注如果相关:我正在处理的特定字符集(带扩展名的梵文音译的 IAST)具有 L 和 N 的三种变体; M、R 和 S 的两个变体;以及A、D、E、H、I、T和U的一种变体;总共 A-Z + 19 个变音符号变体; + 大写(这里没有问题)。

php mysql regex collation unicode-normalization
3个回答
2
投票

使用 MySQL 及其 REGEXP,您只能找到与 REGEXP 匹配的行。您无法在该列中找到匹配项。

REGEXP 和 LIKE 都遵循相关列的排序规则,但这无助于定位列中的文本。

查看 MariaDB 及其 REGEXP_REPLACE。

MySQL 至少有一个与之相关的错误: http://bugs.mysql.com/bug.php?id=70767


2
投票

这就是我最终所做的。似乎对性能的影响可以忽略不计。 (我没有注意到!)

首先,一个将查询词转换为迭代变体的正则表达式的函数:

function iast_normalize_regex($str) {

    $subst = [ 
        'a|ā', 'd|ḍ', 'e|ӗ', 'h|ḥ', 'i|ī', 'l|ḷ|ḹ', 'm|ṁ|ṃ', 
        'n|ñ|ṅ|ṇ', 'r|ṛ|ṝ', 's|ś|ṣ', 't|ṭ', 'u|ū' 
        ];

    $subst_rex = [];

    foreach($subst as $variants) {
        $chars = explode('|', $variants);
        foreach($chars as $char) {
            $subst_rex[$char] = "({$variants})";
        }
    }

    $str_chars = str_split_unicode($str);

    $str_rex = '';
    foreach($str_chars as $char) {
        $str_rex .= !isset($subst_rex[$char]) ? $char : $subst_rex[$char];
    }

    return $str_rex;
}

这会将单词

saka
śaka
等变成
(s|ś|ṣ)(a|ā)k(a|ā)
。然后,使用变体迭代的单词模式来突出显示搜索结果:

$word = iast_normalize_regex($word);
$result = preg_replace("#({$word})#iu", "<b>$1</b>", $result);

Presto:我突出显示了所有变体。感谢您迄今为止的贡献,如果您能想到更好的方法来实现这一目标,请告诉我。干杯!


0
投票

我能够通过编写一个自定义 MySQL 函数来解决这个问题,该函数的工作方式类似于内部函数 LOCATE,只不过它将 unicode 字符串与非 unicode 字符串相匹配(反之亦然):

DROP FUNCTION IF EXISTS locate_like;

DELIMITER $$

CREATE FUNCTION locate_like(substring VARCHAR(255), string VARCHAR(255)) RETURNS INTEGER
    DETERMINISTIC
BEGIN

    DECLARE i INTEGER;
    SET i = 1;

    myloop: WHILE (i <= CHAR_LENGTH(string)) DO

        IF SUBSTRING(string, i) LIKE CONCAT(substring, "%") THEN    
            -- subtract 1 to return a 0-based position
            RETURN(i - 1);
            LEAVE myloop;        
        END IF;    

        SET i = i + 1;

    END WHILE; 

    RETURN(0);
    
END

创建一些测试数据:

CREATE TABLE search_table AS SELECT "Antonín Leopold Dvořák (8 September 1841 – 1 May 1904)" column1;

调用函数:

SELECT LOCATE_LIKE('dvorak', column1) pos FROM search_table WHERE column1 like '%dvorak%';

结果:16

它是双向的:

CREATE TABLE search_table AS SELECT "Antonín Leopold Dvorak (8 September 1841 – 1 May 1904)" column1;

调用函数:

SELECT LOCATE_LIKE('Dvořák', column1) pos FROM search_table WHERE column1 like '%Dvořák%';

结果:16

使用 javascript 突出显示结果(其中搜索结果位于 id="results" 的输入中,位置通过名为“pos”的变量从 PHP 传递到 javascript,并且搜索词的长度传递为名为“searchLength”的变量):

document.getElementById('results').setSelectionRange(pos, pos + searchLength);
© www.soinside.com 2019 - 2024. All rights reserved.