[Groonga-commit] pgroonga/pgroonga at e90baca [master] Document how to use jsonb

Back to archive index

Kouhei Sutou null+****@clear*****
Tue Sep 29 17:04:27 JST 2015


Kouhei Sutou	2015-09-29 17:04:27 +0900 (Tue, 29 Sep 2015)

  New Revision: e90baca84af714aab54bfaf054aa9339ca508186
  https://github.com/pgroonga/pgroonga/commit/e90baca84af714aab54bfaf054aa9339ca508186

  Message:
    Document how to use jsonb

  Modified files:
    README.md

  Modified: README.md (+299 -0)
===================================================================
--- README.md    2015-09-29 17:04:15 +0900 (dcffaac)
+++ README.md    2015-09-29 17:04:27 +0900 (367ef20)
@@ -941,6 +941,305 @@ SELECT * FROM products WHERE tags %% 'PostgreSQL';
 -- (2 行)
 ```
 
+### `jsonb`を使う
+
+PGroongaは`jsonb`型のデータもサポートしています。PGroongaのインデック
+スを作成することにより高速に検索できます。
+
+PGroongaは`jsonb`の検索のために次の2つの演算子を提供しています。
+
+  * `@>`演算子
+  * `@@`演算子
+
+[`@>`演算子はPostgreSQLが標準で提供している演算子](http://www.postgresql.org/docs/current/static/functions-json.html#FUNCTIONS-JSONB-OP-TABLE)
+です。右辺が左辺のサブセットなら真になります。
+
+`@>`演算子はGINでも高速化できる演算子です。インデックス作成時間は
+PGroongaとGINでそれほど変わりませんが、検索時間はPGroongaの方が少し速
+いです。
+
+`@@`演算子はPGroonga独自の演算子です。GINではインデックスを使えない複
+雑な検索条件も記述できます。もし、
+[JsQuery](https://github.com/postgrespro/jsquery)を知っているなら
+構文が違うJsQueryのようなものと考えてください。
+
+JsQueryができる検索とPGroongaができる検索はほぼ同じですが、PGroongaだ
+けができる特徴的な検索は全文字列値に対しての全文検索です。
+
+たとえば、次のJSONがあるとします。
+
+```json
+{
+  "message": "Server is started.",
+  "host": "www.example.com",
+  "tags": [
+    "web",
+  ]
+}
+```
+
+すべての文字列値に対して全文検索ができるので、「`server`」でも
+「`example`」でも「`web`」でもヒットします。
+
+#### サンプル用テーブル定義とサンプルデータ
+
+例を示すために使うサンプル用のテーブル定義とサンプルデータを次に示します。
+
+```sql
+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"
+                ]
+              }');
+```
+
+少ないデータでもインデックスを使うようにシーケンシャルスキャンを無効に
+します。
+
+```sql
+SET enable_seqscan = off;
+```
+
+#### `@>`演算子
+
+`@>`演算子は`jsonb`で条件を指定します。カラムの値が条件として指定した
+`jsonb`を含んでいればマッチします。
+
+マッチする例です。(結果を見やすくするためにPostgreSQL 9.5から使える
+`jsonb_pretty()`関数を使っています。)
+
+```sql
+SELECT jsonb_pretty(record) FROM logs WHERE record @> '{"host": "www.example.com"}'::jsonb;
+--             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)
+```
+
+マッチしない例です。
+
+条件の`jsonb`で配列を指定した場合、すべての要素が含まれていればマッチ
+します。(配列の要素の順序は関係ありません。)しかし、1つでも含まれて
+いない要素があればマッチしません。次の`tags`に`"mail"`を含むレコードも
+`"web"`を含むレコードもありますが、両方含むレコードはないのでヒットし
+ません。
+
+```sql
+SELECT jsonb_pretty(record) FROM logs WHERE record @> '{"tags": ["mail", "web"]}'::jsonb;
+--  jsonb_pretty 
+-- --------------
+-- (0 rows)
+```
+
+#### `@@`演算子
+
+`@@`演算子は
+[Groongaのスクリプト構文](http://groonga.org/ja/docs/reference/grn_expr/script_syntax.html)
+で条件を指定します。条件をどのように指定すればよいかわかるためには、
+PGroongaがどのように`jsonb`のデータに対してインデックスを作成している
+かを理解する必要があります。
+
+PGroongaは`jsonb`の値を分解し、それぞれの値に対してインデックスを張っ
+ています。SQLでいうと次のスキーマがあると考えてください。
+
+```sql
+CREATE TABLE values (
+  key text PRIMARY KEY,
+  path text,
+  paths text[],
+  type text,
+  boolean boolean,
+  number double,
+  string text,
+  size numeric
+);
+```
+
+それぞれ次の値が入っています。
+
+  * `key`: 同じ値では同一になる値。フォーマットは「`${パス}|${種類}|${値}`」。条件で使うことはない。
+  * `path`: その値の位置を示すルートからのパス。[jq](https://stedolan.github.io/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を考えます。
+
+```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`がマッチした`jsonb`になります。
+
+`www.example.com`という文字列を含んだ`jsonb`を検索する場合は次のようにします。
+
+```sql
+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`台のレコードを検索する場合は次のようにします。省略記法(`.code`)でパスを指定したいので`paths @ "..."`という条件指定をしています。
+
+```sql
+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)
+```
+
+全文検索をする場合は次のように`string @ "..."`を使います。
+
+```sql
+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)
+```
+
+クエリー構文(`a OR b`のような書き方)を使って全文検索をしたい場合は
+`query("string", "...")`を使います。
+
+```sql
+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)
+```
+
 ### Groongaの機能を使う
 
 多くの場合、PostgreSQLよりGroongaの方が高速に処理できます。たとえば、
-------------- next part --------------
HTML����������������������������...
下載 



More information about the Groonga-commit mailing list
Back to archive index