5

假设我的 wiki (MediaWiki 1.19.4) 中有这些页面标题:

SOMETHIng
Sómethìng
SomêthÏng
SÒmetHínG

如果用户搜索something我希望所有 4 个页面都作为结果返回。

目前我唯一能想到的是这个查询(MySQL Percona 5.5.30-30.2):

SELECT page_title
FROM page
WHERE page_title LIKE '%something%' COLLATE utf8_general_ci

仅返回SOMETHIng.

我必须走在正确的道路上,因为如果我搜索sóméthíngOR SÓMÉTHÍNG,我会得到SOMETHIng结果。如何修改查询,以便按预期获得其他结果?此处的性能并不重要,因为该page表仅包含约 2K 行。

这是具有相关位的表定义:

CREATE TABLE page (
    (...)
    page_title VARCHAR(255) NOT NULL DEFAULT '' COLLATE latin1_bin,
    (...)
    UNIQUE INDEX name_title (page_namespace, page_title),
)

不得修改表定义,因为这是 MediaWiki 和 AFAIK 的库存安装,其代码期望该字段以这种方式定义(即 unicode 存储为二进制数据)。

4

3 回答 3

3

MediaWiki TitleKey 扩展基本上就是为此而设计的,但它只进行大小写折叠。但是,如果你不介意修改它,并且安装了 PHP iconv 扩展,你可以编辑TitleKey_body.php并替换方法:

static function normalize( $text ) {
    global $wgContLang;
    return $wgContLang->caseFold( $text );
}

例如:

static function normalize( $text ) {
    return strtoupper( iconv( 'UTF-8', 'US-ASCII//TRANSLIT', $text ) );
}

和(重新)运行rebuildTitleKeys.php。

TitleKey 扩展将其规范化的标题存储在一个单独的表中,名称出人意料titlekey。它旨在通过 MediaWiki 搜索界面访问,但如果您愿意,您当然也可以直接查询它,例如:

SELECT page.* FROM page
  JOIN titlekey ON tk_page = page_id
WHERE tk_namespace = 0 AND tk_key = 'SOMETHING';
于 2013-04-15T12:49:24.407 回答
3

我找到了完美的解决方案,无需修改或创建表格。它可能会对性能产生影响(我没有测试),但正如我在问题中所说,它是一个约 2K 行的表,所以它应该无关紧要。

问题的根源在于MediaWiki 将 UTF8 编码的文本存储在 latin1 编码的表中。这对 MediaWiki 来说并不重要,因为它知道这一点,它总是会用正确的字符集查询数据库并做它的事情,本质上是使用 MySQL 作为一个哑位容器。这样做是因为 MySQL 中的 UTF8 支持显然不足以满足其需求(请参阅 MediaWiki 中的注释DefaultSettings.php,变量$wgDBmysql5)。

当您希望数据库本身能够执行 UTF8 感知操作(就像我想在我的问题中做的那样)时,就会出现问题。您将无法这样做,因为据 MySQL 所知,它不存储 UTF8 编码的文本(尽管它是,如上一段所述)。

对此有一个明显的解决方案:将您要使用的列转换为 UTF8,如下所示CONVERT(col_name USING utf8)。这里的问题是 MySQL 试图提供危险的帮助:它认为col_name存储的是 latin1 编码的文本,它会将每个字节转换(而不是编码)为其等效的 UTF8,并且您将以双编码的 UTF8 结尾,这显然是错误的。

如何避免 MySQL 如此友好和乐于助人?在转换为 UTF8之前只需转换为 BINARY !这样,MySQL 就不会假设任何事情,并且会完全按照要求进行:将这些位编码为 UTF8。确切的语法是CONVERT(CAST(col_name AS BINARY) USING utf8).

所以这是我现在的最后一个查询:

SELECT CONVERT(CAST(page_title AS BINARY) USING utf8)
FROM page
WHERE
    CONVERT(CAST(page_title AS BINARY) USING utf8)
        LIKE '%keyword_here%'
            COLLATE utf8_spanish_ci

现在,如果我搜索somethingsôMëthîNG或任何变体,我会得到所有结果!

请注意,我使用它utf8_spanish_ci是因为我希望搜索能够区分ñná区分a. 根据您的用例使用不同的排序规则(这里是完整列表)。

相关链接:

于 2013-04-15T15:00:03.570 回答
1

不区分大小写:您可以简单地让数据库为您完成工作(您已经使用_ci完成了)

口音: 为了拥有所有口音或至少所有已知口音,您可以在数据库中使用两行。第一行按原样存储结果(这意味着您存储SomeêthÏng),并且您另外创建第二个search_row,在这种情况下将包含字符串something(没有任何重音符号)。对于转换,您可以使用替换规则创建一个函数。

现在您可以使用转换功能转换搜索字符串

最后一步是,您创建一个触发器,每次您在 table page中插入/更新标题时,它都会填充/更新字段search_row

此解决方案也不会对性能产生任何负面影响!

于 2013-04-15T11:50:32.237 回答