これはPGroonga 2.X and 3.X用のドキュメントです。古いPGroongaを使っているならPGroonga 1.xのドキュメントを見てください。

pgroonga_condition() 関数

3.1.6で追加。

概要

pgroonga_condition()関数はpgroonga_condition型の値を返します。 関数名と型名が同じですが別物です。 pgroonga_condition型はpgroonga_full_text_search_condition型やpgroonga_full_text_search_condition_with_scorers型のように複雑な条件式を表現します。

pgroonga_condition()関数はpgroonga_condition型の値を作るための便利関数です。 特定の属性値のみを指定してpgroonga_condition型の値を作れます。

pgroonga_full_text_search_condition型やpgroonga_full_text_search_condition_with_scorers型では、このような便利関数がなかったため必ず全ての属性値を指定して値を作る必要がありました。

したがって、不要な属性値があっても、pgroonga_full_text_search_condition型やpgroonga_full_text_search_condition_with_scorers型では、次のように不要な属性値にはNULLを指定する必要がありました。

title &@~ ('keyword', NULL, 'index_name')::pgroonga_full_text_search_condition
title &@~ ('keyword', ARRAY[1,1,1,5,0], NULL, 'index_name')::pgroonga_full_text_search_condition_with_scorers

型を指定して直接値を作る従来の方法では後方互換性を維持したまま新しい属性を作ることができませんでした。 そのため、新しい属性を追加するたびにpgroonga_full_text_search_condition_with_XXXというような新しい型を追加する必要がありました。 たとえば、pgroogna_full_text_search_condition_with_scorers型はそのために追加された型です。

pgroonga_full_text_search_condition型とpgroonga_full_text_search_condition_with_scorers型の違いはscorersが存在するかどうかですが、pgroonga_full_text_search_condition型にscorersを追加してしまうと、scorersを使わないユーザーも新たにNULLを挿入してpgroonga_full_text_search_condition型の値を作らなければなりません。

しかし、pgroonga_condition型の値を作るための便利関数pgroonga_condition()関数を導入することにより後方互換性を維持したままpgroonga_condition型に新しい属性を追加できます。 pgroonga_condition()関数が非互換を吸収してくれるからです。

次のように、pgroonga_condition()関数は不要な属性値を省略できるため、新たに属性値が追加されても既存の書き方を維持できます。 (次の例では、weightsscorersschema_namecolumn_nameを省略しています。属性値の詳細については、後述の「構文」で記載します。ここでは、不要な属性値が省略できることに注目してください。)

title &@~ pgroonga_condition('keyword', index_name => 'index_name')

pgroonga_condition()関数では、上のように属性値を省略できますが、代わりに、「index_name => 'index name'」のようにキーワード引数のような記載が必要になることに注意してください。

上の例では、キーワード引数のような書き方をしている属性値とそうでない属性値があります。 どのように書き分けるかについては、後述の「構文」で記載します。 ここでは、従来とは異なる書き方が必要になることがあるという点に注目してください。

構文

この関数の構文は次の通りです。

pgroonga_condition pgroonga_condition(keyword,
                                      weights,
                                      scorers,
                                      schema_name,
                                      index_name,
                                      column_name,
                                      fuzzy_max_distance_ratio)

keywordは全文検索で使うキーワードです。text型です。

weightsは、検索対象のカラムの重要度です。int[]型です。

scorersは、検索対象のカラムのスコアーを計算するスコアラーです。text[]型です。

schema_nameは、シーケンシャルサーチ実行時に参照するインデックスが属するスキーマの名前です。text型です。

index_nameは、シーケンシャルサーチ実行時に参照するインデックスの名前です。text型です。

column_nameは、シーケンシャルサーチ実行時に参照するインデックス内のカラムの名前です。text型です

fuzzy_max_distance_ratioは編集距離の割合です。float4型です。(3.2.1で追加。) 詳しくはGroongaのfuzzy_max_distance_ratioオプションをご覧ください。

pgroonga_condition()の引数はすべて省略可能です。引数の位置に依らずに、特定の引数を指定したい場合は引数名 => 値という名前付き表記が使えます。たとえば、引数にindex_nameだけ指定する場合はpgroonga_condition(index_name => 'index1')となります。

一般的なユースケースでは次の3種類の書き方を覚えておけば十分です。

pgroonga_condition('keyword', index_name => 'pgroonga_index')
pgroonga_condition('keyword', ARRAY[weight1, weight2, ...])
pgroonga_condition('keyword', ARRAY[weight1, weight2, ...], index_name => 'pgroonga_index')

上の例以外の使い方をする場合のために、引数名 => 値で記述する必要がある引数とそうでない引数の違いについては、関数呼び出しを参照してください。

使い方

index_nameを指定する

シーケンシャルサーチ実行時でも、インデックスに指定したノーマライザーやトークナイザーのオプションを使って検索する方法を紹介します。

pgroonga_condition('keyword', index_name => 'pgroonga_index')を使います。 index_nameにノーマライザーやトークナイザーを指定したインデックスの名前を指定します。

サンプルスキーマとデータは次の通りです。

CREATE TABLE tags (
  name text PRIMARY KEY
);

CREATE INDEX pgroonga_tag_name_index ON tags
  USING pgroonga (name pgroonga_text_term_search_ops_v2)
  WITH (normalizers='NormalizerNFKC150("remove_symbol", true)');

INSERT INTO tags VALUES ('PostgreSQL');
INSERT INTO tags VALUES ('Groonga');
INSERT INTO tags VALUES ('PGroonga');
INSERT INTO tags VALUES ('pglogical');

インデックスサーチ時はインデックスに指定したオプションを使ってインデックスサーチ時の挙動をカスタマイズできます。 上のサンプルでは、normalizers='...'の部分でオプションを指定しています。

一方、インデックスサーチではなくシーケンシャルサーチが実行されると、インデックスに指定されているオプションは参照できません。 シーケンシャルサーチ時はどのインデックスを参照すればよいかという情報がないからです。

そのためシーケンシャルサーチ実行時は、インデックスサーチ実行時と検索結果が異なる可能性があります。 この問題を回避するためにシーケンシャルサーチ時に参照するインデックスを明示的に指定します。 pgroonga_condition()index_name => '...'がそのための引数です。

次の例は、「_p_G」というキーワードで前方一致検索をしており、インデックスにはNormalizerNFKC150("remove_symbol", true)が設定されています。 remove_symbolは記号を無視するオプションなので、「_p_G」は「pg」にノーマライズされます。 (大文字が小文字になっているのは、remove_symbolオプションの挙動ではなく、NormalizerNFKC150のデフォルトの挙動によるものです。) そのため、このオプションが有効であれば、「PGroonga」と「pglogical」がヒットします。

次の例は、シーケンシャルサーチですが、「PGroonga」と「pglogical」がヒットしていることが確認できます。 このことから、シーケンシャルサーチ実行時でもインデックスに設定されているNormalizerNFKC150("remove_symbol", true)が参照できていることが確認できます。

EXPLAIN ANALYZE
SELECT *
  FROM tags
 WHERE name &^ pgroonga_condition('_p_G',
                                  index_name => 'pgroonga_tag_name_index');
                                            QUERY PLAN
--------------------------------------------------------------------------------------------------
 Seq Scan on tags  (cost=0.00..1043.60 rows=1 width=32) (actual time=2.267..2.336 rows=2 loops=1)
   Filter: (name &^ '(_p_G,,,,pgroonga_tag_name_index,)'::pgroonga_condition)
   Rows Removed by Filter: 2
 Planning Time: 0.871 ms
 Execution Time: 2.352 ms
(5 rows)

SELECT *
  FROM tags
 WHERE name &^ pgroonga_condition('_p_G',
                                  index_name => 'pgroonga_tag_name_index');
   name
-----------
 PGroonga
 pglogical
(2 rows)

index_nameを指定しない場合(つまり、NormalizerNFKC150("remove_symbol", true)が参照できない場合)は、次のように「PGroonga」と「pglogical」はヒットしません。

EXPLAIN ANALYZE
SELECT *
  FROM tags
 WHERE name &^ pgroonga_condition('_p_G');
                                            QUERY PLAN
--------------------------------------------------------------------------------------------------
 Seq Scan on tags  (cost=0.00..1043.60 rows=1 width=32) (actual time=0.032..0.032 rows=0 loops=1)
   Filter: (name &^ '(_p_G,,,,,)'::pgroonga_condition)
   Rows Removed by Filter: 4
 Planning Time: 0.910 ms
 Execution Time: 0.053 ms
(5 rows)

SELECT *
  FROM tags
 WHERE name &^ pgroonga_condition('_p_G');

 name
------
(0 rows)

このように、index_nameを指定することで、シーケンシャルサーチ実行時でもインデックスサーチ実行時でも検索結果が変わらないようにできます。

weightsを指定する

カラム毎に異なるweight(重要度)を設定する方法を紹介します。 これにより、「タイトルを本文よりも重要視する」を実現できます。

pgroonga_condition('keyword', ARRAY[weight1, weight2, ...]) を使います。 weight1weight2でカラム毎の重要度を指定します。

サンプルスキーマとデータは次の通りです。

DROP TABLE IF EXISTS memos;
CREATE TABLE memos (
  title text,
  content text
);

CREATE INDEX pgroonga_memos_index
    ON memos
 USING pgroonga ((ARRAY[title, content]));

INSERT INTO memos VALUES ('PostgreSQL', 'PostgreSQLはリレーショナル・データベース管理システムです。');
INSERT INTO memos VALUES ('Groonga', 'Groongaは日本語対応の高速な全文検索エンジンです。');
INSERT INTO memos VALUES ('PGroonga', 'PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。');
INSERT INTO memos VALUES ('コマンドライン', 'groongaコマンドがあります。');

指定したクエリーによりマッチしたレコードを探すためにはpgroonga_score functionを使えます。

SELECT *, pgroonga_score(tableoid, ctid) AS score
  FROM memos
 WHERE ARRAY[title, content] &@~
       pgroonga_condition('Groonga OR PostgreSQL', ARRAY[5, 1])
 ORDER BY score DESC;
      title      |                                  content                                  | score 
----------------+---------------------------------------------------------------------------+-------
 Groonga        | Groongaは日本語対応の高速な全文検索エンジンです。                         |     6
 PostgreSQL     | PostgreSQLはリレーショナル・データベース管理システムです。                |     6
 PGroonga       | PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。 |     2
 コマンドライン | groongaコマンドがあります。                                               |     1
(4 rows)

上の例では、ARRAY[title, content] &@~ pgroonga_condition('Groonga OR PostgreSQL', ARRAY[5, 1])と指定しているので、タイトルが本文より5倍重要としています。 titleカラムに「Groonga」または「PostgreSQL」があるレコードの方がcontentカラムに「Groonga」または「PostgreSQL」がある方よりスコアーが高いことを確認できます。

特定のカラムを検索対象から除外する

特定のカラムを検索対象から除外して検索する方法を紹介します。

pgroonga_condition('keyword', ARRAY[weight1, weight2, ...])を使います。 検索対象から除外したいカラムに対応するweight0を指定します。

サンプルスキーマとデータは次の通りです。

DROP TABLE IF EXISTS memos;
CREATE TABLE memos (
  title text,
  content text
);

CREATE INDEX pgroonga_memos_index
    ON memos
 USING pgroonga ((ARRAY[title, content]));

INSERT INTO memos VALUES ('PostgreSQL', 'PostgreSQLはリレーショナル・データベース管理システムです。');
INSERT INTO memos VALUES ('Groonga', 'Groongaは日本語対応の高速な全文検索エンジンです。');
INSERT INTO memos VALUES ('PGroonga', 'PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。');
INSERT INTO memos VALUES ('コマンドライン', 'groongaコマンドがあります。');

次の例では、contentカラムを検索対象から除外しています。 「拡張」というキーワードで全文検索しているので、contentカラムを検索対象としていれば、'PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。'がヒットするはずですが、このレコードはヒットしていません。 このことから、contentカラムが検索対象から除外されていることを確認できます。

SELECT *
  FROM memos
 WHERE ARRAY[title, content] &@~
       pgroonga_condition('拡張', ARRAY[1, 0]);
 title | content
-------+---------
(0 rows)

次のように、contentカラムを検索対象にすると'PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。'がヒットします。

SELECT *
  FROM memos
 WHERE ARRAY[title, content] &@~
       pgroonga_condition('拡張', ARRAY[1, 1]);
  title   |                                  content
----------+---------------------------------------------------------------------------
 PGroonga | PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。
(1 row)

参考