これはPGroonga 1.X用のドキュメントです。新しいPGroongaを使っているならPGroonga 2.xのドキュメントを見てください。
jsonb
型用の@@
演算子この演算子は1.2.1から非推奨になりました。代わりに&`
演算子を使ってください。
@@
演算子はPGroonga独自の演算子です。@>
演算子では書けない範囲検索のような複雑な条件を書くことができます。
もしJsQueryを知っているなら、「PGroongaはJsQueryが提供しているようなjsonb
型用の検索機能を違う構文で提供している」と理解してください。
この演算子の構文は次の通りです。
jsonb_column @@ condition
jsonb_column
はjsonb
型のカラムです。
condition
はクエリーとして使うtext
型の値です。Groongaのスクリプト構文を使います。
この演算子はcondition
がjsonb_column
の値にマッチしたらtrue
を返し、マッチしなかったらfalse
を返します。
この演算子を使うには次のどれかの演算子クラスを指定する必要があります。
pgroonga.jsonb_ops
:jsonb
型のデフォルト
pgroonga.jsonb_ops_v2
:jsonb
型用
例に使うサンプルスキーマとデータは次の通りです。
CREATE TABLE logs (
record jsonb
);
CREATE INDEX pgroonga_logs_index ON logs USING pgroonga (record);
INSERT INTO logs
VALUES ('{
"message": "Server is started.",
"host": "www.example.com",
"tags": [
"web",
"example.com"
]
}');
INSERT INTO logs
VALUES ('{
"message": "GET /",
"host": "www.example.com",
"code": 200,
"tags": [
"web",
"example.com"
]
}');
INSERT INTO logs
VALUES ('{
"message": "Send to <info@example.com>.",
"host": "mail.example.net",
"tags": [
"mail",
"example.net"
]
}');
シーケンシャルスキャンを無効にします。
SET enable_seqscan = off;
検索条件を作るためにはPGroongaがjsonb
型のデータ用のインデックスをどのように作るかを理解する必要があります。
PGroongaはjsonb
型の値を複数の値に分割し、それらの分割した値に対してインデックスを作成します。SQLで言うと次のスキーマを作っていると考えてください。
CREATE TABLE values (
key text PRIMARY KEY,
path text,
paths text[],
type text,
boolean boolean,
number double precision,
string text,
size numeric
);
各カラムの説明は次の通りです。
key
:値のIDです。値が違うパスで違う内容ならkey
は違う値になります。キーのフォーマットは'${パス}|${種類}|${値}'
です。このカラムは検索条件には使いません。
path
:値がある位置へのルートからのパスです。jq互換のフォーマットです。オブジェクトは["${要素名}"]
で配列は[]
です。たとえば、{"tags": ["web"]}
の中の"web"
のパスは.["tags"][]
です。もし、値の絶対パスを知っているなら検索条件でこの値を使えます。
paths
: 値を示すパスです。値を示すパスは複数あります。絶対パス、サブパス、.${要素名1}.${要素名2}
というフォーマットのパス、配列部分を省略したパスがあります。このカラムは検索条件の指定を便利にするために用意されています。検索時にはこの中のパスのどれでも使うことができます。以下はどれも{"a": {"b": "c": ["x"]}}
の中の"x"
を指定するパスです。
.a.b.c
.["a"]["b"]["c"]
.["a"]["b"]["c"][]
a.b.c
["a"]["b"]["c"]
["a"]["b"]["c"][]
b.c
["b"]["c"]
["b"]["c"][]
c
["c"]
["c"][]
[]
type
:値の種類です。このカラムの値は次のどれかになります。
"object"
:オブジェクト。値はありません。
"array"
:配列。size
カラムに要素数が入っています。
"boolean"
:真偽値。boolean
カラムに値が入っています。
"number"
:数値。number
カラムに値が入っています。
"string"
:文字列。string
カラムに値が入っています。
boolean
:type
カラムの値が"boolean"
なら値が入っています。それ以外のときはfalse
が入っています。
number
:type
カラムの値が"number"
なら値が入っています。それ以外のときは0
が入っています。
string
:type
カラムの値が"string"
なら値が入っています。それ以外のときは""
が入っています。
size
:type
カラムの値が"array"
なら要素数が入っています。それ以外のときは0
が入っています。
以下はサンプルJSONです。
{
"message": "GET /",
"host": "www.example.com",
"code": 200,
"tags": [
"web",
"example.com"
]
}
このJSONを次の値に分割します。(これは分割した値の一部です。)
key | path | paths | type | boolean | number | string | size |
---|---|---|---|---|---|---|---|
.|object |
. |
[.] |
object |
||||
.["message"]|string|GET / |
.["message"] |
[.message, .["message"], message, ["message"]] |
string |
GET / |
|||
.["tags"][]|string|web |
.["tags"] |
[.tags, .["tags"], .["tags"][], tags, ["tags"], ["tags"][], []] |
string |
web |
@@
演算子を使って分割した値にマッチする条件を指定します。もし、jsonb
型の値の中に条件にマッチする分割した値が1つ以上ある場合はそのjsonb
型の値はマッチしたことになります。
次はwww.example.com
という文字列値を含むjsonb
型の値を検索する条件です。
(読みやすくするためにPostgreSQL 9.5以降で使えるjsonb_pretty()
関数を使っています。)
SELECT jsonb_pretty(record) FROM logs WHERE record @@ 'string == "www.example.com"';
-- jsonb_pretty
-- -------------------------------------
-- { +
-- "host": "www.example.com", +
-- "tags": [ +
-- "web", +
-- "example.com" +
-- ], +
-- "message": "Server is started."+
-- }
-- { +
-- "code": 200, +
-- "host": "www.example.com", +
-- "tags": [ +
-- "web", +
-- "example.com" +
-- ], +
-- "message": "GET /" +
-- }
-- (2 rows)
以下はcode
カラムの値として200
から299
の間の数値を持っているjsonb
型の値を検索する条件です。この条件はパスの指定に簡易パスフォーマット(.code
)を使うため、paths @ "..."
という構文を使っています。
SELECT jsonb_pretty(record) FROM logs WHERE record @@ 'paths @ ".code" && number >= 200 && number < 300';
-- jsonb_pretty
-- --------------------------------
-- { +
-- "code": 200, +
-- "host": "www.example.com",+
-- "tags": [ +
-- "web", +
-- "example.com" +
-- ], +
-- "message": "GET /" +
-- }
-- (1 row)
jsonb
型の値の中のすべてのテキスト値に対して全文検索をする条件は次の通りです。
SELECT jsonb_pretty(record) FROM logs WHERE record @@ 'string @ "started"';
-- jsonb_pretty
-- -------------------------------------
-- { +
-- "host": "www.example.com", +
-- "tags": [ +
-- "web", +
-- "example.com" +
-- ], +
-- "message": "Server is started."+
-- }
-- (1 row)
全文検索用にGroongaのクエリー構文(a OR b
という構文を使えます)を使うにはquery("string", "...")
という構文を使います。
SELECT jsonb_pretty(record) FROM logs WHERE record @@ 'query("string", "send OR server")';
-- jsonb_pretty
-- ----------------------------------------------
-- { +
-- "host": "www.example.com", +
-- "tags": [ +
-- "web", +
-- "example.com" +
-- ], +
-- "message": "Server is started." +
-- }
-- { +
-- "host": "mail.example.net", +
-- "tags": [ +
-- "mail", +
-- "example.net" +
-- ], +
-- "message": "Send to <info@example.com>."+
-- }
-- (2 rows)