5

ユーザーがさまざまな方法で検索できる場所 (ショップ、ランドマークなど) の大きな postgres テーブルがあります。ユーザーが場所の名前を検索したい場合、システムは現在実行しています (検索がカフェであると仮定します)。

lower(location_name) LIKE '%cafe%'

クエリの一部として。これは非常に非効率的です。とてつもなくそう。これをより速くすることが不可欠です。テーブルのインデックスを作成しようとしました

gin(to_tsvector('simple', location_name))

と検索

(to_tsvector('simple',location_name) @@ to_tsquery('simple','cafe'))

これは見事に機能し、検索時間を数桁短縮します。

ただし、場所の名前は、空白で区切られていない中国語などの言語を含む任意の言語にすることができます。この新しいシステムは、正確な名前を検索しない限り、中国の場所を見つけることができませんが、古いシステムは部分的な名前の一致をうまく見つけることができました.

それで、私の質問は次のとおりです。これをすべての言語で一度に機能させることはできますか、それとも間違った方向に進んでいますか?

4

3 に答える 3

4

任意の部分文字列の一致を最適化したい場合、1 つのオプションはmoduleを使用することpg_tgrmです。インデックスを追加します。

CREATE INDEX table_location_name_trigrams_key ON table
  USING gin (location_name gin_trgm_ops);

これにより、「Simple Cafe」が「sim」、「imp」、「mpl」などに分割され、各行の各 trigam のインデックスにエントリが追加されます。クエリ プランナーは、次のような部分文字列パターンの一致にこのインデックスを自動的に使用できます。

SELECT * FROM table WHERE location_name ILIKE '%cafe%';

このクエリは、インデックスで "caf" と "afe" を検索し、共通部分を見つけ、それらの行をフェッチしてから、パターンに対して各行をチェックします。("caf" と "afe" の共通部分は "simple cafe" と "unsafe scaffolding" の両方に一致しますが、"%cafe%" は 1 つだけに一致する必要があるため、最後のチェックが必要です)。より多くの行を除外できるため、入力パターンが長くなるほどインデックスはより効果的になりますが、単語全体をインデックス化するほど効率的ではないため、to_tsvector.

注意点は、トライグラムは 3 文字未満のパターンではまったく機能しないということです。それはあなたのアプリケーションの契約を破るかもしれませんし、そうでないかもしれません.


編集:最初にこれをコメントとして追加しました。

昨夜、ほとんど寝ていたときに別のことを考えていました。cjk_chars入力文字列、regexp_matchesCJK Unicode 範囲全体を受け取り、そのような文字の配列を返す関数を作成しますNULL。に GIN インデックスを追加しcjk_chars(location_name)ます。次に、次のクエリを実行します。

WHERE CASE
  WHEN cjk_chars('query') IS NOT NULL THEN
    cjk_chars(location_name) @> cjk_chars('query')
    AND location_name LIKE '%query%'
  ELSE
    <tsvector/trigrams>
  END

タダ、ユニグラム!

于 2012-10-11T03:21:26.660 に答える
2

多言語環境での全文検索の場合、各データムの言語をテキスト自体と一緒に保存する必要があります。次に、tsearch 関数の言語固有のフレーバーを使用して、適切なステミングなどを取得できます。

例:

CREATE TABLE location(
    location_name text, 
    location_name_language text
);

... 適切な制約に加えて、次のように記述できます。

CREATE INDEX location_name_ts_idx
USING gin(to_tsvector(location_name_language, location_name));

および検索の場合:

SELECT to_tsvector(location_name_language,location_name) @@ to_tsquery('english','cafe');

クロス言語検索は、何をしても問題になります。実際には、複数の一致戦略を使用します。検索用語をtsvector構成内location_name、保存されているテキストの言語と比較します。willglynn が示唆するようなトライグラムベースのアプローチも使用する可能性があり、次に表示用の結果を統合して、一般的な用語を探します。simple

Pg の全文検索が制限されすぎていることに気付く可能性があります。その場合は、Lucerne / Solrなどを調べてみてください。

参照: *全文検索の制御。* tsearch 辞書

于 2012-10-11T04:12:14.540 に答える
1

@willglynnがすでに投稿したものと同様に、pg_trgmモジュールを検討します。ただし、GiSTインデックスを使用することをお勧めします。

CREATE INDEX tbl_location_name_trgm_idx
USING gist(location_name gist_trgm_ops);

gist_trgm_ops演算子クラスは一般的に大文字と小文字を区別せず、と同じILIKEくらい高速LIKEです。ソースコードの引用:

注意:IGNORECASEマクロは、トリグラムで大文字と小文字が区別されないことを意味します。

私はCOLLATE "C"ここで使用します-これは事実上特別な照合ではありません(代わりにバイトオーダー)。これは明らかに、列にさまざまな照合が混在しているためです。照合は順序や範囲に関連しています。基本的な類似性検索では、照合なしで実行できます。COLLATE "C"まず、コラムの設定を検討します。

このインデックスは、クエリの最初の単純な形式をサポートします。

SELECT * FROM tbl WHERE location_name ILIKE '%cafe%';
  • とても早い。
  • 部分一致を見つける機能を保持します。
  • あいまい検索の機能を追加します。演算子とを
    チェックしてください。%set_limit()
  • GiSTインデックスは、LIMIT nn個の「最適な」一致を選択するクエリに対しても非常に高速です。上記のクエリに追加できます。

ORDER BY location_name <-> 'cafe'
LIMIT 20

「距離」演算子の詳細については、<-> こちらのマニュアルをご覧ください

あるいは:

SELECT *
FROM   tbl
WHERE  location_name ILIKE '%cafe%'        -- exact partial match
OR     location_name %     'cafe'          -- fuzzy match
ORDER  BY 
       (location_name ILIKE 'cafe%') DESC  -- exact beginning first
      ,(location_name ILIKE '%cafe%') DESC -- exact partial match next
      ,(location_name <->   'cafe')        -- then "best" matches
      ,location_name                       -- break remaining ties (collation!)
LIMIT  20;

私は(私にとって)満足のいく結果を得るために、いくつかのアプリケーションでそのようなものを使用しています。もちろん、複数の機能を組み合わせて適用すると、少し遅くなります。あなたのスイートスポットを見つけてください...

さらに一歩進んで、言語ごとに個別の部分インデックスを作成し、それぞれに一致する照合を使用することができます。

CREATE INDEX location_name_trgm_idx
USING gist(location_name COLLATE "de_DE" gist_trgm_ops)
WHERE location_name_language = 'German';

-- repeat for each language

これは、クエリごとに特定の言語の結果のみが必要であり、この場合は非常に高速である場合にのみ役立ちます。

于 2012-10-15T18:02:15.360 に答える