12

私は、多くのデータベースと同様に、経歴情報を含むテーブルを持つ Oracle データベースを持っています。その上で、「自然な」方法で名前で検索したいと思います。

テーブルにはforenamesurnameフィールドがあり、現在、次のようなものを使用しています。

select id, forename, surname
from   mytable
where  upper(forename) like '%JOHN%'
and    upper(surname) like '%SMITH%';

これは機能しますが、このテーブルのインデックスは明らかに前のワイルドカードを考慮できないため、非常に遅くなる可能性があります。また、ユーザーは通常、電話で話した内容に基づいて人を検索します (膨大な数の英語以外の名前を含む)。そのため、音声分析も行うとよいでしょう。

そのため、私は Oracle Text を試しています。

create index forenameFTX on mytable(forename) indextype is ctxsys.context;
create index surnameFTX on mytable(surname) indextype is ctxsys.context;

select   score(1)+score(2) relevance,
         id,
         forename,
         surname
from     mytable
where    contains(forename,'!%john%',1) > 0
and      contains(surname,'!%smith%',2) > 0
order by relevance desc;

これには、Soundex アルゴリズムとフルテキスト インデックスを使用できるという利点があるため、もう少し効率的である必要があります。(ただし、私の逸話的な結果は、かなり遅いことを示しています!) これについて私が持っている唯一の懸念は次のとおりです。

  • まず、テキスト インデックスを意味のある方法で更新する必要があります。使用on commitすると遅すぎて、フロントエンド ソフトウェア (私の制御範囲外) がデータベースと対話する方法に干渉する可能性があります。そのため、いくつかの検討が必要です...

  • Oracle から返される結果は、正確に自然にソートされているわけではありません。scoreこの機能についてはよくわかりません。たとえば、私の開発データでは、「Jonathan Peter Jason Smith」が一番上に表示されていますが、「Jane Margaret Simpson」は「John Terrance Smith」と同じレベルにあります。

実生活では、名前の途中でチャンクを検索することは決してないため、前のワイルドカードを削除すると、結果を低下させることなくパフォーマンスが向上する可能性があると考えています。しかし、そうでなければ、私はアイデアを受け入れます... このシナリオは、うんざりして実装されたに違いありません! 私が今やっている/考えていることに対して、より良いアプローチを提案できる人はいますか?

ありがとう :)

4

1 に答える 1

5

コメントの提案に従って、かなりうまく機能するソリューションを思いつきました。特に、Soundexes のテーブルを作成するという @X-Zero の提案: 私の場合、新しいテーブルを作成できますが、既存のスキーマを変更することは許可されていません!

したがって、私のプロセスは次のとおりです。

  • 次の列を含む新しいテーブルを作成します: IDtokensoundおよびposition; ID( 、sound、 )上の主キーと ( 、position) 上の追加のインデックスを使用します。IDsound

  • 伝記表の各人物を調べます。

    • 姓と名を連結します。

    • コードページを に変更して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) にトークン化され、これら 3 つのエントリは、Soundex と共に、long に挿入されますpersonsearch。彼らのIDで。

  • 検索するには、同じプロセスを実行します。検索条件をトークン化し、Soundexe と相対位置が一致する結果を返します。ld位置、次に各トークンの元の検索からのレーベンシュタイン距離 ( ) で並べ替えます。

たとえば、このクエリは 2 つのトークン (つまり、事前にトークン化された検索文字列) に対して検索します。

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... I ID のテーブルを返す検索文字列のトークン化と同様に、これを行う関数を作成する予定です。ただし、これをここに投稿するだけで、アイデアが得られます:)

私が言うように、これは非常にうまく機能します。良い結果がすぐに返ってきます。サーバーにキャッシュされた "John Smith" の検索でさえ、0.2 秒未満で実行されます。200 行以上を返します...私はこれにかなり満足しており、製品化することを検討しています。唯一の問題は次のとおりです。

  • トークンの事前計算には時間がかかりますが、これは 1 回限りのプロセスであるため、それほど問題にはなりません。mypeopleただし、関連する問題は、対応する操作が で実行されるたびに、トークンを検索テーブルに挿入/更新/削除するために、テーブルにトリガーを配置する必要があることmypeopleです。これにより、システムが遅くなる可能性があります。ただし、これは年に数回しか発生しないため、定期的に検索テーブルを再構築することをお勧めします。

  • ステミングは行われないため、Soundex アルゴリズムは完全なトークンでのみ一致します。たとえば、「chris」を検索しても「christopher」は返されません。これに対する考えられる解決策は、トークンの語幹の Soundex のみを保存することですが、語幹の計算は単純な問題ではありません! これは将来のアップグレードで、TeX で使用されるハイフネーション エンジンを使用する可能性があります...

とにかく、それが役に立てば幸いです:) コメントは大歓迎です!


編集Metaphone と Damerau-Levenshtein Distance を使用して、私の完全なソリューション (作成と実装) がここにあります。

于 2011-10-04T11:54:56.030 に答える