概要
グラフデータを扱う機会があり、Gremlin
クエリ言語にて、グラフDBの操作を行なっております。私の主観ですが、使用頻度が高いと思われるGremlin
クエリ言語をまとめます。
環境
- グラフDBは、
Amazon Nputune
を使用 -
Gremlin
クエリは、Python
からApache TinkerPop
のGremlin-Python
ライブラリをインストールして操作 -
Jupyter notebook
を使用して、グラフデータの操作、可視化を行なっている -
Amazon SageMaker
を使用して、Jupyter notebook
の環境を立ち上げた
設定の確認
ステータスの確認
%status
# 出力内容
# 下記が表示されていれば、正常に動作している
'status': 'healthy'
接続しているDBの情報の確認
私の環境では、Amazon Nputuneに接続している為、Nputuneの接続情報が記載されております
%%bash
cat ~/graph_notebook_config.json
# 表示内容
{
"host": "db-neptune.cluster-************.リージョン.neptune.amazonaws.com",
"neptune_service": ************,
"port": port番号,
"proxy_host": "",
"proxy_port": port番号,
"auth_mode": "DEFAULT",
"load_from_s3_arn": "",
"ssl": true,
"ssl_verify": true,
"aws_region": リージョン,
"sparql": {
"path": "sparql"
},
"gremlin": {
"traversal_source": "g",
"username": "",
"password": "",
"message_serializer": ************
},
"neo4j": {
"username": ******,
"password": ******,
"auth": ****,
"database": ****
}
}
グラフDBを操作する為の設定
注意事項
-
Python
で記述する必要がある
ライブラリのインポート
-
Gremlin Server
に接続する為の必要なライブラリをインポート
from gremlin_python.structure.graph import Graph
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
Gremlin Server : グラフDBをネットワーク越しに操作する為のサーバ
コネクションの設定
-
DriverRemoteConnection
を使用して、Gremlin Server
への接続を行う -
DriverRemoteConnection
の第一引数に、エンドポイントとポート番号、第二引数にグラフのエイリアス(g)を指定する
connection = DriverRemoteConnection(
'wss://db-neptune.cluster-************.リージョン.neptune.amazonaws.com:8182/gremlin',
'g'
)
第二引数の補足
Gremlin Server
では、デフォルトのグラフエイリアスとしてgを使用しする。これにより、クライアントがgを指定する事で、デフォルトのグラフDBにアクセス出来る
グラフエイリアス : Gremlin Server
内で特定のグラフDBを参照する為の名前(別名)
デフォルトグラフDB : Gremlin Server
が起動時に自動で読み込むグラフDB
トラバーサル(探索)ソースの設定
- トラバーサルソースを設定する事で、リモートの
Gremlin Server
上のグラフDBに対してクエリを実行する事が可能 - 上記の設置により、リモートのグラフDBを検索・操作が可能となる
g = Graph().traversal().withRemote(connection)
トラバーサル : Gremlin
クエリを使ってグラフDB上で行う全ての操作(ノードやエッジの挿入、削除、更新、データ取得)の事を示す
接続を切る
- 使用後、接続をクローズしてリソースを解放する
connection.close()
書き方
from gremlin_python.structure.graph import Graph
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
# 構文
g = Graph().traversal().withRemote(
DriverRemoteConnection('wss://DBのエンドポイントを記述:port番号/gremlin','g')
)
# 入力例
connection = DriverRemoteConnection(
'wss://db-neptune.cluster-************.リージョン.neptune.amazonaws.com:port/gremlin','g'
)
g = Graph().traversal().withRemote(connection)
gremlin
クエリのみの書き方
- 冒頭に下記を定義する。それ以降は、
Gremlin
クエリでグラフDBの操作が可能となる -
%%gremlin
が冒頭に記載されている場合は、その記述は、Gremlin
クエリのみが記述されていると判断して良い
%%gremlin
S3に接続する方法
注意事項
- S3に接続する為には、IAMロールの修正が必要になる場合がある
ライブラリのインポート
import boto3
S3に接続する
s3 = boto3.client('s3')
obj = s3.get_object(
Bucket='バケット名',
Key='ファイルパス'
)
pandasのread_csvの使用方法
- データ解析を容易に行う為のデータ解析ライブラリ
- グラフDBを使用する際は、使う頻度が多いと思われる
ライブラリのインポート
import pandas as pd
CSVの取得方法
# ローカル環境にあるcsvを取得する場合は、下記の方法
filepath = 'test0.csv'
print(Path(filepath).read_text())
#0.0,1.1,2.2
#3.3,4.4,5.5
#6.6,7.7,8.8
df = pd.read_csv(filepath)
print(df)
#0.0,1.1,2.2
#3.3,4.4,5.5
#6.6,7.7,8.8
# s3からCSVを取得する場合は、read_csvの引数に、obj['Body']を入力する
# objの[]は、様々なデータと取得する事が可能。詳細は、下記のget_objectの詳細を参照
data = pd.read_csv(obj['Body'])
- [pandas 公式] What kind of data does pandas handle?
- pandasでCSVファイルの書き込み・追記(to_csv)
- [AWS 公式]get_objectの詳細
nest_asyncio
-
Jupyter Notebook
では、セルごとにコードを実行し、その結果をインタラクティブに表示する為、バックグラウンド(裏側)で処理が常に動いている -
Jupyter Notebook
環境では、asyncio
によって、イベントループが実行されている為、新たなイベントループ(今回の場合であれば、Gremlin
クエリーを叩く)を開始しようとするとエラーが発生する - このエラーを発生しない様にする 為、
nest_asyncio
を使用して、新しい非同期タスクを実行出来る様に様にする
イベントループ : 非同期タスクを管理、スケジュール、実行する為の仕組み
インタラクティブ : ユーザーとシステムが対話的にやり取りを行う事を意味する
ライブラリのインポート
import nest_asyncio
Gremlinクエリーを叩く前に実行しておく
nest_asyncio.apply()
データの追加
データ挿入の注意事項
ノードとエッジのデータ挿入をPython
で記述する場合、下記の記述をデータ挿入の前に書く必要がある。
from gremlin_python.structure.graph import Graph
from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
connection = DriverRemoteConnection(
'wss://DBのエンドポイントを記述:port番号/gremlin','g'
)
g = Graph().traversal().withRemote(connection)
#
# 以下省略
詳細は下記
PythonでグラフDBを操作する為の設定
ノードとエッジの追加
ノードで使うメソッド
addV()
: ノードにラベルを付与する(名前は任意で決めて良い)
エッジで使うメソッド
addE()
: エッジにラベルを付与する(名前は任意で決めて良い)
from()
: エッジの始点を定義する (Python
で書く場合は、from_()
で記述する)
to()
: エッジの終点を定義する
ノードとエッジ、両者で使うメソッド
property
:
-
key
とvalue
の形式で値を付与する -
property
は複数付与する事が可能
next()
:
- 新しい頂点やエッジを追加した後に、その結果を参照する場合に使用する
-
Python
にて、グラフDBの操作を行う場合、変数に保持して使用する機会が多い為、next()
を使う (必須ではない)
iterate()
:
- 新しい頂点やエッジを追加した後に、その結果を参照しない場合に使用する
as
: (Python
で書く場合は、as_()
で記述する)
- 新しい頂点やエッジを追加した後に、その結果に対してラベルを付与する
- Gremlinのトラバーサル内で、ラベルを使ってノードやエッジを参照する為、連続した操作がシンプルに記述できる
- Gremlinクエリのみで記述する場合に使う
(トラバーサル : Gremlin
クエリを使ってグラフDB上で行う全ての操作(ノードやエッジの挿入、削除、更新、データ取得)の事を示す)
pythonでの記述(参考例)
# ノードの追加
node1 = g.addV('Node').property('node_id', 1).property('name', 'test1').next()
node2 = g.addV('Node').property('node_id', 2).property('name', 'test2').next()
# エッジの追加
g.addE('edge').from_(node1).to(node2).iterate()
# プロパティー有りのエッジを追加
g.addE('edge').from_(node1).to(node2).property('edge_id',1).property('edge_name', 'edge_test1').iterate()
Gremlinクエリのみでの記述(参考例)
# ノードの追加
g.addV('Node').property('node_id', 1).property('node_name', 'node1').as('node1').
addV('Node').property('node_id', 2).property('node_name', 'node2').as('node2').
# プロパティー無しのエッジの追加
addE('edge').from('node1').to('node2').iterate()
# プロパティー有りのエッジを追加
addE('edge').from('node1').to('node2').
property('edge_id',1).property('edge_name', 'edge_test1').
iterate()
pythonで記述する場合の省略形の書き方
- Pythonでは、(.)で書くとメソッドチェーン(一連のメソッド呼び出し)になり、エラーとなる。その為、各行の末尾に( \ )を置く事で、処理の継続を示す事が出来る
- コードが複数行にわたって続いても、一つの連続した操作として扱われる
g.addV('Node1').property('node_name', 'node1').property('id', 1).as_('node1').\
addV('Node2').property('node_name', 'node2').property('id', 2).as_('node2').\
addV('Node3').property('node_name', 'node3').property('id', 3).as_('node3')
ノードとエッジの消去
-
drop()
の後にiterate()
を追加する事で、全てのノードとエッジの削除が行われる - ノードが削除されると、それに接続されているエッジも自動的に削除される
ノードの消去
g.V().drop().iterate()
エッジの消去
g.E().drop().iterate()
補足
-
drop()
のみでは、ノードとエッジの削除は実行されない - 削除対象(ノードとエッジ)がキューに追加された状態で保持される
g.V().drop()
g.E().drop()
グラフデータの出力、検索、フィルタリング
- Gremlinクエリを使用してデータを追加
- 下記に登録したデータを使用して説明を行う
%%gremlin
# データの初期化
g.V().drop().iterate()
g.
addV('Node').property('node_id', 1).property('node_name', 'node1').as('node1').
addV('Node').property('node_id', 2).property('node_name', 'node2').as('node2').
addV('Node').property('node_id', 3).property('node_name', 'node3').as('node3').
addE('Edge').from('node1').to('node2').property('edge_id', 1).property('edge_name', 'edge1').
addE('Edge').from('node1').to('node3').property('edge_id', 2).property('edge_name', 'edge2').
iterate()
全てのノードを出力
g.V()
: グラフ内のすべての頂点をオブジェクト形式で表示する
%%gremlin
g.V()
# 出力結果
v[8ac7fed5-f443-fd00-12cb-9793383bb568]
v[0ac7fed5-f44d-4b71-fc4d-c6d120650fb7]
v[aac7fed5-f451-550b-0177-f8fe98791bef]
g.V().valueMap()
: 各ノードのプロパティの値を出力する。プロパティは、key
とvalue
のペアで出力される
%%gremlin
g.V().valueMap()
# 出力結果
{'node_name': ['node1'], 'node_id': [1]}
{'node_name': ['node2'], 'node_id': [2]}
{'node_name': ['node3'], 'node_id': [3]}
※ プロパティーを定義していないとオブジェクトが表示される
全てのエッジを出力
g.E()
: グラフ内のすべてのエッジをオブジェクト形式で表示する
g.E()
# 出力結果
e[28c7fed5-f456-ecd8-a24e-e445b8e89770][8ac7fed5-f443-fd00-12cb-9793383bb568-edge->0ac7fed5-f44d-4b71-fc4d-c6d120650fb7]
e[68c7fed5-f45f-8bb0-49ca-d77a8bc12c6d][8ac7fed5-f443-fd00-12cb-9793383bb568-edge->aac7fed5-f451-550b-0177-f8fe98791bef]
g.E().valueMap()
: 各エッジのプロパティの値を出力する。プロパティはkey
とvalue
のペアで出力される
g.E().valueMap()
# 出力結果
{'edge_name': 'edge1', 'edge_id': 1}
{'edge_name': 'edge2', 'edge_id': 2}
※ プロパティーを定義していないとオブジェクトが表示される
指定したのノード or エッジを出力する
g.V().has
: 特定のラベルを持つ頂点 or エッジを検索する際に使用する
g.V().hasLabel
: 特定のプロパティを持つ頂点 or エッジを検索する際に使用する
(key
のみの検索 or key
とvalue
の両方を記述した検索が可能 )
# 構文(ノード)
g.V().hasLabel('lavel')
g.V().has('property')
# or
g.V().has('key', 'value')
# 構文(エッジ)
g.E().hasLabel('lavel')
g.E().has('property')
# or
g.V().has('key', 'value')
入力例
# ノード
g.V().has('Node')
g.V().hasLabel('node_name', 'node1')
# エッジ
g.E().has('Edge')
g.E().hasLabel('edge_name', 'edge1')
- 上記の記述は、 オブジェクト形式の値が出力される
-
valueMap()
を末尾に追加する事で、プロパティーの出力出来る
エッジに紐付いている始点のノードを出力する
-
inV()
単体で使用する場合は、g.E()
のみに使用可能 -
g.V()
に使用する場合は、outE()
と組み合わせる事で使用可能となる
# グラフ内のすべてのエッジを見て、そのエッジが向かう先のノード(ターゲットノード)を取得する
g.E().inV()
# グラフの中のすべてのノードから出発して、それぞれのノードから出ているエッジの先にあるノードを取得する
g.V(),outE().inV()
# g.V():グラフ内のすべてのノード(頂点)を選択する
# outE():選択された各ノードから出ているすべてのエッジを選択する
# inV():選択されたエッジが向かう先のノード(ターゲットノード)を取得する
その他
path()
- グラフDBの可視化
- クエリがどの様にグラフを通過したかを確認する際に使用する
g.V().path()
-
g.E()
には、使用不可 -
Jupyter Notebook
で、グラフを可視化する場合は、追加のライブラリ(例えば NetworkX や matplotlib)が必要 -
Amazon SageMaker
を使用して、Jupyter notebook
を起動した場合、グラフの可視化ツールはインストールされている為、何か新たにインストールをする必要はない
values()
- ノード or エッジのプロパティを表示
- 指定した、ノード or エッジのプロパティを表示する事も可能
g.V().values()
g.E().values()
# 指定した場合
g.V().values('test')
g.E().values('test')
by()
- 特定のプロパティを抽出したり、グラフDBのデータを必要な形式に整形する事が可能
-
by()
単体で使用する事は不可能 (例 :g.V().by('test')
) -
group()
,order()
,select()
等のメソッドと組み合わせて使う
by()
toList()
- グラフDBの結果をリスト形式で表示する
toList()
参考資料
まとめ
今回は、Gremlin
クエリの基本的な使い方について理解しました。また、分かり次第、追加して記事を更新して行きたいと思います。