Я пришел к решению, которое работает довольно хорошо, следуя предложениям в комментариях.В частности, предложение @ X-Zero о создании таблицы Soundexes: в моем случае я могу создавать новые таблицы, но изменение существующей схемы не допускается!
Итак, мой процессследующим образом:
Создать новую таблицу со столбцами: ID
, token
, sound
и position
;с первичным ключом (ID
, sound
, position
) и дополнительным индексом над (ID
, sound
).
Пройдите по каждому человеку вбиографическая таблица:
Объедините их имя и фамилию.
Измените кодовую страницу на us7ascii
, чтобы символы с акцентом нормализовались.Это связано с тем, что алгоритм Soundex не работает с акцентированными символами.
Преобразуйте все неалфавитные символы в пробелы и считайте это границей между токенами.
Маркируем эту строку и вставляем в таблицу токен (в нижнем регистре), Soundex токена и позицию, в которой токен находится в исходной строке;ассоциируйте это с ID
.
Примерно так:
declare
nameString varchar2(82);
token varchar2(40);
posn integer;
cursor myNames is
select id,
forename||' '||surname person_name
from mypeople;
begin
for person in myNames
loop
nameString := trim(
utl_i18n.escape_reference(
regexp_replace(
regexp_replace(person.person_name,'[^[:alpha:]]',' '),
'\s+',' '),
'us7ascii')
)||' ';
posn := 1;
while nameString is not null
loop
token := substr(nameString,1,instr(nameString,' ') - 1);
insert into personsearch values (person.id,lower(token),soundex(token),posn);
nameString := substr(nameString,instr(nameString,' ') + 1);
posn := posn + 1;
end loop;
end loop;
end;
/
Так, например, "Siân O'Conner" получает токеныв "sian" (позиция 1), "o" (позиция 2) и "conner" (позиция 3), и эти три записи с их Soundex вставляются в personsearch
вместе с их идентификатором.
- Для поиска мы выполняем тот же процесс: разбиваем критерии поиска и затем возвращаем результаты, где совпадают Soundexes и относительно позиций.Мы упорядочиваем по позиции, а затем по очереди расстояние Левенштейна (
ld
) от исходного поиска для каждого токена.
Этот запрос, например, будет искать по двум токенам (т. Е. Предварительнострока поиска):
with searchcriteria as (
select 'john' token1,
'smith' token2
from dual)
select alpha.id,
mypeople.forename||' '||mypeople.surname
from peoplesearch alpha
join mypeople
on mypeople.student_id = alpha.student_id
join peoplesearch beta
on beta.student_id = alpha.student_id
and beta.position > alpha.position
join searchcriteria
on 1 = 1
where alpha.sound = soundex(searchcriteria.token1)
and beta.sound = soundex(searchcriteria.token2)
order by alpha.position,
ld(alpha.token,searchcriteria.token1),
beta.position,
ld(beta.token,searchcriteria.token2),
alpha.student_id;
Для поиска по произвольному количеству токенов нам потребуется использовать динамический SQL: присоединение к таблице поиска столько раз, сколько токенов, где поле position
в объединенной таблице должно быть больше position
ранее объединенной таблицы ... Я планирую написать для этого функцию, а также токенизацию строки поиска, которая будет возвращать таблицу идентификаторов.Тем не менее, я просто опубликую это здесь, чтобы вы поняли:)
Как я уже сказал, это работает довольно хорошо: довольно быстро возвращает хорошие результаты.Даже поиск «Джона Смита», когда-то кэшированного сервером, выполняется менее чем за 0,2 с;возвращая более 200 строк ... Я очень доволен этим и буду стремиться запустить его в производство.Единственные проблемы:
Предварительный расчет токенов занимает некоторое время, но это одноразовый процесс, поэтому проблема не слишком большая.Однако связанная с этим проблема заключается в том, что в таблицу mypeople
должен быть помещен триггер для вставки / обновления / удаления токенов в таблицу поиска всякий раз, когда соответствующая операция выполняется для mypeople
.Это может замедлить работу системы;но так как это должно происходить только в течение нескольких периодов в году, возможно, лучшим решением было бы перестроить таблицу поиска на плановой основе.
Не производится обработка, поэтомуАлгоритм Soundex совпадает только с полными токенами.Например, поиск «chris» не вернет никаких «christopher».Возможное решение этой проблемы - хранить только Soundex основы токена, но вычисление основы не является простой проблемой!Это будет обновление в будущем, возможно, с использованием механизма переноса, используемого TeX ...
В любом случае, надеюсь, это поможет :) Комментарии приветствуются!
РЕДАКТИРОВАТЬ Мое полное решение (написать и реализовать) теперь здесь , с использованием Метафона и расстояния Дамерау-Левенштейна.