Warning: array_key_exists(): The second argument should be either an array or an object in /hsphere/local/home/magistr/codeismy.name/ads/sape.php on line 695 Warning: array_merge(): Argument #2 is not an array in /hsphere/local/home/magistr/codeismy.name/wp-content/plugins/wp-pagenavi/scb/Options.php on line 46

Создание продвинутого поиска. Часть 2 — Soundex

Дата: Июнь 4, 2009

(1) комментарий

Если вы серьёзно взялись оптимизировать ваш поиск и довести релевантность «до небес :) » то вам просто необходима эта статья. Возможно вы уже видели как работают поисковики yandex и google и встречали там такую фишку как «возможно вы искали», так вот эта функция называется soundex и давно реализована на PHP и MySQL, но только для латинчких символов. Но как же так, а где же для русских? — скажете вы. Вот реализацию для кирилических символов сегодня и я хочу опиасать.

Но сначала немного об оригинальной функции и её методе работы в php

Двум словам, имеющим схожее произношение, соответствует один и тот же ключ soundex. Это свойство может быть использовано, например, при поиске по базе даных, когда известно произношение слова и неизвестно его написание. Данная функция возвращает строку из 4 символов, начинающуюся с буквы.

Ну а теперь о реализации данного алгоритма пор русский язык.
Написанно не мною но красиво:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<?php
$str = "журнал код моё имя";
print "ru_soundex($str) = ".ru_soundex($str)."<br />\r\n";

$str = "с каламбуром";
print "ru_soundex($str) = ".ru_soundex($str)."<br />\r\n";

function ru_soundex($source)
{
$res = '';

$literal = array();
// ассоциативный массив букв
// параметры звуков гласный / согласный

// для гласных переход буквы в звук(и), редуцированный/нет, предполагаемые правила ударения исходя из кол-ва слогов (stressed syllable)
// реализована проверка предполагаемого ударения

// для согласных переход букв[ы] в звук(и), редуцируемый/нет, правила редуцирования

// vowel
$literal['А'] = array('status'=>'гласный','sound'=>'а','stressed'=>'а'); // никогда не меняется
$literal['Е'] = array('status'=>'гласный','sound'=>'и','stressed'=>'э', 'АаЕеЁёИиОоУуЭэЮюЯяЬьЫыЪъ' => 'йэ'); // - особые правила, для этой буквы, стоящей после указанных, а также в начале слов
$literal['Ё'] = array('status'=>'гласный','sound'=>'о','stressed'=>'о', 'АаЕеЁёИиОоУуЭэЮюЯяЬьЫыЪъ' => 'йо');
$literal['И'] = array('status'=>'гласный','sound'=>'и','stressed'=>'и');
$literal['О'] = array('status'=>'гласный','sound'=>'а','stressed'=>'о');
$literal['У'] = array('status'=>'гласный','sound'=>'у','stressed'=>'у');
$literal['Ы'] = array('status'=>'гласный','sound'=>'ы','stressed'=>'ы');
$literal['Э'] = array('status'=>'гласный','sound'=>'э','stressed'=>'э');
$literal['Ю'] = array('status'=>'гласный','sound'=>'у','stressed'=>'у', 'АаЕеЁёИиОоУуЭэЮюЯяЬьЫыЪъ' => 'йу');
$literal['Я'] = array('status'=>'гласный','sound'=>'а','stressed'=>'а', 'АаЕеЁёИиОоУуЭэЮюЯяЬьЫыЪъ' => 'йа'); // заяц произносится как [зайец]
$v_pattern = 'АаЕеЁёИиОоУуЭэЮюЯяЬьЫыЪъ';

// кстати, надо добавить выкусывание гласных из концов слов, заканчивающихся на согласный-гласный-звонкий согласный (-ром, -лем, итд) гласная очень часто сглатывается
// зы: это здесь не реализовано %)
// проверено: soundex и сам с этим неплохо справляется

// звонкие согласные редуцируются при удвоении.
// звонкие согласные переходят в парный глухой перед глухим
// глухие редуцируются полностью перед глухими.

// consonant
// в отличие от гласных, для согласных условие "стоит перед указанной или в конце слова"
$literal['Б'] = array('status'=>'согласный','sound'=>'б', 'КкПпСсТтФфХхЦцЧчШшЩщ' => 'п');
$literal['В'] = array('status'=>'согласный','sound'=>'в', 'КкПпСсТтФфХхЦцЧчШшЩщ' => 'ф');
$literal['Г'] = array('status'=>'согласный','sound'=>'Г', 'КкПпСсТтФфХхЦцЧчШшЩщ' => 'к');
$literal['Д'] = array('status'=>'согласный','sound'=>'д', 'КкПпСсТтФфХхЦцЧчШшЩщ' => 'т');
$literal['Ж'] = array('status'=>'согласный','sound'=>'ж', 'КкПпСсТтФфХхЦцЧчШшЩщ' => 'ш');
$literal['З'] = array('status'=>'согласный','sound'=>'з', 'КкПпСсТтФфХхЦцЧчШшЩщ' => 'с');
$literal['Й'] = array('status'=>'согласный','sound'=>'й');
$literal['К'] = array('status'=>'согласный','sound'=>'к', 'КкПпСсТтФфХхЦцЧчШшЩщ' => '');
$literal['Л'] = array('status'=>'согласный','sound'=>'л');
$literal['М'] = array('status'=>'согласный','sound'=>'м');
$literal['Н'] = array('status'=>'согласный','sound'=>'н');
$literal['П'] = array('status'=>'согласный','sound'=>'п', 'КкПпСсТтФфХхЦцЧчШшЩщ' => '');
$literal['Р'] = array('status'=>'согласный','sound'=>'р');
$literal['С'] = array('status'=>'согласный','sound'=>'с'); // а вот С не хочет редуцироваться, на первый взгляд...
$literal['Т'] = array('status'=>'согласный','sound'=>'т', 'КкПпСсТтФфХхЦцЧчШшЩщ' => '');
$literal['Ф'] = array('status'=>'согласный','sound'=>'ф', 'КкПпСсТтФфХхЦцЧчШшЩщ' => ''); // спорно
$literal['Х'] = array('status'=>'согласный','sound'=>'х');
$literal['Ц'] = array('status'=>'согласный','sound'=>'ц');
$literal['Ч'] = array('status'=>'согласный','sound'=>'чь'); // всегда мягкий
$literal['Ш'] = array('status'=>'согласный','sound'=>'ш');
$literal['Щ'] = array('status'=>'согласный','sound'=>'щь');

// спецсимволы
$literal['Ъ'] = array('status'=>'знак','sound'=>' '); // только разделительный. делит жёстко
$literal['Ь'] = array('status'=>'знак','sound'=>'ь'); // даже если делит, то мягко

$literal['ТС'] = array('status'=>'сочетание','sound'=>'ц');
$literal['ТЬС'] = $literal['ТС'];
$literal['ШЬ'] = array('status'=>'сочетание','sound'=>'ш'); // всегда твёрдый. и это не единстенный рудимент языка

$literal['СОЛНЦ'] = array('status'=>'сочетание','sound'=>'сонц');
$literal['ЯИЧНИЦ'] = array('status'=>'сочетание','sound'=>'еишниц');
$literal['КОНЕЧНО'] = array('status'=>'сочетание','sound'=>'канешно');
$literal['ЧТО'] = array('status'=>'сочетание','sound'=>'што');
$literal['ЗАЯ'] = array('status'=>'сочетание','sound'=>'зайэ'); // да-да. не только [зайэц], но и [зайэвльэнийэ]




$sound = str_to_upper($source);

// сначала сочетания
foreach( array_filter($literal,
create_function('$item','if( $item["status"] === "сочетание") return true; return false;'))
as $sign => $translate )
$sound = str_replace($sign,$translate["sound"],$sound);

// потом знаки
foreach( array_filter($literal,
create_function('$item','if( $item["status"] === "знак") return true; return false;'))
as $sign => $translate )
$sound = str_replace($sign,$translate["sound"],$sound);


// разделяем на слова, определяем кол-во слогов, заменяем ударный/безударный гласный (единственный или предполагая второй в двух-трёхсложном слове, предпредпоследний - в остальных)

$words = preg_split('~[,.\~`1234567890-=\~!@#$%^&*()_+|{}\]\];:\'"<>/? ]~', $sound, -1, PREG_SPLIT_NO_EMPTY);

// гласные
foreach( array_filter($literal,
create_function('$item','if( $item["status"] === "гласный") return true; return false;'))
as $sign => $translate )
{
// для каждого слова
foreach( $words as &$word )
{
// кол-во гласных
$vowel = preg_match_all("~[$v_pattern]~", $word, $del_me );
// готовим
$cur_pos = 0;
$cur_vowel = 0;
while( false !== $cur_pos = strpos($word,$sign,$cur_pos) )
{
$cur_vowel++;
// print $cur_pos.' = '.$sound[$cur_pos]."<br />\r\n";
if( sizeof($translate)==4 && ($cur_pos === 0 || strpos( $v_pattern , $word[$cur_pos-1] )))
{
$word = substr_replace($word,$translate[$v_pattern],$cur_pos,1);
}
elseif( 1 == $vowel )
$word = substr_replace($word,$translate["stressed"],$cur_pos,1); //
elseif( 2 == $vowel && 1 == $cur_vowel )
$word = substr_replace($word,$translate["stressed"],$cur_pos,1); // предполагаем, что в двухсложных словах первый слог ударный
elseif( 3 <= $vowel && $cur_vowel == $vowel - 2 )
$word = substr_replace($word,$translate["stressed"],$cur_pos,1); // предполагаем, что слог ударный предпредпоследний
else
$word = substr_replace($word,$translate["sound"],$cur_pos,1);
$cur_pos++;
}
}
}

$sound = implode( $words, ' ' ); // клеим

Алгоритм достаточно сложный, но что не сделаеш для любимого юзера)

a




    Один комментарий на "Создание продвинутого поиска. Часть 2 — Soundex"

    Bethrezen сказал:
    08.04.2010

    Вместо soundex интереснее использовать mystem или phpMorphy для приведения в начальную форму и определения корней.

    Продолжение дискуссии на форуме: link


    Вы можете продолжить обсуждение этой статьи на форуме


    Имя : 
    Почта : 
    Сайт : 
    Комментарий : 

    Проверка комментариев включена. Прежде чем Ваши комментарии будут опубликованы пройдет какое-то время.

    Создание сайта - Echo-group Раскрутка сайтов