1

私はここしばらくフルテキストのアイデアをいじっていましたが、読んだり実験したりしたにもかかわらず、その利点/欠点や一般的な実用性について学ぶべきことがまだたくさんあると感じています.

多くの場合、それは非常にうまく機能しているように見えますが、最近私が抱えている大きな問題は、実在の人物の名前に関してです。簡単に言えば、アポストロフィ、特殊文字、アクセント、およびなど。そこで私は弾丸を噛み、インデックスを作成したい「検索」フィールドを更新する独自のトリガー関数を作成してみることにしました。

まず、Bjorn のような名前を検索するときに問題が発生します。Bjørn または Björn を使用することもできますが、O'Leary については、フルテキストで単語が 2 つの部分に分割されます。誰かがアポストロフィなしで名前を入力したらどうなりますか? 等々。

それに対処するために、私の現在の取り組みは次のとおりです。

CREATE OR REPLACE FUNCTION quote_insert() RETURNS TRIGGER AS $$
DECLARE
  --Set to lowercase and convert any strange vowels or accents to ascii (no Bjørn's, José's etc)
  myString text := to_ascii(convert_to(lower(NEW."name"),'latin1'),'latin1');
  nwString text := '';
  --Split the words up by their spaces and put them in an array
  myArray  text[] := regexp_split_to_array(myString, E'\\s+');
  nArray   text[];
    --These are counters for the secondary array, and for the apostrophe placement
    n int := 0;
    p int;
BEGIN
  FOR i IN 1..array_upper(myArray,1) LOOP
    --If it has an apostrophe (hopefully only one!) split the words up like so: o'leary -> oleary o leary
    IF position('''' in myArray[i]) > 0 
    THEN
      nArray[n] = '''' || regexp_replace(myArray[i], '''', '') || ''':' || n+1 || ' ';
            n = n+1;
      p = position('''' in myArray[i]);
      nArray[n] = '''' || substring(myArray[i],0,p) || ''':' || n+1 || ' ';
      n = n+1;
      nArray[n] = '''' || substring(myArray[i],p+1,length(myArray[i])) || ''':' || n+1 || ' ';
      n = n+1;
    ELSE
      nArray[n] = '''' || myArray[i] || ''':' || n+1 || ' ';
      n = n+1;
    END IF;
  END LOOP;

  --Sort 'nArray' here..........
  --(not even sure if sorting alphabetically is important...)
  --............................

  --Turn the array back into a string
  FOR i IN 0..array_upper(nArray,1) LOOP
    nwString = nwString || nArray[i];
  END LOOP;
  --Set the search field to whatever string is generated
  NEW."search" = trim(nwString);
  RETURN NEW;
END
$$ LANGUAGE 'plpgsql';

忘れそうになりましたが、以前に別のサイトから持ち上げた非常に便利なカスタム to_ascii スクリプトを追加しました。

CREATE FUNCTION to_ascii(bytea, name)
RETURNS text STRICT AS 'to_ascii_encname' LANGUAGE internal;

そのため、私のスクリプト トリガーはフルテキストと同様のテキストを生成します。唯一の違いは、単語がアルファベット順に配置されていることです。それが必要かどうか、またはそうすることが効率的かどうかはわかりませんが、そうしなければならない場合は、並べ替えに関する提案を歓迎します。

このスクリプトの結果と全文を比較するには、次のようにします。

My query (firing trigger function):
INSERT INTO "table" ("name") VALUES ('Bjørn O''Leary') RETURNING "search";
[search]
'bjorn':1 'oleary':2 'o':3 'leary':4

Using fulltext:
SELECT to_tsvector('Bjørn & O''Leary & OLeary'); --Obviously using o'leary and oleary because of different sorting methods
[to_tsvector]
'bjørn':1 'leari':3 'o':2 'oleari':4

ご覧のとおり、特殊な ø 文字は削除されません。同様の結果を得るには、明らかに OLeary を挿入する必要がありました。

では、質問をしてみましょう: 単語をアルファベット順に整理する方が良い/必要/速いですか?

第二に...私は今これを尋ねるのは嫌いですが、私が気付いていない私の基準に従う名前を照会するフルテキストの方法はすでにありますか?

最後に、本当の頭痛の種は、単一のテーブルから継承された複数のテーブルにまたがる名前があることです。完全に別個の「検索」テーブルを更新するために、このトリガーに挿入/更新を追加することを検討する場合があります。これは行き過ぎですか?

4

1 に答える 1

1

とてもおもしろい!名前検索に関連する問題のいくつかを解決するために、PostgreSQLを使用した名前検索に関するブログ投稿を書いています。
しかし、私の半分の問題解決ソリューションはあなたに役立つかもしれません。次のトリックから始めましょう。

  1. 検索者が少なくとも2つの名前を入力した場合、そのうちの1つが( BjørnO'Learyを検索していない限り)うまくいき、そのうちの1つがフルテキストインデックスに「一致」する可能性があります。その後、レーベンシュタイン距離やその他の弦の距離などのツールと多くの巧妙な作業を使用して、結果を適切に並べ替えることができます。これには、単語のored検索の検索を変更する必要があります。O'Learyは、さらに処理するためにデータベースによってさらに多くの行がフェッチされる可能性があるため、注意してください。

  2. それだけでなく、Bjørnのケースを完全に解決することができますunaccent。フィルタリング辞書を使用してください。それはそれを「ビョルン」に「変換」し、それでもアクセントを取り除くことによって他の多くの一般名のスペルミスを解決します!これは、トリガーが修正しようとしているものの一部です。

  3. これだけでは不十分な場合は、 SchwarzeneggerSchwarzneggerSchwarzenegerなど)の同義語をいくつか挿入し、パーサーが主な痛みの原因であるケースを処理する前処理を行うことで、同義語辞書を使用して特別なケースを処理できます。 O'Learyで(前処理は、たとえば、トリガーと同様に、検索文字列からアポストロフィを削除します)。同義語辞書もすぐに維持するのに苦労する可能性があると思いますが、小さな辞書でも名前検索に非常に役立つ場合があります。

    • 注:同義語辞書が必要ないが、(1)で提案したようなものを実装する場合、ユーザーが「Arnold Schwarznegr」(完全に間違っている)を検索すると、おそらく「ArnoldSchwarzenegger」が非常に近くに表示されます。 「シュワルツェネッガー」と「シュワルツェネッガー」の間のレーベンシュタイン距離は非常に小さく、アーノルドは全文一致であるため、ランク付けされた結果のトップになります。

それが少しでも役立つことを願っています。

編集:役立つかもしれないいくつかのリンク:
アクセントのないフィルタリング辞書
Fuzzystrmatchpg_trgm
モジュール

于 2013-01-30T23:32:20.477 に答える