# HG changeset patch # User Kaito Tokumori # Date 1410254627 -32400 # Node ID bf976aa9ebb739b147743d9d90103d5e61575b3c add report diff -r 000000000000 -r bf976aa9ebb7 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Tue Sep 09 18:23:47 2014 +0900 @@ -0,0 +1,25 @@ +TARGET=PG9_4Report + +LATEX=platex +BIBTEX=pbibtex +DVIPDF=dvipdfmx + +.SUFFIXES: .tex .div .pdf + +.tex.dvi: + $(LATEX) $< + $(LATEX) $< + $(LATEX) $< + +.dvi.pdf: + $(DVIPDF) $(DVIPDF_OPT) $< + +all: $(TARGET).pdf + open $(TARGET).pdf + +dvi: $(TARGET).div + +pdf: $(TARGET).pdf + +clean: + rm *.{log,aux,dvi,pdf,bbl,blg} diff -r 000000000000 -r bf976aa9ebb7 PG9_4Report.pdf Binary file PG9_4Report.pdf has changed diff -r 000000000000 -r bf976aa9ebb7 PG9_4Report.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PG9_4Report.tex Tue Sep 09 18:23:47 2014 +0900 @@ -0,0 +1,190 @@ +\documentclass{jsarticle} +\usepackage[dvipdfmx]{graphicx} +\usepackage{amsmath} +\usepackage{ascmac} +\usepackage{listings} +\lstset{ + language={C}, + basicstyle={\footnotesize\ttfamily}, + identifierstyle={\footnotesize}, + commentstyle={\footnotesize\itshape}, + keywordstyle={\footnotesize\bfseries}, + ndkeywordstyle={\footnotesize}, + stringstyle={\footnotesize\ttfamily}, + frame={tb}, + breaklines=true, + columns=[l]{fullflexible}, + numbers=none, + xrightmargin=0zw, + xleftmargin=3zw, + numberstyle={\scriptsize}, + stepnumber=1, + numbersep=1zw, + lineskip=-0.5ex, + keepspaces=true +} +\def\figcaption{\def\@captype{figure}\caption} +\begin{document} +\title{Postgres Unstructured 調査報告書} +\author{琉球大学理工学研究科 徳森海斗} +\maketitle + +\section{Postgres Unstructured 概要} +Postgres Unstructured は EnterpriseDB 社による プレゼン\cite{PostgresUnstructured}タイトル内の言葉で、NoSQL に対応した PostgreSQL を指す。 Postgres 9.4 beta2 時点では HStore, json, jsonb という三つのデータ型が非構造なデータを扱うことを可能にしている。本報告書では Postgres 9.4 で新たに加わった jsonb 型、9.3 からの json 型について中心に述べる。 + +\section{Postgres 9.4 beta2 環境設定} +EnterpriseDB 社のページにインストーラが用意されているのでそれを利用する\cite{Install}。Mac OS X, Windows, Linux 向けにそれぞれ app, exe, run ファイルが用意されており、実行するだけでインストールが完了する。インストーラにより、データベース用ディレクトリの作成、Postgresユーザの追加までサポートされる。ただしパスの通ってないディレクトリをインストール先に指定した場合はパスを通す必要がある。 + +インストール後, createdbコマンドでデータベースを作成、psql [db name]でデータベースを操作できる。デフォルトではデータベースのユーザーには postgres しか存在しないので、ユーザーが postgres 出ない場合は -U オプションで postgres ユーザーを指定する必要がある。 + +サーバプロセスの管理は pg\_ctl コマンドによって行う。こちらの操作も postgres ユーザーでしか行えない。以下のように使用する。 +\begin{lstlisting}[frame=lrbt] +% pg_ctl -D [database path] [start | stop | status | restart] +\end{lstlisting} +portやlisten address の設定はデータベースに指定したディレクトリ以下の data/postgresql.conf を編集することで行える。 +それぞれport=****,listen\_addresses=****を任意の値に変更する。 +この設定は再起動するまで反映されない。 + +\section{json, jsonb型} +jsonb型は postgres の持つ json のバイナリ表現である。 mongoDB の BSON とは異なり, 64bit 以上の数値を正確に表現することが可能という利点を持つ. jsonb は json の内部表現というわけではなく, json型とjsonb型はそれぞれ独立して存在する. jsonb型を利用する際、入力として与える値は json であるので、型を指定する以外は特別 jsonb型であることを意識する必要はない。jsonb型を格納する際、与えられた json を jsonb へと変換する処理が入るので 値の格納に関しては json よりやや速度が劣る。しかし、jsonbのみが利用できる演算子の存在、値の処理はjsonbの方が速いという利点が存在する。 + +その他の違いとして、json型は文字列をそのまま格納するのに対しjsonb型は一度変換を介すので, jsonb型は整形されるという点がある。そのため、1.230e-5 のように小数の表現に e を使ったものも 0.0000123 と直される。 + +また、json,jsonb型に共通して、数値に NaN, infinity を使用することができない。これらの値をもつ json は別の値に変換する必要がある。 + +\section{json, jsonbとデーブルデータ間の変換} +テーブルデータをjsonに変換するのには to\_json関数を用いる。この関数はテーブルの項目, 値をそれぞれキー, 要素とするjsonに変換する関数ある。以下に使用例と実行結果を示す。 +\begin{lstlisting}[frame=lrbt] +=# SELECT to_json(table_name.*) FROM table_name; + to_json +------------------------------------------------------------------------------ + {``city'':''okinawa'',''temp_lo'':20,''temp_hi'':30,''prcp'':0.42,''date'':''2014-08-30''} + {``city'':''okinawa'',''temp_lo'':20,''temp_hi'':30,''prcp'':0.42,''date'':null} + {``city'':''naha'',''temp_lo'':20,''temp_hi'':30,''prcp'':0.42,''date'':null} + +\end{lstlisting} + +jsonをテーブルデータに変換するにはjson\_populate\_record関数,json\_populate\_recordset関数を用いる。この関数は引数としてベースとなるテーブルとjsonを求め, ベースとなるテーブルに与えられた json を対応させていく。このときベースに存在しない項目は無視され、jsonに存在しない項目にはnullが格納される。以下の使用例と実行結果を示す。 +\begin{lstlisting}[frame=lrbt] +=# SELECT * FROM json_populate_recordset(NULL::table_name, (SELECT array_to_json(array_agg(json.data)) FROM json_table_name)); + city | temp_lo | temp_hi | prcp | date +---------+---------+---------+------+------------ + okinawa | 20 | 30 | 0.42 | 2014-08-30 + okinawa | 20 | 30 | 0.42 | + naha | 20 | 30 | 0.42 | +\end{lstlisting} + +\section{json, jsonb型のフィールドへのアクセス} +json, jsonのフィールドへのアクセスには'\verb|->|','\verb|->>|'演算子を用いる。前者はフィールドを単に取得するもので、後者はテキストとして取得する。フィールドの値を SELECT に用いるのは容易で、以下のように使用する。 +\begin{lstlisting}[frame=lrbt] +=# SELECT json_data FROM json_jsonb; + json_data +------------------------------------------------------------------------------------- + {``string'':''Unstructuerd Postgres'', ``number'':1.230e-5, ``boolean'': true, ``null'':null} + {``string'':''NULL CHECK'', ``number'':1.230e-5, ``boolean'': true, ``null'':null} + {``number'':2.345e+5,''boolean'':false,''null'':null,''string'':''aua''} +=# SELECT json_data->'string' FROM json_jsonb; + ?column? +------------------------- + ``Unstructuerd Postgres'' + ``NULL CHECK'' + ``aua'' +\end{lstlisting} + +WHEREの条件に利用する場合は型に気をつける必要があり, フィールドの型と比較先の値の型を揃える必要がある。例えばフィールド内の数値と 100 を比較したい場合、フィールドの値は'\verb|->>|'演算子を用いてテキストとして取得し、右辺の 100 はシングルクォートで囲い文字列扱いにする必要がある。'\verb|->|'演算子で取得した値は json 型になるので比較できない。 + +\begin{lstlisting}[frame=lrbt, caption={エラーが出る例}] +=# SELECT json_data->'string' FROM json_jsonb WHERE jsonb_data->>'number' = 234500;; +ERROR: operator does not exist: text = integer +\end{lstlisting} +\begin{lstlisting}[frame=lrbt, caption={正しい例}] +ELECT json_data->'string' FROM json_jsonb WHERE jsonb_data->>'number' = '234500'; + ?column? +------------------------- + ``Unstructuerd Postgres'' +\end{lstlisting} + + + +\section{json, jsonb型のデータの更新} +json全体の UPDATE は通常の値を更新するのと同様に UPDATE を用いて行うことができる。しかし、以下のように'\verb|->|','\verb|->>|'演算子を利用してフィールドの値を直接更新することはできず、Postgres 公式ドキュメント\cite{PostgresDoc}にもその方法は記載されていない。 +\begin{lstlisting}[frame=lrbt, caption={エラーが出る例}] +=# UPDATE table_name SET json->'a' = to_json(5) WHERE json->>'b' = '2'; +ERROR: syntax error at or near ``->'' +\end{lstlisting} + +以下に示すリスト\ref{json_update}のような関数を利用することで更新は可能であるが、この場合、複数のjson内のフィールドの値を更新することはできない。(関数は stackoverflowより引用\cite{stackoverflow})。 +\begin{lstlisting}[frame=lrbt,caption={json\_object\_set\_key}, label=json_update] +CREATE OR REPLACE FUNCTION ``json_object_set_key''( + ``json'' json, + ``key_to_set'' TEXT, + ``value_to_set'' anyelement +) + RETURNS json + LANGUAGE sql + IMMUTABLE + STRICT +AS $function$ +SELECT COALESCE( + (SELECT ('{' || string_agg(to_json(``key'') || ':' || ``value'', ',') || '}') + FROM (SELECT * + FROM json_each(``json'') + WHERE ``key'' <> ``key_to_set'' + UNION ALL + SELECT ``key_to_set'', to_json(``value_to_set'')) AS ``fields''), + '{}' +)::json +$function$; +\end{lstlisting} +この関数は次のように利用する。 +\begin{lstlisting}[frame=lrbt] +UPDATE json_jsonb SET SELECT json_object_set_key((SELECT json_data FROM json_jsonb WHERE json_data->>'condition_key'='condition_value'),'key_name','new_value'::text)) WHERE json_data->>'condition_key'='condition_value'; +\end{lstlisting} + + +\section{json,jsonb型でのINDEX} +json, jsonb型どちらでもフィールドに対する index の作成が可能で、作成のためのCREATE INDEX 文は次の二通りの記述方法がある。 +\begin{lstlisting}[frame=lrbt] +CREATE INDEX index_name ON table_name ((json_column_name->>'key')); +CREATE INDEX index_name ON table_name ((json_extract_path_text(json_column_name, 'key'))); +\end{lstlisting} + +json型の場合, jsonデータへのインデックスは作成できない。jsonb型へはインデックスの作成が可能で、\verb-@>, ?, ?&, ?|- の4つの演算子がサポートされる。 +\begin{lstlisting}[frame=lrbt] +CREATE INDEX index_name ON table_name USING GIN (jsonb_column_name); +\end{lstlisting} +また、以下のようにすると \verb|@>|のみをサポートする index を作ることができる。 +\begin{lstlisting}[frame=lrbt] +CREATE INDEX index_name ON table_name USING GIN (jsonb_column_name jsonb_path_ops); +\end{lstlisting} + +尚、各演算子の意味は次のようになっている。 +\begin{table}[htb] + \centering + \begin{tabular}{|c|c|}\hline + 演算子 & 説明 \\ \hline\hline + \verb-@>- & 指定した json を含むかどうか。\\ \hline + \verb-?- & 指定したキーを持つかどうか。\\ \hline + \verb-?&- & 指定したキーをすべて持つかどうか。\\ \hline + \verb-?|- & 指定したキーのうちどれか一つでも持つか。\\ \hline + \end{tabular} +\end{table} + +\section{まとめ} +Postgres は 9.4 beta2 時点で HStore, json, jsonb の三種の方により Unstructuerd data に対応している。既存のテーブルデータと json, jsonb 間の変換が容易なので、jsonで受け取ったデータを RDB で管理するといった使い方もできる。 + +json, jsonb型を用いて NoSQLDB として使う場合はフィールドの値を簡単に更新できないことが課題になるだろう。先に作られた HStore 型の方はフィールドの値の更新に対応しているので、HStore 型で表現できるのならば HStore 型を用いるのが良い。 + +また、'\verb+?+','\verb+?|+','\verb+?&+'の三つの演算子はドキュメントでは要素の検索も可能となっているが実際にはキーの検索しかできず、ドキュメント側のミスなのか開発段階なのかの判断ができない。 + +同様に json を扱う mongoDB と比較した場合、 EnterpriseDB 社の発表\cite{PostgresUnstructured}によると処理速度、データベースサイズ等のパフォーマンス面では Postgres が勝るようだが、前述したjsonのフィールドの更新が未対応である点に使いづらさを感じた。 + +この点を考えると、json を扱うためのデータベースとして利用するにはまだ早いと感じた。NoSQL と SQL の両方が利用できるという強みを活かせる使い方をするのがいいだろう。 + +\begin{thebibliography}{9} +\bibitem{PostgresUnstructured}NoSQL on ACID - Meet Unstructured Postgres. \\ ``http://www.slideshare.net/EnterpriseDB/no-37327319'' +\bibitem{Install}Download PostgreSQL. \\ ``http://www.enterprisedb.com/products-services-training/pgdownload'' +\bibitem{PostgresDoc}PostgreSQL 9.4beta2 Documentation. \\ ``http://www.postgresql.org/docs/9.4/static/'' +\bibitem{stackoverflow}stackoverflow - How do I modify fields inside the new PostgreSQL JSON datatype?. \\ ``http://stackoverflow.com/questions/18209625/how-do-i-modify-fields-inside-the-new-postgresql-json-datatype'' +\end{thebibliography} +\end{document}