使用非拉丁字符搜索 Geoname 数据库

Searching Geoname database with non-latin characters

我有一个 the Geonames database 的副本存储在 MySQL 数据库中,还有一个 PHP 应用程序允许用户在数据库中搜索他们的城市。如果他们用英文输入城市名称,效果很好,但我希望他们能够用他们的母语搜索。

例如,与其让说日语的人搜索 Tokyo,不如让他们搜索 東京.

Geonames 数据库包含一个 alternatenames 列,其中包含“alternatenames, comma separated, ascii names automatically transliterated, convenience attribute from alternatename table, varchar(10000)”。

例如,Tokyo 行的 alternatenames 值为 Edo,TYO,Tochiu,Tocio,Tokija,Tokijas,Tokio,Tokió,Tokjo,Tokyo,Toquio,Toquio - dong jing,Toquio - æ±äº¬,Tòquio,Tókýó,Tóquio,TÅkyÅ,dokyo,dong jing,dong jing dou,tokeiyw,tokkiyo,tokyo,twkyw,twqyw,Τόκιο,Токио,Токё,Токіо,ÕÕ¸Õ¯Õ«Õ¸,טוקיו,توكيو,توکیو,طوكيو,ܛܘܟÜܘ,ܜܘܟÜܘ,टोकà¥à¤¯à¥‹,டோகà¯à®•à®¿à®¯à¯‹,โตเà¸à¸µà¸¢à¸§,ტáƒáƒ™áƒ˜áƒ,东京,æ±äº¬,æ±äº¬éƒ½,ë„ì¿„

这些值并不完全包含 東京,但我猜测它们包含以某种方式编码或转换的形式。所以,我假设如果我对我的搜索字符串执行相同的 encoding/conversion,那么我将能够匹配该行。例如:

mysql_query( sprintf( "
    SELECT * FROM geoname 
    WHERE 
        MATCH( name, asciiname, alternatenames ) 
        AGAINST ( %s )  
    LIMIT 1",
    iconv( 'UTF-8', 'ASCII', '東京' )
) );

问题是我不知道该转换是什么。我尝试了很多 iconv()mb_convert_string() 等的组合,但没有成功。

MySQL table 看起来像这样:

CREATE TABLE `geoname` (
 `geonameid` int(11) NOT NULL DEFAULT '0',
 `name` varchar(200) DEFAULT NULL,
 `asciiname` varchar(200) DEFAULT NULL,
 `alternatenames` mediumtext,
 `latitude` decimal(10,7) DEFAULT NULL,
 `longitude` decimal(10,7) DEFAULT NULL,
 `fclass` char(1) DEFAULT NULL,
 `fcode` varchar(10) DEFAULT NULL,
 `country` varchar(2) DEFAULT NULL,
 `cc2` varchar(60) DEFAULT NULL,
 `admin1` varchar(20) DEFAULT NULL,
 `admin2` varchar(80) DEFAULT NULL,
 `admin3` varchar(20) DEFAULT NULL,
 `admin4` varchar(20) DEFAULT NULL,
 `population` int(11) DEFAULT NULL,
 `elevation` int(11) DEFAULT NULL,
 `gtopo30` int(11) DEFAULT NULL,
 `timezone` varchar(40) DEFAULT NULL,
 `moddate` date DEFAULT NULL,
 PRIMARY KEY (`geonameid`),
 KEY `timezone` (`timezone`),
 FULLTEXT KEY `namesearch` (`name`,`asciiname`,`alternatenames`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4

任何人都可以指出正确的方向吗?

推荐阅读:

https://www.joelonsoftware.com/articles/Unicode.html

http://kunststube.net/encoding/


对于MySQL而言,至关重要的是MySQL连接的字符集。这是 MySQL 服务器认为客户端在其通信中使用的字符集。

SHOW VARIABLES LIKE '%characterset%'

如果设置不正确,例如,客户端正在发送 latin1 (ISO-8859-1),但 MySQL 服务器认为它正在接收 UTF8,反之亦然,则可能存在 mojibake。

同样重要的是alternatenames列的字符集


处理多字节字符集的一个问题是 PHP sprintf 函数。 PHP 中的许多字符串处理函数都有 "mutlibyte" 等价物,可以正确处理包含多字节字符的字符串。

https://secure.php.net/manual/en/book.mbstring.php

遗憾的是,没有内置 mb_sprintf 函数。

有关 PHP 中字符串处理的更详细描述,包括多字节 characters/charactersets:

https://secure.php.net/manual/en/language.types.string.php#language.types.string.details

摘录:

Ultimately, this means writing correct programs using Unicode depends on carefully avoiding functions that will not work and that most likely will corrupt the data and using instead the functions that do behave correctly, generally from the intl and mbstring extensions. However, using functions that can handle Unicode encodings is just the beginning. No matter the functions the language provides, it is essential to know the Unicode specification.

此外,google 搜索 "utf8 all the way through" 可能 return 一些有用的注释。但请注意,这个咒语不是解决问题的灵丹妙药或灵丹妙药。


MySQL 参考手册中提到的另一个可能的问题:

https://dev.mysql.com/doc/refman/5.7/en/fulltext-restrictions.html

13.9.5 Full-Text Restrictions

Ideographic languages such as Chinese and Japanese do not have word delimiters. Therefore, the built-in full-text parser cannot determine where words begin and end in these and other such languages.

In MySQL 5.7.6, a character-based ngram full-text parser that supports Chinese, Japanese, and Korean (CJK), and a word-based MeCab parser plugin that supports Japanese are provided for use with InnoDB and MySIAM tables.

当我下载 the Japan file 并设置这样的数据库时:

CREATE TABLE geonames (
    geonameid SERIAL,
    name varchar(200),
    asciiname varchar(200),
    alternatenames varchar(10000),
    latitude float,
    longitude float,
    featureclass varchar(1),
    featurecode varchar(10),
    countrycode varchar(2),
    cc2 varchar(200),
    admin1code varchar(20),
    admin2code varchar(80),
    admin3code varchar(20),
    admin4code varchar(20),
    population BIGINT,
    elevation INT,
    dem INT,
    timezone varchar(40),
    modificationdate DATE
    ) CHARSET utf8mb4;

然后我load the data这样:

LOAD DATA INFILE '/tmp/JP.txt' INTO TABLE geonames CHARACTER SET utf8mb4;

而select是这样的:

SELECT alternatenames FROM geonames WHERE geonameid=1850147\G

我明白了:

*************************** 1. row ***************************
alternatenames: Edo,TYO,Tochiu,Tocio,Tokija,Tokijas,Tokio,Tokió,Tokjo,Tokyo,Toquio,Toquio - dong jing,Toquio - 東京,Tòquio,Tókýó,Tóquio,Tōkyō,dokyo,dong jing,dong jing dou,tokeiyw,tokkiyo,tokyo,twkyw,twqyw,Τόκιο,Токио,Токё,Токіо,Տոկիո,טוקיו,توكيو,توکیو,طوكيو,ܛܘܟܝܘ,ܜܘܟܝܘ,टोक्यो,டோக்கியோ,โตเกียว,ტოკიო,东京,東京,東京都,도쿄

我也可以这样搜索:

SELECT name FROM geonames WHERE alternatenames LIKE '%,東京,%';

长话短说:注意我创建 table 时的字符集声明。我相信这是你在创建数据库时没有做到的。