如何在MediaWiki数据库中进行重音和不区分大小写的搜索?

时间:2013-04-15 11:36:51

标签: mysql mediawiki

让我假装我的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íngSÓ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存储为二进制数据)。

3 个答案:

答案 0 :(得分:3)

MediaWiki TitleKey extension基本上是为此设计的,但它只进行大小写折叠。但是,如果你不介意破解它,并安装了PHP iconv extension,你可以编辑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扩展程序将其标准化标题存储在separate table中,令人惊讶地命名为titlekey。它打算通过MediaWiki搜索界面访问,但如果您愿意,您当然也可以直接查询它,例如像这样:

SELECT page.* FROM page
  JOIN titlekey ON tk_page = page_id
WHERE tk_namespace = 0 AND tk_key = 'SOMETHING';

答案 1 :(得分:3)

我找到了完美的解决方案,没有创造或创造表格。它可能有性能影响(我没有测试),但正如我在我的问题中所说,它是一个~2K行表,所以它应该没什么关系。

问题的根源是 MediaWiki在latin1编码表中存储UTF8编码的文本。它对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。根据您的使用案例(here is a complete list)使用不同的排序规则。

相关链接:

答案 2 :(得分:1)

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

<强>口音: 为了拥有所有重音或至少所有已知的重音,您可以在数据库中使用两行。第一行按原样存储结果(表示存储SomêthÏng)并另外创建第二个 search_row ,在这种情况下包含字符串某事< / strong>(没有任何口音)。对于转换,您可以使用替换规则来创建函数。

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

最后一步是,每当您在表格页面中插入/更新标题时,您都会触发,填充/更新字段 search_row

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