全文検索¶
django.contrib.postgres.search モジュールのデータベース関数は PostgreSQL の 全文検索エンジン を利用しやすくしています。
このドキュメントの例では、 クエリを作成する で定義されたモデルを使用します。
参考
検索の概要については、 トピックのドキュメント を参照してください。
search ルックアップ¶
全文検索の一般的な使用方法は、データベースの1つのカラムに対して1つの用語を検索することです。たとえば:
>>> Entry.objects.filter(body_text__search="Cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>
これにより、データベース内の body_text フィールドから to_tsvector が作成され、検索語 'Cheese' から plainto_tsquery が作成されます。両方ともデフォルトのデータベース検索設定を使用しています。結果は、クエリとベクトルを一致させることで取得されます。
search ルックアップを使用するには、INSTALLED_APPS に 'django.contrib.postgres' を含める必要があります。
SearchVector¶
単一のフィールドに対する検索は素晴らしいですが、制約があります。検索対象となる Entry インスタンスは Blog に属しており、 tagline フィールドを持っています。両方のフィールドに対してクエリを行うには、 SearchVector を使用します:
>>> from django.contrib.postgres.search import SearchVector
>>> Entry.objects.annotate(
... search=SearchVector("body_text", "blog__tagline"),
... ).filter(search="Cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>
SearchVector への引数は、任意の Expression またはフィールドの名前を指定できます。複数の引数は、スペースを使用して連結され、検索ドキュメントにすべて含まれます。
SearchVector オブジェクトは組み合わせて再利用できます。たとえば:
>>> Entry.objects.annotate(
... search=SearchVector("body_text") + SearchVector("blog__tagline"),
... ).filter(search="Cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>
config パラメータおよび weight パラメータの説明については、検索設定を変更する と クエリを重み付けする を参照してください。
SearchQuery¶
SearchQuery は、ユーザーが提供した用語を検索クエリオブジェクトに変換し、データベースが検索ベクトルと比較する機能を提供します。デフォルトでは、ユーザーが提供したすべての単語がステミングアルゴリズムを通過され、その結果の用語すべてに一致するものを検索します。
もし search_type が 'plain' (デフォルト) の場合、用語は別々のキーワードとして扱われます。もし search_type が 'phrase' の場合、用語は1つのフレーズとして扱われます。もし search_type が 'raw' の場合、用語と演算子を含む書式付きの検索クエリを指定できます。もし search_type が 'websearch' の場合、Web検索エンジンで使用されるものに類似した書式で検索クエリを指定できます。 'websearch' には PostgreSQL ≥ 11 が必要です。違いや構文については PostgreSQL の Full Text Search docs を参照してください。例:
>>> from django.contrib.postgres.search import SearchQuery, Lexeme
>>> SearchQuery("red tomato") # two keywords
>>> SearchQuery("tomato red") # same results as above
>>> SearchQuery("red tomato", search_type="phrase") # a phrase
>>> SearchQuery("tomato red", search_type="phrase") # a different phrase
>>> SearchQuery("'tomato' & ('red' | 'green')", search_type="raw") # boolean operators
>>> SearchQuery(
... "'tomato' ('red' OR 'green')", search_type="websearch"
... ) # websearch operators
>>> SearchQuery(Lexeme("tomato") & (Lexeme("red") | Lexeme("green"))) # Lexeme objects
SearchQuery の用語は論理的に組み合わせることで、柔軟性を高めることができます:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery("meat") & SearchQuery("cheese") # AND
>>> SearchQuery("meat") | SearchQuery("cheese") # OR
>>> ~SearchQuery("meat") # NOT
config パラメータの説明については、検索設定を変更する を参照してください。
Lexeme objects were added.
SearchRank¶
これまでに、ベクトルとクエリの間で一致する結果を返してきました。おそらく結果をある種の関連性によって並べ替えたいと思うかもしれません。PostgreSQLにはランキング関数が用意されており、クエリ用語がドキュメント内にどれだけ多く現れるか、用語がドキュメント内でどれだけ近くにあるか、そしてそれらが発生するドキュメント内の部分がどれだけ重要かを考慮します。一致度が高いほど、ランクの値が高くなります。関連度で並び替えるには:
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector("body_text")
>>> query = SearchQuery("cheese")
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).order_by("-rank")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]>
weights パラメータの説明については、クエリを重み付けする を参照してください。
cover_density パラメーターを True に設定して、カバー密度ランキングを有効にします。これにより、クエリ用語の一致する近接度が考慮されます。
normalization パラメータに整数を指定すると、順位の正規化を制御できます。この整数はビットマスクであり、複数の動作を組み合わせることができます。
>>> from django.db.models import Value
>>> Entry.objects.annotate(
... rank=SearchRank(
... vector,
... query,
... normalization=Value(2).bitor(Value(4)),
... )
... )
PostgreSQLのドキュメントに、 different rank normalization options についての詳細があります。
SearchHeadline¶
- class SearchHeadline(expression, query, config=None, start_sel=None, stop_sel=None, max_words=None, min_words=None, short_word=None, highlight_all=None, max_fragments=None, fragment_delimiter=None)[ソース]¶
単一のテキストフィールドまたは式、クエリ、設定、およびオプションのセットを受け入れ、ハイライトされた検索結果を返します。
start_sel パラメータと stop_sel パラメータには、ハイライトされたクエリ語をドキュメントで囲むために使用する文字列値を設定します。PostgreSQLのデフォルトは <b> と </b> です。
パラメータ max_words と min_words に整数値を指定して、最長と最短の見出しを決定します。PostgreSQLのデフォルトは35と15です。
short_word パラメータに整数値を指定すると、各見出しでこの長さ以下の単語を破棄します。PostgreSQLのデフォルトは3です。
highlight_all パラメータを True に設定すると、フラグメントの代わりにドキュメント全体を使用し、 max_words、min_words、short_word パラメータを無視します。PostgreSQLではデフォルトで無効になっています。
表示するフラグメントの最大数を設定するには、 max_fragments に0以外の整数値を指定してください。PostgreSQLではデフォルトで無効になっています。
fragment_delimiter 文字列パラメータを設定すると、フラグメント間の区切り文字を構成できます。PostgreSQL のデフォルトは " ... " です。
PostgreSQL のドキュメントには、highlighting search results に関する詳細が記載されています。
使用例:
>>> from django.contrib.postgres.search import SearchHeadline, SearchQuery
>>> query = SearchQuery("red tomato")
>>> entry = Entry.objects.annotate(
... headline=SearchHeadline(
... "body_text",
... query,
... start_sel="<span>",
... stop_sel="</span>",
... ),
... ).get()
>>> print(entry.headline)
Sandwich with <span>tomato</span> and <span>red</span> cheese.
config パラメータの説明については、検索設定を変更する を参照してください。
検索設定を変更する¶
SearchVector および SearchQuery に config 属性を指定して、異なる検索設定を使用できます。これにより、データベースで定義された異なる言語の解析機能や辞書を使用できます。
>>> from django.contrib.postgres.search import SearchQuery, SearchVector
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config="french"),
... ).filter(search=SearchQuery("œuf", config="french"))
<QuerySet [<Entry: Pain perdu>]>
config の値は別の列にも保存できます。
>>> from django.db.models import F
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config=F("blog__language")),
... ).filter(search=SearchQuery("œuf", config=F("blog__language")))
<QuerySet [<Entry: Pain perdu>]>
クエリを重み付けする¶
すべてのフィールドがクエリにおいて同じ重要度を持つとは限りません。そのため、それらを組み合わせる前に、複数のベクトルの重みを設定できます。
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector("body_text", weight="A") + SearchVector(
... "blog__tagline", weight="B"
... )
>>> query = SearchQuery("cheese")
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.3).order_by(
... "rank"
... )
重みは次のいずれかの文字である必要があります: D, C, B, A。デフォルトでは、これらの重みはそれぞれ 0.1, 0.2, 0.4, 1.0 に対応しています。異なる重みを使用する場合は、同じ順序で四つの浮動小数点数のリストを、上記の通りに SearchRank に weights として渡してください。
>>> rank = SearchRank(vector, query, weights=[0.2, 0.4, 0.6, 0.8])
>>> Entry.objects.annotate(rank=rank).filter(rank__gte=0.3).order_by("-rank")
Lexeme¶
Lexeme objects allow search operators to be safely used with strings from
an untrusted source. The content of each lexeme is escaped so that any
operators that may exist in the string itself will not be interpreted.
You can combine lexemes with other lexemes using the & and | operators
and also negate them with the ~ operator. For example:
>>> from django.contrib.postgres.search import SearchQuery, SearchVector, Lexeme
>>> vector = SearchVector("body_text", "blog__tagline")
>>> Entry.objects.annotate(search=vector).filter(
... search=SearchQuery(Lexeme("fruit") & Lexeme("dessert"))
... )
<QuerySet [<Entry: Apple Crumble Recipes>, <Entry: Banana Split Recipes>]>
>>> Entry.objects.annotate(search=vector).filter(
... search=SearchQuery(Lexeme("fruit") & Lexeme("dessert") & ~Lexeme("banana"))
... )
<QuerySet [<Entry: Apple Crumble Recipes>]>
Lexeme objects also support term weighting and prefixes:
>>> Entry.objects.annotate(search=vector).filter(
... search=SearchQuery(Lexeme("Pizza") | Lexeme("Cheese"))
... )
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]>
>>> Entry.objects.annotate(search=vector).filter(
... search=SearchQuery(Lexeme("Pizza") | Lexeme("Cheese", weight="A"))
... )
<QuerySet [<Entry: Pizza recipes>]>
>>> Entry.objects.annotate(search=vector).filter(
... search=SearchQuery(Lexeme("za", prefix=True))
... )
<QuerySet []>
パフォーマンス¶
これらの関数を使用するために特別なデータベース設定をする必要はありませんが、数百以上のレコードを検索する場合、パフォーマンスの問題に直面する可能性が高くなります。全文検索は、例えば整数のサイズを比較するよりも負荷の高い処理です。
クエリ対象の全てのフィールドが特定のモデルに含まれている場合、使いたい検索ベクトルにマッチする関数 GIN または GiST インデックスを作成できます。たとえば:
GinIndex(
SearchVector("body_text", "headline", config="english"),
name="search_vector_idx",
)
The PostgreSQL docs has details on creating indexes for full text search.
SearchVectorField¶
この方法が遅すぎる場合は、モデルに SearchVectorField を追加できます。このフィールドには、例えば PostgreSQL documentation で説明されているように、トリガーを入力しておく必要があります。そうすれば、あたかもアノテーションされた SearchVector のようにフィールドに問い合わせることができます:
>>> Entry.objects.update(search_vector=SearchVector("body_text"))
>>> Entry.objects.filter(search_vector="cheese")
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]>
trigram(トリグラム)類似度¶
検索の別の手法として trigram 類似度があります。trigram は3つの連続する文字のグループです。 trigram_similar、trigram_word_similar、trigram_strict_word_similar の他にもいくつかの式を使用できます。
これらを使用するには、PostgreSQL上で pg_trgm 拡張機能 を有効にする必要があります。これは、 TrigramExtension マイグレーション・オペレーションを使用してインストールできます。
TrigramSimilarity¶
フィールド名や式、文字列や式を受け取ります。2つの引数の間の trigram 類似度を返します。
使用例:
>>> from django.contrib.postgres.search import TrigramSimilarity
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Katie Stephens"
>>> Author.objects.annotate(
... similarity=TrigramSimilarity("name", test),
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
<QuerySet [<Author: Katy Stevens>, <Author: Stephen Keats>]>
TrigramWordSimilarity¶
文字列または式、およびフィールド名または式を受け入れます。2つの引数間の trigram 単語類似度を返します。
使用例:
>>> from django.contrib.postgres.search import TrigramWordSimilarity
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Kat"
>>> Author.objects.annotate(
... similarity=TrigramWordSimilarity(test, "name"),
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
<QuerySet [<Author: Katy Stevens>]>
TrigramStrictWordSimilarity¶
文字列または式とフィールド名または式を受け取り、2つの引数間の trigram 厳密単語類似度を返します。 TrigramWordSimilarity() と似ていますが、この関数は範囲境界を単語境界と一致させるように強制します。
TrigramDistance¶
フィールド名または式と、文字列または式を受け入れます。2つの引数の間の trigram 距離を返します。
使用例:
>>> from django.contrib.postgres.search import TrigramDistance
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Katie Stephens"
>>> Author.objects.annotate(
... distance=TrigramDistance("name", test),
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
<QuerySet [<Author: Katy Stevens>, <Author: Stephen Keats>]>
TrigramWordDistance¶
文字列または式と、フィールド名または式を受け取ります。2つの引数間の trigram 単語距離を返します。
使用例:
>>> from django.contrib.postgres.search import TrigramWordDistance
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Kat"
>>> Author.objects.annotate(
... distance=TrigramWordDistance(test, "name"),
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
<QuerySet [<Author: Katy Stevens>]>
TrigramStrictWordDistance¶
文字列または式、フィールド名または式を受け入れ、これらの引数間の trigram strict 単語距離を返します。