Esta é uma continuação da publicação “ Internacionalização da Pesquisa de Endereços de Cidades. Implementando o Soundex em russo no Sphinx Search ”, no qual discuti como implementar o suporte para os algoritmos fonéticos Soundex no Sphinx Search, para texto escrito em cirílico. O suporte Soundex já está disponível para texto em latim. É o mesmo com Metphone, para o alfabeto latino, mas não para o alfabeto cirílico, mas vamos tentar corrigir esse fato irritante com a ajuda de transliteração, expressões regulares e um arquivo.
Esta é uma continuação direta, na qual iremos analisar como implementar a Metaphone original, Russian Metaphone (no sentido de que a transliteração não é necessária), Caverphone, e não seremos capazes de fazer Double Metaphone.
A implementação é adequada para as plataformas Sphinx Search e Manticore Search.
No final, vamos ver como Metaphone percebe o rakomakophone .
Imagem Docker
Preparou a imagem docker tkachenkoivan / searchfonetic para que você pudesse "sentir" o resultado. Todos os índices desta publicação e da anterior foram adicionados à imagem, mas, atenção, os nomes dos índices da publicação anterior não correspondem ao que está armazenado na imagem. Por quê? Porque um bom pensamento vem depois.
A descrição dos algoritmos, mesmo assim, foi retirada da publicação " algoritmos fonéticos ". Tentarei duplicar o mínimo possível o texto nele escrito.
Metaphone Original
É implementado de forma elementar, as expressões regulares para transliteração são criadas:
regexp_filter = (|) => a
regexp_filter = (|) => b
regexp_filter = (|) => v
…
E ligue o metafone :
morphology = metaphone
, Soundex. , , , Soundex , Soundex, – , .
, , , Metaphone + . .
Sphinx blend_chars. , Sphinx , , , , – , , , .., .. , , , , «&». «M&M’s» ? «&»? blend_chars
.
, blend_chars
:
blend_chars = U+0020
, - “ ”, , , . , , .
mysql> select * from metaphone where match('');
+------+--------------------------------------+-----------+---------------------------+
| id | aoguid | shortname | offname |
+------+--------------------------------------+-----------+---------------------------+
| 1130 | e21aec85-0f63-4367-b9bb-1943b2b5a8fb | | |
+------+--------------------------------------+-----------+---------------------------+
, « », call keywords
:
mysql> call keywords (' ', 'metaphone');
+------+---------------+------------+
| qpos | tokenized | normalized |
+------+---------------+------------+
| 1 | morisa toreza | MRSTRS |
| 1 | morisa | MRS |
| 2 | toreza | TRS |
+------+---------------+------------+
, : «morisa», «toreza» «morisa toreza», Metaphone, «».
Metaphone Sphinx Search. , . , , :
regexp_filter = [ ] =>
« », , , .
, , , .
Caverphone , .
mysql> call keywords (' ', 'caverphone');
+------+-----------+------------+
| qpos | tokenized | normalized |
+------+-----------+------------+
| 1 | mrsa trza | mrsa trza |
| 1 | mrsa | mrsa |
| 2 | trza | trza |
+------+-----------+------------+
mysql> select * from caverphone where match('');
Empty set (0.00 sec)
Soundex ( ), Sphinx, , , , , «morisa» «toreza» , «morisa toreza» :
mysql> call keywords (' ', 'simple_soundex');
+------+---------------+---------------+
| qpos | tokenized | normalized |
+------+---------------+---------------+
| 1 | morisa toreza | morisa toreza |
| 1 | morisa | m620 |
| 2 | toreza | t620 |
+------+---------------+---------------+
blend_chars
– , . metaphone. ( ) – : , .
.
Double Metaphone
Metaphone , , , .
, , Metaphone . , , , , DoubleMetaphone.java. , «C», , .
, , – , , , Sphinx Manticore.
, Metaphone . , . Sphinx . .
, , Java, Commons Codec. – , . , – , .
, , , . – .
, , :
DoubleMetaphone dm = new DoubleMetaphone();
String metaphone1 = dm.doubleMetaphone("Text", false);
String metaphone2 = dm.doubleMetaphone("Text", true);
metaphone1
metaphone2
.
– .
, Commons Codec. , . Metaphone , , . , : , , .
Sphinx .
Metaphone
.
. , . « », « Metaphone».
, , , .
, , . , « », «», «» , :
mysql> call keywords (' ', 'rus_metaphone');
+------+--------------+--------------+
| qpos | tokenized | normalized |
+------+--------------+--------------+
| 1 | | |
| 2 | | |
+------+--------------+--------------+
. , , GitHub Gist manticore.conf.
:
regexp_filter = (?i)(|||) =>
regexp_filter = (?i)(||) =>
regexp_filter = (?i)(||) =>
regexp_filter = (?i)() =>
, , , , , :
regexp_filter = (?i)()(||||||||||||||||) => \2
regexp_filter = (?i)()(||||||||||||||||) => \2
regexp_filter = (?i)()(||||||||||||||||) => \2
regexp_filter = (?i)()(||||||||||||||||) => \2
regexp_filter = (?i)()(||||||||||||||||) => \2
regexp_filter = (?i)()(||||||||||||||||) => \2
,
regexp_filter = (?i)\b => regexp_filter = (?i)\b => regexp_filter = (?i)\b => regexp_filter = (?i)\b => regexp_filter = (?i)\b => regexp_filter = (?i)\b =>
regexp_filter = (?i)(||) =>
Caverphone
.
, :
regexp_filter = (A|a) => a
regexp_filter = (B|b) => b
…
, , , , .
e
regexp_filter = e\b =>
, , :
regexp_filter = \b(cough) => cou2f regexp_filter = \b(rough) => rou2f …
regexp_filter = (cq) => 2q
regexp_filter = (ci) => si
…
a, — 3
regexp_filter = (?i)\b(a|e|i|o|u|y) => A
regexp_filter = (?i)(a|e|i|o|u|y) => 3
regexp_filter = (j) => y
regexp_filter = \b(y3) => Y3
…
2
regexp_filter = 2 =>
3, A
regexp_filter = 3\b => A
3
regexp_filter = 3 =>
10 .
:
mysql> select * from caverphone where match ('');
+------+--------------------------------------+-----------+------------------+
| id | aoguid | shortname | offname |
+------+--------------------------------------+-----------+------------------+
| 5 | 01339f2b-6907-4cb8-919b-b71dbed23f06 | | |
| 387 | 4b919f60-7f5d-4b9e-99af-a7a02d344767 | | |
+------+--------------------------------------+-----------+------------------+
«» «». , , , Daitch Mokotoff Soundex - «»:
mysql> select * from daitch_mokotoff_soundex where match ('');
+------+--------------------------------------+-----------+--------------+
| id | aoguid | shortname | offname |
+------+--------------------------------------+-----------+--------------+
| 387 | 4b919f60-7f5d-4b9e-99af-a7a02d344767 | | |
| 541 | 69b8220e-a42d-4fec-a346-1df56370c363 | | |
+------+--------------------------------------+-----------+--------------+
:
mysql> call keywords (' ', 'caverphone');
+------+-----------+------------+
| qpos | tokenized | normalized |
+------+-----------+------------+
| 1 | lnna | lnna |
| 2 | lnna | lnna |
| 3 | lna | lna |
+------+-----------+------------+
mysql> call keywords (' ', 'daitch_mokotoff_soundex');
+------+-----------+------------+
| qpos | tokenized | normalized |
+------+-----------+------------+
| 1 | 866 | 866 |
| 2 | 8616 | 8616 |
| 3 | 866 | 866 |
+------+-----------+------------+
, , , - . , .
: .
, , . Just for fun.
, rock the microphone?! , Metaphone . !
-, blend_chars, rock the microphone, :
blend_chars = U+0020
- metaphone, .
keywords
Sphinx:
mysql> call keywords ('', 'metaphone');
+------+-------------+------------+
| qpos | tokenized | normalized |
+------+-------------+------------+
| 1 | rakomakofon | RKMKFN |
+------+-------------+------------+
rock the microphone:
mysql> call keywords ('rock the microphone', 'metaphone');
+------+---------------------+------------+
| qpos | tokenized | normalized |
+------+---------------------+------------+
| 1 | rock the microphone | RK0MKRFN |
| 1 | rock | RK |
| 2 | the | 0 |
| 3 | microphone | MKRFN |
+------+---------------------+------------+
RK0MKRFN, RKMKFN, 2(!). the , RKMKRFN:
mysql> call keywords ('rock microphone', 'metaphone');
+------+-----------------+------------+
| qpos | tokenized | normalized |
+------+-----------------+------------+
| 1 | rock microphone | RKMKRFN |
| 1 | rock | RK |
| 2 | microphone | MKRFN |
+------+-----------------+------------+
RKMKRFN RKMKFN, 1! .
«the», stopwords , - blend_chars = U+0020
«the» . , 1, .
A esperança qsuggest
não se concretizou - não dará pistas. Por quê? Você pode notar que quando você chama keywords
há duas colunas tokenized
e normalized
, qsuggest
dá uma dica sobre a coluna tokenized
e mede a distância de Levenshtein em relação a ela, qsuggest
não importa que lá, em normalized
, a distância seja 1.
Portanto, a observação é engraçada, mas não prática.