1
0

Gremlinクエリ言語を使う為の設定とGremlinクエリ言語の使い方をまとめた

Last updated at Posted at 2024-06-10

概要

グラフデータを扱う機会があり、Gremlinクエリ言語にて、グラフDBの操作を行なっております。私の主観ですが、使用頻度が高いと思われるGremlinクエリ言語をまとめます。

環境

  • グラフDBは、Amazon Nputuneを使用
  • Gremlinクエリは、PythonからApache TinkerPopGremlin-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'])

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 :

  • keyvalueの形式で値を付与する
  • 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() : 各ノードのプロパティの値を出力する。プロパティは、keyvalueのペアで出力される

%%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() : 各エッジのプロパティの値を出力する。プロパティはkeyvalueのペアで出力される

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 keyvalueの両方を記述した検索が可能 )

# 構文(ノード)
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クエリの基本的な使い方について理解しました。また、分かり次第、追加して記事を更新して行きたいと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0