GitHub
https://github.com/edgedb/edgedb
雑な意訳が含まれているのでより詳しい説明をみたい方は公式ドキュメントを確認してください。
データベースって何?というとこはすっ飛ばして説明します。
概要
この章はEdgeDB: A New Beginningの意訳と概要です
現行のデータベースをプロダクトに組み込む際にはリレーショナルデータベースもしくはNoSQLを使用することが多いです。
後者のNoSQL、例えばMongoDBはスキーマレスを活かした柔軟な設計により素早い開発を行える反面、時間の経過と共に技術的な負債を抱えることが多くなります。
対してリレーショナルデータベースは使用予定のリソースや強力な構文(訳注:これは標準SQL構文を指しているの各データーベスの独自記法を指しているのかは不明)一貫性の保証などが働きアプリケーションとして堅牢な作りとなり市場を独占する主な理由となります。
しかしリレーショナルデータベースは数十年前の設計志向にもどいたプロダクションであり、現在のような急激に変化する技術要件に対して追いついてこれていないという現実があります。
PostgreSQLはJSON対応やクエリの並列化など改善を行いつつも基幹となる手法は全く変わっておらず我々は未だ遅いORMに頼ることになり、スキーマの移植に消耗しています。
殆どの開発者はテーブルについては特に意識せずコーディングを考えます。例えばオブジェクトA,Bが存在するとして、これらをお互いが参照したい場合は
A.b = B
と書くだけです。リレーショナルデータベースでこれを行おうとする場合は外部キー制約、JOIN、中間テーブルなど様々なこを考えなくてはいけません
(訳注:標準SQL構文の場合ぱっと思いつく限りは以下のクエリのような発行が必要です)
FROM *
SELECT A
JOIN B ON A.id = B.id
リレーショナルデータベースと現代のプログラミング言語にはこの大きな壁・矛盾が発生しそれらを object-relational impedance mismatch. と呼ばれています。
この現象が現代のORMが人気の理由で、GraphQLといった技術に関して話題になる理由となっています。
EdgeDBは次世代の object-relational database(オブジェクトリレーショナルデータベース)です。
リレーショナルモデルの代わりにobject graph modelを実装し、このmodelではデータは強い型指定を受けオブジェクトの関係・それらの間のリンクを記述として格納されます。オブジェクトとリンクはプロパティを保持できます。これは名前付きスカラー値のセットになります。
EdgeDBは graph databaseでもdocument databaseでもありませんが、リレーショナルデータベースのようなスキーマの挿入参照を行え、階層的なdocumentの挿入変更参照も容易に行なえます。
サブクエリ、高度な集計、window関数などの最新のSQL機能に匹敵する構文を持ち、それらを上回ることを目標としたEdgeQLという表現豊かな構文を備えています。
EdgeDBではPostgreSQLをベースとしたreliability, ACID compliance, そして performanceという長所を継承しております。
インストール
上記ページから使用しているOSを選択して手順に則って試してください。
注意点としてDockerを使用する際公式の手順に則った場合コケる場合があります。
$ docker run -it --rm -p 5656:5656 \
-v <datadir>:/var/lib/edgedb/data \
edgedb/edgedb
自分の場合以下のようなエラーが帰ってきました
$docker run -it --rm -p 5656:5656 -v data:/var/lib/edgedb/data edgedb/edgedb
rm: cannot remove '/var/run/edgedb/*': No such file or directory
コードを見る限りdocker-entrypoint.sh
の記述に誤りがあるのでもしエラーが起きた場合はDockerfileをcloneし該当箇所を変更してビルドし直してください。
対象箇所は現在PRを提出してレビュー待ちという状態です。
Fix remove directory return error by yoshiken · Pull Request #1 · edgedb/edgedb-docker
(注意)
シンタックスハイライトの.........
の部分は入力待機時に表示されるやつなので無視してください
チュートリアル
兎に角にも記述してみないと何もわからなんという感じだとは思うのでやっていきましょう。
環境構築
dockerの起動が終わったら別ターミナルで立ち上がったコンテナにexecでログインし、以下のコマンドを実行しましょう
$root@7c1f7d5c6def:/# edgedb --admin alter role edgedb --password
Password:
Repeat for confirmation:
パスワードが聞かれるのでお好きなパスワードを設定してください。
設定が終わったらedgedbにログインします
root@7c1f7d5c6def:/# edgedb -u edgedb
EdgeDB 1.0.alpha.1
Type "\?" for help.
edgedb>
これで対話式のコンソールに入ることができました。
早速データベースを作成します
edgedb> CREATE DATABASE tutorial;
CREATE
edgedb> \c tutorial
tutorial>
これはもう見慣れたもののはずなので細かいところは省略します
次にスキーマを設定するのですが記法が2つあり、SDLとDDLというに種類の記法が使用できます。
SDL
EdgeDB schema definition language もしくはSDLと呼ばれているものです。
SDLは宣言型高水準言語となっており、人間が読みやすいように最適化されています。
SDLでのmygrationは以下の構文となります
tutorial> START TRANSACTION;
START TRANSACTION
tutorial> CREATE MIGRATION movies TO {
......... type Movie {
......... required property title -> str;
......... # the year of release
......... property year -> int64;
......... required link director -> Person;
......... multi link cast -> Person;
......... }
......... type Person {
......... required property first_name -> str;
......... required property last_name -> str;
......... }
......... };
CREATE MIGRATION
tutorial> COMMIT MIGRATION movies;
COMMIT MIGRATION
tutorial> COMMIT;
COMMIT TRANSACTION
DDL
Data definition languageもしくはDDLと呼ばれるものです。
命令形低水準に値するものです。SDLと比べてスキーマ変更のため低レベルでより確実な実行を行いたい場合はこちらを使用するべきです。
DDLでの構文は以下のようになります。
tutorial> CREATE TYPE Person {
......... CREATE REQUIRED PROPERTY first_name -> str;
......... CREATE REQUIRED PROPERTY last_name -> str;
......... };
CREATE
tutorial> CREATE TYPE Movie {
......... CREATE REQUIRED PROPERTY title -> str;
......... # the year of release
......... CREATE PROPERTY year -> int64;
......... CREATE REQUIRED LINK director -> Person;
......... CREATE MULTI LINK cast -> Person;
......... };
CREATE
現状確認
上記のどちらかを実行し、スキーマを定義したところで実際にデータを挿入してみましょう。
現在の状態はpostgreSQLの構文に例えると以下のようなテーブル構成となっています。
(directorは複数いるケースがあるんじゃねみたいなお気持ちはそっと閉まっておきましょう)
postgres=# CREATE DATABASE tutorial;
CREATE DATABASE
postgres=# \c tutorial
You are now connected to database "tutorial" as user "postgres".
tutorial=# CREATE TABLE Person (
person_id SERIAL PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL
);
CREATE TABLE
tutorial=# CREATE TABLE PerformerBundle(
bundle_id SERIAL PRIMARY KEY,
movie_id INTEGER NOT NULL,
performer_id INTEGER NOT NULL REFERENCES Person(person_id)
);
CREATE TABLE
tutorial=# CREATE TABLE Movie (
movie_id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
year INTEGER,
director INTEGER NOT NULL REFERENCES Person(person_id),
performer INTEGER NOT NULL REFERENCES PerformerBundle(bundle_id)
);
CREATE TABLE
tutorial=# ALTER TABLE PerformerBundle ADD FOREIGN KEY (movie_id) REFERENCES Movie(movie_id);
ALTER TABLE
どちらかというとリレーショナルデータベースよりNoSQLのほうが例としてわかりやすいかもしれませんがサッと手元で試しやすかったpostgresqlを使用しています
EdgeQL
ここからは駆け足で進めます。
INSERT
映画(Movie)と出演者(Person)を同時にINSERTを行えます
tutorial> INSERT Movie {
......... title := 'Blade Runner 2049',
......... year := 2017,
......... director := (
......... INSERT Person {
......... first_name := 'Denis',
......... last_name := 'Villeneuve',
......... }
......... ),
......... cast := {
......... (INSERT Person {
......... first_name := 'Harrison',
......... last_name := 'Ford',
......... }),
......... (INSERT Person {
......... first_name := 'Ryan',
......... last_name := 'Gosling',
......... }),
......... (INSERT Person {
......... first_name := 'Ana',
......... last_name := 'de Armas',
......... }),
......... }
......... };
{Object { id: <uuid>'a8218f8c-655b-11e9-8815-2b3c434e8fbf' }}
tutorial> SELECT Movie {
......... title,
......... year,
......... director: {
......... first_name,
......... last_name
......... },
......... cast: {
......... first_name,
......... last_name
......... }
......... };
{
Object {
title: 'Blade Runner 2049',
year: 2017,
director: Object { first_name: 'Denis', last_name: 'Villeneuve' },
cast: {
Object { first_name: 'Harrison', last_name: 'Ford' },
Object { first_name: 'Ryan', last_name: 'Gosling' },
Object { first_name: 'Ana', last_name: 'de Armas' }
}
}
}
すでにPerson内に存在している人物を使用する場合はサブクエリを使用することで重複せずに使いまわすことができます。
tutorial> INSERT Movie {
......... title := 'Dune',
......... director := (
......... SELECT Person
......... FILTER
......... # the last name is sufficient
......... # to identify the right person
......... .last_name = 'Villeneuve'
......... # the LIMIT is needed to satisfy the single
......... # link requirement validation
......... LIMIT 1
......... )
......... };
{Object { id: <uuid>'6ddd34a4-6563-11e9-8815-7fc823af2d49' }}
tutorial> SELECT Movie {
......... title,
......... year,
......... director: {
......... first_name,
......... last_name
......... },
......... cast: {
......... first_name,
......... last_name
......... }
......... };
{
Object {
title: 'Blade Runner 2049',
year: 2017,
director: Object { first_name: 'Denis', last_name: 'Villeneuve' },
cast: {
Object { first_name: 'Harrison', last_name: 'Ford' },
Object { first_name: 'Ryan', last_name: 'Gosling' },
Object { first_name: 'Ana', last_name: 'de Armas' }
}
},
Object {
title: 'Dune',
year: {},
director: Object { first_name: 'Denis', last_name: 'Villeneuve' },
cast: {}
}
}
SELECT
先程も少し出ましたが、SELECT文を用いて内容を見ることが可能です
tutorial> SELECT Movie;
{
Object { id: <uuid>'a8218f8c-655b-11e9-8815-2b3c434e8fbf' },
Object { id: <uuid>'6ddd34a4-6563-11e9-8815-7fc823af2d49' }
}
tutorial> SELECT Movie {
......... title,
......... year
......... };
{Object { title: 'Blade Runner 2049', year: 2017 }, Object { title: 'Dune', year: {} }}
条件(WHERE句)で絞る際には FILTER
を使用します
tutorial> SELECT Movie {
......... title,
......... year
......... }
......... FILTER .title ILIKE 'blade runner%';
{Object { title: 'Blade Runner 2049', year: 2017 }}
ALTER
属性の変更などを加えたい場合はALTER句を使用します
現在 Person
では first_name
が REQUIRED
になっていますが諸々の事情で無くても問題ない。といった場合は REQUIRED
属性を外します
tutorial> ALTER TYPE Person {
......... ALTER PROPERTY first_name {
......... DROP REQUIRED;
......... }
......... };
ALTER
tutorial> INSERT Person {
......... last_name := 'Zendaya'
......... };
{Object { id: <uuid>'a6ebef0a-6564-11e9-8815-0f556b5186ca' }}
tutorial> SELECT Person {
......... first_name,
......... last_name
......... };
{
Object { first_name: 'Denis', last_name: 'Villeneuve' },
Object { first_name: 'Harrison', last_name: 'Ford' },
Object { first_name: 'Ryan', last_name: 'Gosling' },
Object { first_name: 'Ana', last_name: 'de Armas' },
Object { first_name: {}, last_name: 'Zendaya' }
}
tutorial>
GraphQL
すみませんGraphQLはまだ詳しく理解していないので曖昧になってしまうかもです。
edgeDBではネイティブでGraphQLを対応しており、テスト検証環境も自前で持っています。
これでサードパーティ製のややこしいORMからおさらばできます。
まずedgeDBでGraphQLを使用する場合は専用のportの穴あけが必要なので一旦dockerを落とすなり別途portを開けてあげるなりして 88888
を開けます。
↓一旦落として開けてあげる人
docker run -it --rm -p 5656:5656 -p 8888:8888 -v data:/var/lib/edgedb/data edgedb:1.0
次にCONFIGでGraphQLをしようする設定を記述します
tutorial> CONFIGURE SYSTEM INSERT Port {
......... protocol := "graphql+http",
......... database := "tutorial",
......... address := "0.0.0.0",
......... port := 8888,
......... user := "http",
......... concurrency := 4,
......... };
CONFIGURE SYSTEM
docker以外のホストで起動している方はaddressのところを 127.0.0.1
で記述してください
つぎに http://127.0.0.1:8888/explore を開きます
こちらのページはさくっとGraphQLを調査したり試したりすることができるインターフェースです。
試しにMovieの情報を取得してみます
{
Movie {
title
year
}
}
{
"data": {
"Movie": [
{
"title": "Blade Runner 2049",
"year": 2017
},
{
"title": "Dune",
"year": null
}
]
}
}
おわり
まだまだalpha版ということで足りない機能やバグも多いですが 珍しくPythonにしては早い ということもありかなり今後の進化に期待できると思っています。
ここでは紹介しなかったJSON Support・Type Safety・Binary Protocol…色々機能も充実してきています。
個人的には既存のDBでは色々お膳立てしないといけないのでなんだかんだ触っていなかったGraphQLのネイティブ対応がアツいので今後もGraphQLの勉強に使っていこうかなと思っています。
設計思想などは開発者ブログに書いてあるのでぜひ読んでみてください
https://edgedb.com/blog/edgedb-a-new-beginning