jsonb_diff

2つのjsonb文書の差分を表示するSQL関数。

インストール

$ git clone https://github.com/nuko-yokohama/jsonb_diff
$ make USE_PGXS=1 install
$ psql testdb -c 'CREATE EXTENSION jsonb_diff'

提供するSQL関数形式

jsonb_diff(lj jsonb, rj jsonb)

引数

  • lj, rj JSONB型のデータ

返却値

  • diff 行型

diff行型は以下のような列を持ちます。

列名 内容
kind text 差分の種別。aは追加、uは更新、dは削除を示す。
left_path text 差分箇所のパス文字列(jsonbのパス文字列)
left_schema jsonb 差分箇所のスキーマを示すJSONBデータ
right_path text 差分箇所のパス文字列(jsonbのパス文字列)
right_schema jsonb 差分箇所のスキーマを示すJSONBデータ

このEXTENSIONを登録すると、jsonb_diff()以外に数種類のSQL関数も登録されるが、それらは内部関数であるため、SQLからの呼び出しは推奨しない。

使用例

例えば、以下の2つのJSONB文書(わかりやすくするため、jsonb_prettyで表示する)がある。

left側

jsonb_diff=# SELECT jsonb_pretty('{"id":1, "name":"foo", "data":"foo", "col":1, "arr":[1,"2",3,4] }');
    jsonb_pretty
--------------------
 {                 +
     "id": 1,      +
     "arr": [      +
         1,        +
         "2",      +
         3,        +
         4         +
     ],            +
     "col": 1,     +
     "data": "foo",+
     "name": "foo" +
 }
(1 row)

right側

jsonb_diff=# SELECT jsonb_pretty('{"id":1, "data":"bar", "val":1, "arr":[1,2,3]}');
   jsonb_pretty
-------------------
 {                +
     "id": 1,     +
     "arr": [     +
         1,       +
         2,       +
         3        +
     ],           +
     "val": 1,    +
     "data": "bar"+
 }
(1 row)

この2つのJSONB文書をjsonb_diff関数で比較すると以下のようになる。

jsonb_diff=# SELECT * FROM jsonb_diff(
  '{"id":1, "name":"foo", "data":"foo", "col":1, "arr":[1,"2",3,4] }',
  '{"id":1, "data":"bar", "val":1, "arr":[1,2,3]}'
);
  kind  | left_path  |                                           left_schema                                           | right_path |
          right_schema
--------+------------+-------------------------------------------------------------------------------------------------+------------+-----------------------------------------------------------------------------
 update |            | [{"id": "number"}, {"arr": "array"}, {"col": "number"}, {"data": "string"}, {"name": "string"}] |            | [{"id": "number"}, {"arr": "array"}, {"val": "number"}, {"data": "string"}]
 update | ->'arr'    | [{"length": 4}, "number", "string", "number", "number"]                                         | ->'arr'    | [{"length": 3}, "number", "number", "number"]
 delete | ->'col'    | {"number": 1}                                                                                   |            |
 update | ->'arr'->1 | {"string": "2"}                                                                                 | ->'arr'->1 | {"number": 2}
 delete | ->'name'   | {"string": "foo"}                                                                               |            |
 append |            |                                                                                                 | ->'val'    | {"number": 1}
 delete | ->'arr'->3 | {"number": 4}                                                                                   |            |
 update | ->'data'   | {"string": "foo"}                                                                               | ->'data'   | {"string": "bar"}
(8 rows)

jsonb_diff=#

差分情報の内容は以下のようになる。

種別 内容
1 update 文書トップ(パスが空白)の直下の差分を示す。
2 update ->'arr' パスで示される箇所の差分を示す。配列要素数の違いや、途中の要素の型の違いがある。
3 delete ->'col' パスで示される箇所は、left側には存在するが、right側には存在しない。
4 update ->'arr'->1 パスで示される箇所の差分を示す。左辺はstring型の2だが、右辺はnumber型の2である。
5 delete ->'name' パスで示される箇所は、left側には存在するが、right側には存在しない。
6 append ->'val' パスで示される箇所は、left側には存在しないが、right側には存在する。
7 delete ->'arr'->3 パス(0相対なのでarrの4番目の要素)で示される箇所は、left側には存在するが、right側には存在しない。
8 update ->'data' パスで示される箇所の差分を示す。この例では値(fooとbar)が異なっている。

TODO

  • pl/pgsqlで作っているので、性能面での懸念がある。(ただ、C言語等での作り直しはするつもりはない)
  • README.md の英語化
  • リグレッションテストの強化