入門 - Gremlin Consoleのクイック・スタート

データを挿入して探索を実行します。

グラフ・データベースは、オブジェクト間の簡素な関係や複雑な関係を検出するために便利です。オブジェクトが他のオブジェクトと、およびその環境とどのように相互に作用するのかは、関係に左右されます。グラフ・データベースは、オブジェクト間の関係を完璧に表します。

グラフ・データベースは次の2つの要素で構成されます。
頂点
頂点は、人、場所、自動車、レシピなどのオブジェクトで、名詞と考えられるあらゆるものがあります。
辺は、2つの頂点間の関係を定義します。人はソフトウェアを作成し、著者は本を記述します。辺を定義するのは動詞と考えてください。
頂点と辺はどちらもプロパティを持つことができます。このため、DSE Graphは、プロパティ・グラフとして分類されます。頂点と辺の両方のプロパティは、プロパティ・グラフの情報を格納したり、クエリーを実行したりするための重要な要素です。

プロパティ・グラフは一般的に大規模なグラフですが、プロパティ・グラフに対するクエリーの性質は、グラフに多数の頂点があるのか、辺があるのか、頂点と辺の両方があるのかによって異なります。手始めに、グラフ・データベースの概念を把握するために、わかりやすいようにサンプル・グラフを例にとってみましょう。ここでは、食べ物の分野を探索する例を使用します。

図: レシピ・サンプル・グラフ

グラフ・データベース内の頂点と辺のタイプを区別するために、要素にはラベルが付いています。著者についての情報を保持する頂点にはauthor(著者)というラベルが付けられています。グラフ内の辺にはauthored(著作)というラベルが付けられています。ラベルは、グラフを構成する頂点と辺のタイプを指定します。適切なラベルを指定することは、グラフ・データ・モデリングにおける重要なステップです。

頂点と辺は通常、プロパティを持っています。たとえば、author(著者)頂点は、name(名前)というプロパティを持つことができます。Gender(性別)とcurrent job(現在のジョブ)は、author頂点のその他のプロパティの例です。辺もプロパティを持ちます。created(創作)辺は、隣接するrecipe(レシピ)頂点が作成された年を識別するtimestamp(タイムスタンプ)プロパティを持つことができます。

グラフ・データベース内の情報は、グラフ探索を使用して取得できます。グラフ探索では、1つまたは一連の探索ステップによってグラフを「巡回」します。このステップでは、探索の開始点を定義して結果をフィルターし、グラフ・データに関するクエリーに対する回答を得ることができます。

情報の取得のためにグラフ探索を実行するためには、まずデータが挿入されている必要があります。以下のセクションに記載する手順に従うと、最小限の構成とスキーマ作成でDSE Graphの基本知識を身に付けることができます。

手順

  1. DSEをインストールします
  2. DSE Graphを起動します
  3. Gremlin Consoleを起動します。
    bin/dse gremlin-console
             \,,,/
    (o o)
    -----oOOo-(3)-oOOo-----plugin activated:tinkerpop.tinkergraph
    plugin activated:tinkerpop.server
    plugin activated:tinkerpop.utilities
    ==>Connected - localhost/127.0.0.1:8182-[4edf75f9-ed27-4add-a350-172abe37f701]
    ==>Set remote timeout to 2147483647ms
    ==>All scripts will now be sent to Gremlin Server - [localhost/127.0.0.1:8182]-[4edf75f9-ed27-4add-a350-172abe37f701] - type ':remote console' to return to local mode
    gremlin>

    Gremlin Consoleでは、プロンプトで入力されたすべてのコマンドがGremlin Serverに送信され、そこで処理されます。DSE Graphは、各DSEノード上でGremlin Server tinkerpop.serverを実行します。Gremlin Consoleは、Gremlin Serverに自動的に接続します。Cassandraキースペースごとに1つのグラフ・インスタンスとして格納されるグラフを作成する必要があります。

    Gremlin Consoleは自動的にリモートモードで実行され、Gremlin Server上でコマンドを処理します。デフォルトでは、Gremlin Consoleはリモート・サーバーでコマンドを実行するセッションを開きます。Gremlin Consoleは、次のコマンドを使用してコマンドをローカルに実行するように切り替えることができます。
    :remote console
    このコマンドの実行後は、すべてのコマンドをリモートで送信する必要があります。このコマンドをもう一度使用すると、コンテキストがGremlin Serverに戻ります。
  4. データを格納するグラフを作成します。DSE Graphのグラフに影響するコマンドの実行には、システム・コマンドを使用します。
    gremlin> system.graph('test').create()
    ==>null

    グラフが作成されたら、グラフ探索を実行できるように、グラフ探索gを構成します。グラフ探索は、グラフ・データに対するクエリーを実行し、結果を返します。グラフ探索は、標準OLTP探索エンジンである特定の探索ソースに結合します。

  5. グラフ探索のデフォルト設定test.gを使用するようにグラフ探索gを構成します。このステップでは、暗黙的なgraphオブジェクトも作成されます。
    gremlin> :remote config alias g test.g
    ==>g=test.g

    graphコマンドでは、通常、データベースへの頂点や辺の追加、または他のグラフ情報の取得を行います。gコマンドでは、一般にクエリー実行によって結果を取得します。

  6. 最初に、スキーマ・モードをDevelopment(開発)に設定します。Developmentは、テスト中にいつでもスキーマを追加できる緩やかなモードです。実稼働については、動作異常の原因となるような対話型のスキーマ変更を回避するために、スキーマ・モードをProduction(実稼働)に設定してください。
    schema.config().option('graph.schema_mode').set('Development')
  7. 探索ステップcount()を使用して、グラフ内に存在する頂点の数を確認します。現時点ではまだデータを追加していないため、頂点の数はゼロです。すべての頂点を取得するにはグラフ探索gをV()に連結し、頂点の数を取得するにはcount()に連結します。
    gremlin> g.V().count()
    ==>0
    注:

    注:g.V().count()を使用して完全グラフスキャンを行うクエリーは、大規模グラフに対しては実行しないでください。複数のDSEノードが構成されている場合、この探索ステップは、集中的に、クラスター内のすべてのノードのすべてのパーティションに対して巡回を行います。

簡単な例は2つの頂点で構成されます。1つはauthor(Julia Child)、もう1つはbook(The Art of French Cooking, Vol. 1)です。これらの頂点の間には辺があり、Julia Childがその本を書いたことを表しています。スキーマを一切作成せずに、これら3つの要素を下記のように作成できます。ただし、DSE Graphは、後述するように、最も妥当なスキーマを推定します。

  1. まず、Julia Childの頂点を作成しましょう。頂点ラベルはauthorで、nameとgenderについてはプロパティ・キーと値の2つのペアを作成します。頂点ラベルを設定するキーと値のペアのキーを指定するためのラベルの使用に注意してください。次のコマンドを実行し、Raw(原型)、Table(テーブル)、およびGraph(グラフ)の各ビューを表示するボタンを使用して結果を確認します。
    gremlin> juliaChild = graph.addVertex(label,'author', 'name','Julia Child', 'gender','F')
    ==>v[{~label=author, member_id=0, community_id=1080937600}]
    各ビューには同じ情報が表示されます。
    • member_id、community_idおよびlabelで構成される自動生成のid
      • member_idとcommunity_idは、グラフ内で頂点をグループ化するために使用します(詳細
    次のコマンドで示すように、プロパティ・キーは、さまざまなタイプの情報に再利用できます。プロパティは、複数の頂点ラベルで使用されるという意味で「グローバル」ですが、グラフ探索でプロパティを指定する場合、それは常に頂点ラベルとともに使用されるということを理解しておくことが重要です。

    次のコマンドを実行してbook(本)頂点を作成します。コマンドは、どれであっても再実行しないでください。再実行すると、グラフ内に頂点が重複して作成されてしまいます。

  2. グラフ内にbook頂点を作成します。
    gremlin> artOfFrenchCookingVolOne = graph.addVertex(label, 'book','name', 'The Art of French Cooking, Vol. 1', 'year', 1961)
    ==>v[{~label=book, member_id=1, community_id=1080937600}]

    author頂点と同様に、作成したbook頂点に関するID情報を表示できます。

    次の2つのコマンドを実行します。最初のコマンドは、author頂点とbook頂点間の辺を作成します。2番目のコマンドは、valueMap()を使用して2つの頂点を取得するグラフ探索です。author頂点プロパティ・キー情報を確認するには、valueMap()を使用します。探索gは、探索ステップV()ですべての頂点を確認し、探索ステップvalueMap()を使用して、頂点ごとのプロパティ値のキーと値のリストを出力します。

  3. 辺を作成し、頂点データを表示します。
    gremlin> juliaChild.addEdge('authored', artOfFrenchCookingVolOne)
    gremlin> g.V().valueMap()
    gremlin> juliaChild.addEdge('authored', artOfFrenchCookingVolOne)
    ==>e[{out_vertex={~label=author, member_id=0, community_id=1080937600}, local_id=6bd73210-0e70-11e6-b5e4-0febe4822aa4, in_vertex={~label=book, member_id=1, community_id=1080937600}, ~type=authored}][{~label=author, member_id=0, community_id=1080937600}-authored->{~label=book, member_id=1, community_id=1080937600}]
    
    gremlin> g.V().valueMap()
    ==>{gender=[F], name=[Julia Child]}
    ==>{name=[The Art of French Cooking, Vol. 1], timestamp=[1961]}

    データが追加されました。キーと値のペアにより、プロパティ・キー、および作成したauthor頂点のnameおよびgenderの値のほか、作成したbook頂点のnameとtimestampの値が特定されます。

  4. ここまで入力したデータは、コミットされていません。DSE Graphはトランザクション・ベースであるため、入力したデータはGremlin Consoleが存在する前にコミットされている必要があります。コミットされていないと、データは失われます。コミットするには、次のコマンドを使用します。
    gremlin> graph.tx().commit()
    コミットに成功した場合、次の結果が得られます。
    ==>null
    コミットは、スキーマの作成時およびデータの挿入時に実行する必要があります。
  5. より複雑な探索の基本的な開始点であるグラフ探索では、頂点ラベルauthorおよびプロパティname = Julia Childとともにhas()ステップを使用して、特定の頂点を識別します。この一般的なグラフ探索を使用するのは、特定の情報によってグラフの検索を絞り込むためです。
    gremlin> g.V().has('author', 'name', 'Julia Child')
    ==>v[{~label=author, member_id=0, community_id=1080937600}]

    自動的に生成されるidは、1つの頂点ラベルと、グラフ内のその頂点の位置に関連付けられた2つの構成要素で構成されます。「グラフ探索の構造」では、そのidの構成要素について解説しています。

  6. 特定のプロパティ・キーの値のみ必要な場合は、values()探索ステップを使用します。次の例は、すべての頂点のnameを取得します。
    gremlin> g.V().values('name')

    存在する頂点は2つなので、2つの結果が返されます。複数の頂点が存在する場合は、nameを持つすべての頂点の結果がこの探索ステップによって返されます。

    ==>Julia Child
    ==>The Art of French Cooking, Vol. 1
  7. 辺情報も取得できます。次のコマンドは、すべての辺をフィルターして、辺ラベルauthoredを持つ辺を見つけます。辺情報では、内向き頂点および外向き頂点のほか、辺パラメーターidlabel、およびtypeに関する詳細が表示されます。
    gremlin> g.E().hasLabel('authored')
    ==>e[{out_vertex={~label=author, member_id=0, community_id=1080937600}, 
    local_id=6bd73210-0e70-11e6-b5e4-0febe4822aa4, 
    in_vertex={~label=book, member_id=1, community_id=1080937600}, ~type=authored}]
    [{~label=author, member_id=0, community_id=1080937600}-authored->{~label=book, member_id=1, community_id=1080937600}]
  8. 探索ステップcount()は、頂点と辺の数をカウントするのに便利です。辺数をカウントするには、V()ではなくE()を使用します。辺は1つになるはずです。
    gremlin> g.E().count()
    ==>1
  9. このチュートリアルの冒頭で行った頂点のカウント数探索を再実行すると、今度は、2つの頂点が返されるはずです。
    gremlin> g.V().count()
    ==>2

グラフにデータを追加する前に、スキーマについて少し説明します。スキーマは、グラフで使用可能なプロパティとそのデータ型の定義に使用します。これらのプロパティはさらに、頂点ラベルおよび辺ラベルの定義に使用します。スキーマ作成における最終的な重要ステップはインデックス作成です。インデックスは、グラフ探索の効率化および高速化に重要な役割を果たします。

詳細については、スキーマの作成およびに関するドキュメントを参照してください。

最初に、プロパティ・キーのスキーマを作成しましょう。次の2つのセルでは、最初のコマンドは、最初の2つの頂点と辺を作成したときに設定されたスキーマを消去します。スキーマ作成が完了したら、それらの要素のデータを長いスクリプトで再入力します。

注: DSE Graphには、Production(実稼働)と Development(開発)の2つのスキーマ・モードがあります。Productionモードでは、データを入力する前にすべてのスキーマを作成しておく必要があります。Developmentモードでは、データを入力した後にスキーマを作成できます。

  1. 前のスキーマを消去します。戻り値nullは、コマンドが成功したことを意味します。
    gremlin> schema.clear()
    ==>null
  2. プロパティ・キーを作成します。
    // プロパティ・キー 
    // ifNotExists()を使用して、作成済みのプロパティ・キーがあるかどうかを確認します。 
    schema.propertyKey('name').Text().ifNotExists().create()        
    schema.propertyKey('gender').Text().create()
    schema.propertyKey('instructions').Text().create()
    schema.propertyKey('category').Text().create()
    schema.propertyKey('year').Int().create()
    schema.propertyKey('timestamp').Timestamp().create()
    schema.propertyKey('ISBN').Text().create()
    schema.propertyKey('calories').Int().create()
    schema.propertyKey('amount').Text().create()
    schema.propertyKey('stars').Int().create()
    // single()はオプションです(デフォルトのため)
    schema.propertyKey('comment').Text().single().create()
    // 複数の値を指定できる複数プロパティの例
    // schema.propertyKey('nickname').Text().multiple().create() // 次の2行は2つのプロパティを定義した後、'country'に対するメタプロパティ'livedIn'を作成します  
    //メタプロパティとは、プロパティのプロパティです
    // 例:'livedIn':'1999-2005' 'country':'Belgium' 
    schema.propertyKey('livedIn').Text().create()                        
    schema.propertyKey('country').Text().multiple().properties('livedIn').create()
    // 返された一連のnull値は、すべてのプロパティ・キーの作成が正常に完了したことを意味します。
    ==>null

    各プロパティは、データ型を使用して定義する必要があります。DSE Graphのデータ型は、Cassandraのデータ型と連動しています。ここで使用するデータ型はText、Int、およびTimestampです。デフォルトでは、プロパティにはカーディナリティが1つありますが、複数のカーディナリティを使用してプロパティを定義することができます。複数のカーディナリティを使用すると、プロパティに複数の値を割り当てることができます。

    また、プロパティはそれ自身のプロパティ、つまり、メタプロパティを持つことができます。メタプロパティは、1段だけ深くネストすることができ、個々のプロパティに情報を割り当てるのに重宝します。プロパティ・キーは、既存の定義を上書きすることがないように追加のメソッドifNotExists()を使用して作成できることに注意してください。プロパティ・キーを作成した後、頂点ラベルと辺ラベルを定義できます。

  3. 頂点ラベルと辺ラベルを作成します。
    // 頂点ラベル
    schema.vertexLabel('author').ifNotExists().create()
    schema.vertexLabel('recipe').create()
    // プロパティを含む頂点ラベルの作成例
    // schema.vertexLabel('recipe').properties('name','instructions').create()
    // 以前に作成した頂点ラベルへのプロパティの追加例      
    // schema.vertexLabel('recipe').properties('name','instructions').add()         
    schema.vertexLabel('ingredient').create()
    schema.vertexLabel('book').create()
    schema.vertexLabel('meal').create()
    schema.vertexLabel('reviewer').create()
    // カスタム頂点IDの例:
    // schema.propertyKey('city_id').Int().create()
    // schema.propertyKey('sensor_id').Uuid().create()
    // schema().vertexLabel('FridgeSensor').partitionKey('city_id').clusteringKey('sensor_id').create()
                    
    //辺ラベル
    schema.edgeLabel('authored').ifNotExists().create()
    schema.edgeLabel('created').create()
    schema.edgeLabel('includes').create()
    schema.edgeLabel('includedIn').create()
    schema.edgeLabel('rated').connection('reviewer','recipe').create()
    // 返された一連のnullは、すべての頂点ラベルと辺ラベルの作成が正常に完了したことを意味します。
    ==>null

    頂点ラベルのスキーマは、ラベル「type」を定義し、この頂点ラベルに関連付けられたプロパティを任意で定義します。プロパティと頂点ラベルの関連付けを定義する方法には、異なる2つの方法があります。1つはスキーマ作成時、もう1つは頂点ラベルの追加後にプロパティを追加する方法です。ifNotExists()メソッドはあらゆるスキーマの作成に使用できます

    頂点IDは自動的に作成されますが、カスタム頂点IDは必要に応じて作成できます。このカスタム頂点IDの例は、ドキュメントでさらに詳しく説明していますが、パーティション・キークラスター化キーも定義できることに注意してください。

    辺ラベルのスキーマは、ラベル「type」を定義し、辺ラベルによって接続されている2つの頂点ラベルを任意で定義します。辺ラベルratedは、頂点ラベルreviewerrecipeを持つ隣接する頂点間の辺に使用します。次に、インデックスのスキーマを作成します。

  4. インデックスを作成します。
    // 頂点インデックス
    // 二次インデックス
    schema.vertexLabel('author').index('byName').secondary().by('name').add()
    // マテリアライズド・インデックス	  		
    schema.vertexLabel('recipe').index('byRecipe').materialized().by('name').add()
    schema.vertexLabel('meal').index('byMeal').materialized().by('name').add()
    schema.vertexLabel('ingredient').index('byIngredient').materialized().by('name').add()
    schema.vertexLabel('reviewer').index('byReviewer').materialized().by('name').add()
    // サーチ
    // schema.vertexLabel('recipe').index('search').search().by('instructions').asText().add()
    // schema.vertexLabel('recipe').index('search').search().by('instructions').asString().add()
    // 複数のプロパティ・キーにサーチ・インデックスが付いている場合
    // schema.vertexLabel('recipe').index('search').search().by('instructions').asText().by('category').asString().add()
    
    // メタプロパティ'livedIn'を使用したプロパティ・インデックス: 
    schema.vertexLabel('author').index('byLocation').property('country').by('livedIn').add()
    
    //辺インデックス
    schema.vertexLabel('reviewer').index('ratedByStars').outE('rated').by('stars').add()
    // 返された一連のnull値は、インデックスの作成が正常に完了したことを意味します。
    ==>null

    インデックス作成 は、複雑で非常に重要なトピックです。ここでは、いくつかの種類のインデックスを作成します。二次インデックスとマテリアライズド・インデックスは、簡潔にいうと、Cassandra組み込みインデックス作成を使用した2種類のインデックスです。 サーチ・インデックスは、SolrベースのDSE Searchを使用します。頂点ラベルごとに作成できるサーチ・インデックスは1つだけですが、複数のプロパティを含めることができます。プロパティ・インデックスを使用すると、メタプロパティにインデックスを作成できます。辺インデックスを使用すると、辺上のプロパティにインデックスを作成できます。以前に作成した頂点ラベルにインデックスを追加するには、add()を使用します。すべてのセルを実行してスキーマを作成したら、次のコマンドでそのスキーマを確認します。

  5. スキーマを確認します。
    gremlin> schema.describe()
    ==>schema.propertyKey("instructions").Text().single().create()
    schema.propertyKey("livedIn").Text().single().create()
    schema.propertyKey("country").Text().multiple().properties("livedIn").create()
    schema.propertyKey("amount").Text().single().create()
    schema.propertyKey("gender").Text().single().create()
    schema.propertyKey("year").Int().single().create()
    schema.propertyKey("calories").Int().single().create()
    schema.propertyKey("stars").Int().single().create()
    schema.propertyKey("ISBN").Text().single().create()
    schema.propertyKey("name").Text().single().create()
    schema.propertyKey("comment").Text().single().create()
    schema.propertyKey("category").Text().single().create()
    schema.propertyKey("timestamp").Timestamp().single().create()
    schema.edgeLabel("authored").multiple().create()
    schema.edgeLabel("rated").multiple().properties("stars").create()
    schema.edgeLabel("includedIn").multiple().create()
    schema.edgeLabel("created").multiple().create()
    schema.edgeLabel("includes").multiple().create()
    schema.vertexLabel("meal").properties("name").create()
    schema.vertexLabel("meal").index("byMeal").materialized().by("name").add()
    schema.vertexLabel("ingredient").properties("name").create()
    schema.vertexLabel("ingredient").index("byIngredient").materialized().by("name").add()
    schema.vertexLabel("author").properties("country", "name").create()
    schema.vertexLabel("author").index("byName").secondary().by("name").add()
    schema.vertexLabel("author").index("byLocation").property("country").by("livedIn").add()
    schema.vertexLabel("book").create()
    schema.vertexLabel("recipe").properties("name").create()
    schema.vertexLabel("recipe").index("byRecipe").materialized().by("name").add()
    schema.vertexLabel("reviewer").properties("name").create()
    schema.vertexLabel("reviewer").index("byReviewer").materialized().by("name").add()
    schema.vertexLabel("reviewer").index("ratedByStars").outE("rated").by("stars").add()
    
    schema.edgeLabel("rated").connection("reviewer", "recipe").add()

    schema.describe()コマンドは、入力したスキーマの再作成に使用できるスキーマを表示します。スキーマを作成せずにデータを入力する場合は、このコマンドによって、プロパティごとに設定されているデータ型を確認します。

    現在、DSE Graphでは、一度作成したスキーマは変更できません。プロパティ、頂点ラベル、辺ラベル、およびインデックスは追加作成できますが、プロパティのデータ型などは変更できません。スキーマを作成せずにデータを入力する方法は、開発および学習時には有効です。この方法は、実際の適用においてはお勧めしないことを強調しておきます。Productionモードでは、データが一度ロードされた後にスキーマを作成することはできません。

  6. describe()リスト内の特定のタイプの項目のスキーマだけを見つけるには、ステップを追加して、出力を改行で区切って、以下のindexの例に示すように文字列をgrep検索します。このノートブックで示すようにGremlinではGroovyが使用されるため、Groovyコマンドはすべてグラフ探索を操作します。
    gremlin> schema.describe().split('\n').grep(~/.*index.*/)
    ==>schema.vertexLabel("meal").index("byMeal").materialized().by("name").add()
    ==>schema.vertexLabel("ingredient").index("byIngredient").materialized().by("name").add()
    ==>schema.vertexLabel("author").index("byName").secondary().by("name").add()
    ==>schema.vertexLabel("author").index("byLocation").property("country").by("livedIn").add()
    ==>schema.vertexLabel("recipe").index("byRecipe").materialized().by("name").add()
    ==>schema.vertexLabel("reviewer").index("byReviewer").materialized().by("name").add()
    ==>schema.vertexLabel("reviewer").index("ratedByStars").outE("rated").by("stars").add()
  7. スキーマが作成できたので、次のスクリプトを使用して頂点と辺をさらに追加します。レシピ・データ・モデルで他の接続を探索できるように、追加の頂点および辺をグラフに入力します。以下に示す情報を使ってスクリプト・ファイルgenerateRecipe.groovyを作成します。最初のコマンドg.V().drop().iterate()に注目してください。このコマンドは、新しいデータを読み込む前に、グラフからすべての頂点データと辺データを削除します。
    // Recipe(レシピ)のすべての頂点および辺を追加します
    g.V().drop().iterate()
    
    // author(著者)頂点
    juliaChild = graph.addVertex(label, 'author', 'name','Julia Child', 'gender', 'F')
    simoneBeck = graph.addVertex(label, 'author', 'name', 'Simone Beck', 'gender', 'F')
    louisetteBertholie = graph.addVertex(label, 'author', 'name', 'Louisette Bertholie', 'gender', 'F')
    patriciaSimon = graph.addVertex(label, 'author', 'name', 'Patricia Simon', 'gender', 'F')
    aliceWaters = graph.addVertex(label, 'author', 'name', 'Alice Waters', 'gender', 'F')
    patriciaCurtan = graph.addVertex(label, 'author', 'name', 'Patricia Curtan', 'gender', 'F')
    kelsieKerr = graph.addVertex(label, 'author', 'name', 'Kelsie Kerr', 'gender', 'F')
    fritzStreiff = graph.addVertex(label, 'author', 'name', 'Fritz Streiff', 'gender', 'M')
    emerilLagasse = graph.addVertex(label, 'author', 'name', 'Emeril Lagasse', 'gender', 'M')
    jamesBeard = graph.addVertex(label, 'author', 'name', 'James Beard', 'gender', 'M')
    
    // book(本)頂点
    artOfFrenchCookingVolOne = graph.addVertex(label, 'book', 'name', 'The Art of French Cooking, Vol. 1', 'year', 1961)
    simcasCuisine = graph.addVertex(label, 'book', 'name', "Simca's Cuisine:100 Classic French Recipes for Every Occasion", 'year', 1972, 'ISBN', '0-394-40152-2')
    frenchChefCookbook = graph.addVertex(label, 'book', 'name','The French Chef Cookbook', 'year', 1968, 'ISBN', '0-394-40135-2')
    artOfSimpleFood = graph.addVertex(label, 'book', 'name', 'The Art of Simple Food:Notes, Lessons, and Recipes from a Delicious Revolution', 'year', 2007, 'ISBN', '0-307-33679-4')
    
    // recipe(レシピ)頂点
    beefBourguignon = graph.addVertex(label, 'recipe', 'name', 'Beef Bourguignon', 'instructions', 'Braise the beef.玉ねぎと人参を炒めます。ワインを入れたら、厚手鍋で220度で1時間調理します')
    ratatouille = graph.addVertex(label, 'recipe', 'name', 'Rataouille', 'instructions', 'Peel and cut the egglant.ナスは縦に、幅2.5cm、長さ7.5cm、厚さ1cmに切ります')
    saladeNicoise = graph.addVertex(label, 'recipe', 'name', 'Salade Nicoise', 'instructions', '供する直前にサラダ・ボウルか大皿にレタスの葉を並べます。レタスにオリーブオイルをたらし、塩を振りかけます。')
    wildMushroomStroganoff = graph.addVertex(label, 'recipe', 'name', 'Wild Mushroom Stroganoff', 'instructions', 'パッケージに記載された説明に従って卵めんを茹でて温かくしておきます。大きめのソテー・パンに大さじ1と1/2のオリーブオイルを入れ、強めの中火で熱します。')
    spicyMeatloaf = graph.addVertex(label, 'recipe', 'name', 'Spicy Meatloaf', 'instructions', 'オーブンを190度に予熱します。大きなスキレットにベーコンを入れ、脂が出てカリッとするまで8〜10分中火で炒めます。')
    oystersRockefeller = graph.addVertex(label, 'recipe', 'name', 'Oysters Rockfeller', 'instructions', 'エシャロット、セロリ、ハーブ、調味料を大さじ3杯のバターで3分間炒めます。クレソンを入れてしんなりさせます。')
    carrotSoup = graph.addVertex(label, 'recipe', 'name', 'Carrot Soup', 'instructions', '厚手の鍋にバターを溶かします。バターが泡立ってきたら玉ねぎ、タイムを加え、柔らかくなるまで約10分間、中火で炒めます。')
    roastPorkLoin = graph.addVertex(label, 'recipe', 'name', 'Roast Pork Loin', 'instructions', '前日に骨端の約2.5cm手前で止めて肉を骨から分離しておきます。豚肉にまんべんなく塩コショウをふって染み込ませ一晩冷蔵庫に入れて置きます。')
    
    // ingredient(材料)頂点
    beef = graph.addVertex(label, 'ingredient', 'name', 'beef')
    onion = graph.addVertex(label, 'ingredient', 'name', 'onion')
    mashedGarlic = graph.addVertex(label, 'ingredient', 'name', 'mashed garlic')
    butter = graph.addVertex(label, 'ingredient', 'name', 'butter')
    tomatoPaste = graph.addVertex(label, 'ingredient', 'name', 'tomato paste')
    eggplant = graph.addVertex(label, 'ingredient', 'name', 'eggplant')
    zucchini = graph.addVertex(label, 'ingredient', 'name', 'zucchini')
    oliveOil = graph.addVertex(label, 'ingredient', 'name', 'olive oil')
    yellowOnion = graph.addVertex(label, 'ingredient', 'name', 'yellow onion')
    greenBean = graph.addVertex(label, 'ingredient', 'name', 'green beans')
    tuna = graph.addVertex(label, 'ingredient', 'name', 'tuna')
    tomato = graph.addVertex(label, 'ingredient', 'name', 'tomato')
    hardBoiledEgg = graph.addVertex(label, 'ingredient', 'name', 'hard-boiled egg')
    eggNoodles = graph.addVertex(label, 'ingredient', 'name', 'egg noodles')
    mushroom = graph.addVertex(label, 'ingredient', 'name', 'mushrooms')
    bacon = graph.addVertex(label, 'ingredient', 'name', 'bacon')
    celery = graph.addVertex(label, 'ingredient', 'name', 'celery')
    greenBellPepper = graph.addVertex(label, 'ingredient', 'name', 'green bell pepper')
    groundBeef = graph.addVertex(label, 'ingredient', 'name', 'ground beef')
    porkSausage = graph.addVertex(label, 'ingredient', 'name', 'pork sausage')
    shallot = graph.addVertex(label, 'ingredient', 'name', 'shallots')
    chervil = graph.addVertex(label, 'ingredient', 'name', 'chervil')
    fennel = graph.addVertex(label, 'ingredient', 'name', 'fennel')
    parsley = graph.addVertex(label, 'ingredient', 'name', 'parsley')
    oyster = graph.addVertex(label, 'ingredient', 'name', 'oyster')
    pernod = graph.addVertex(label, 'ingredient', 'name', 'Pernod')
    thyme = graph.addVertex(label, 'ingredient', 'name', 'thyme')
    carrot = graph.addVertex(label, 'ingredient', 'name', 'carrots')
    chickenBroth = graph.addVertex(label, 'ingredient', 'name', 'chicken broth')
    porkLoin = graph.addVertex(label, 'ingredient', 'name', 'pork loin')
    redWine = graph.addVertex(label, 'ingredient', 'name', 'red wine')
    
    // meal(食事)頂点
    SaturdayFeast = graph.addVertex(label, 'meal', 'name', 'Saturday Feast', 'timestamp', '2015-11-30', 'calories', 1000)
    EverydayDinner = graph.addVertex(label, 'meal', 'name', 'EverydayDinner', 'timestamp', '2016-01-14', 'calories', 600)
    JuliaDinner = graph.addVertex(label, 'meal', 'name', 'JuliaDinner', 'timestamp', '2016-01-14', 'calories', 900)
    
    // authorとbook間の辺
    juliaChild.addEdge('authored', artOfFrenchCookingVolOne)
    simoneBeck.addEdge('authored', artOfFrenchCookingVolOne)
    louisetteBertholie.addEdge('authored', artOfFrenchCookingVolOne)
    simoneBeck.addEdge('authored', simcasCuisine)
    patriciaSimon.addEdge('authored', simcasCuisine)
    juliaChild.addEdge('authored', frenchChefCookbook)
    aliceWaters.addEdge('authored', artOfSimpleFood)
    patriciaCurtan.addEdge('authored', artOfSimpleFood)
    kelsieKerr.addEdge('authored', artOfSimpleFood)
    fritzStreiff.addEdge('authored', artOfSimpleFood)
    
    // author - recipe間の辺
    juliaChild.addEdge('created', beefBourguignon, 'year', 1961)
    juliaChild.addEdge('created', ratatouille, 'year', 1965)
    juliaChild.addEdge('created', saladeNicoise, 'year', 1962)
    emerilLagasse.addEdge('created', wildMushroomStroganoff, 'year', 2003)
    emerilLagasse.addEdge('created', spicyMeatloaf, 'year', 2000)
    aliceWaters.addEdge('created', carrotSoup, 'year', 1995)
    aliceWaters.addEdge('created', roastPorkLoin, 'year', 1996)
    jamesBeard.addEdge('created', oystersRockefeller, 'year', 1970)
    
    // recipe - ingredient間の辺
    beefBourguignon.addEdge('includes', beef, 'amount', '2 lbs')
    beefBourguignon.addEdge('includes', onion, 'amount', '1 sliced')
    beefBourguignon.addEdge('includes', mashedGarlic, 'amount', '2 cloves')
    beefBourguignon.addEdge('includes', butter, 'amount', '3.5 Tbsp')
    beefBourguignon.addEdge('includes', tomatoPaste, 'amount', '1 Tbsp')
    ratatouille.addEdge('includes', eggplant, 'amount', '1 lb')
    ratatouille.addEdge('includes', zucchini, 'amount', '1 lb')
    ratatouille.addEdge('includes', mashedGarlic, 'amount', '2 cloves')
    ratatouille.addEdge('includes', oliveOil, 'amount', '4-6 Tbsp')
    ratatouille.addEdge('includes', yellowOnion, 'amount', '1 1/2 cups or 1/2 lb thinly sliced')
    saladeNicoise.addEdge('includes', oliveOil, 'amount', '2-3 Tbsp')
    saladeNicoise.addEdge('includes', greenBean, 'amount', '1 1/2 lbs blanched, trimmed')
    saladeNicoise.addEdge('includes', tuna, 'amount', '8-10 ozs oil-packed, drained and flaked')
    saladeNicoise.addEdge('includes', tomato, 'amount', '3 or 4 red, peeled, quartered, cored, and seasoned')
    saladeNicoise.addEdge('includes', hardBoiledEgg, 'amount', '8 halved lengthwise')
    wildMushroomStroganoff.addEdge('includes', eggNoodles, 'amount', '16 ozs wmyIde')
    wildMushroomStroganoff.addEdge('includes', mushroom, 'amount', '2 lbs wild or exotic, cleaned, stemmed, and sliced')
    wildMushroomStroganoff.addEdge('includes', yellowOnion, 'amount', '1 cup thinly sliced')
    spicyMeatloaf.addEdge('includes', bacon, 'amount', '3 ozs diced')
    spicyMeatloaf.addEdge('includes', onion, 'amount', '2 cups finely chopped')
    spicyMeatloaf.addEdge('includes', celery, 'amount', '2 cups finely chopped')
    spicyMeatloaf.addEdge('includes', greenBellPepper, 'amount', '1/4 cup finely chopped')
    spicyMeatloaf.addEdge('includes', porkSausage, 'amount', '3/4 lbs hot')
    spicyMeatloaf.addEdge('includes', groundBeef, 'amount', '1 1/2 lbs chuck')
    oystersRockefeller.addEdge('includes', shallot, 'amount', '1/4 cup chopped')
    oystersRockefeller.addEdge('includes', celery, 'amount', '1/4 cup chopped')
    oystersRockefeller.addEdge('includes', chervil, 'amount', '1 tsp')
    oystersRockefeller.addEdge('includes', fennel, 'amount', '1/3 cup chopped')
    oystersRockefeller.addEdge('includes', parsley, 'amount', '1/3 cup chopped')
    oystersRockefeller.addEdge('includes', oyster, 'amount', '2 dozen on the half shell')
    oystersRockefeller.addEdge('includes', pernod, 'amount', '1/3 cup')
    carrotSoup.addEdge('includes', butter, 'amount', '4 Tbsp')
    carrotSoup.addEdge('includes', onion, 'amount', '2 medium sliced')
    carrotSoup.addEdge('includes', thyme, 'amount', '1 sprig')
    carrotSoup.addEdge('includes', carrot, 'amount', '2 1/2 lbs, peeled and sliced')
    carrotSoup.addEdge('includes', chickenBroth, 'amount', '6 cups')
    roastPorkLoin.addEdge('includes', porkLoin, 'amount', '1 bone-in, 4-rib')
    roastPorkLoin.addEdge('includes', redWine, 'amount', '1/2 cup')
    roastPorkLoin.addEdge('includes', chickenBroth, 'amount', '1 cup')
    
    // book - recipe間の辺
    beefBourguignon.addEdge('includedIn', artOfFrenchCookingVolOne)
    saladeNicoise.addEdge('includedIn', artOfFrenchCookingVolOne)
    carrotSoup.addEdge('includedIn', artOfSimpleFood)
    
    // meal - recipe間の辺
    beefBourguignon.addEdge('includedIn', SaturdayFeast)
    carrotSoup.addEdge('includedIn', SaturdayFeast)
    oystersRockefeller.addEdge('includedIn', SaturdayFeast)
    carrotSoup.addEdge('includedIn', EverydayDinner)
    roastPorkLoin.addEdge('includedIn', EverydayDinner)
    beefBourguignon.addEdge('includedIn', JuliaDinner)
    saladeNicoise.addEdge('includedIn', JuliaDinner)
    
    // meal – book間の辺
    EverydayDinner.addEdge('includedIn', artOfSimpleFood)
    SaturdayFeast.addEdge('includedIn', simcasCuisine)
    JuliaDinner.addEdge('includedIn', artOfFrenchCookingVolOne)
    g.V()
    スクリプトを実行するには、Gremlin Consoleで読み込みます。
    gremlin> :load /tmp/generateRecipe.groovy
    上記の「/tmp」はスクリプトを保存したディレクトリに読み替えてください。
    // 頂点および辺の一連の戻り値は、スクリプトが正常に完了したことを意味する
    // サンプル頂点
    ==>v[{~label=author, member_id=0, community_id=1878171264}]
    // サンプル辺
    ==>e[{out_vertex={~label=meal, member_id=27, community_id=1989847424}, 
    local_id=545b88b0-0e7b-11e6-b5e4-0febe4822aa4, 
    in_vertex={~label=book, member_id=10, community_id=1878171264}, 
    ~type=includedIn}]
    [{~label=meal, member_id=27, community_id=1989847424}-includedIn->{~label=book, member_id=10, community_id=1878171264}]

    プロパティtimestampは、 Cassandraの有効なtimestampデータ型に相当するTimestampデータ型です。

  8. トランザクションをコミットして入力データを保存します。
    gremlin> graph.tx().commit()
  9. 頂点カウントを再実行します。
    gremlin> g.V().count()
    ==>56

    データを投入するためのスクリプト記述には、ツールgraphloaderも使用できます。詳細については、graphloaderのドキュメントを参照してください。

グラフ探索を使ってグラフを探索すると、興味深い結論に至る場合があります。

  1. グラフ内に複数のauthor頂点がある場合に特定の頂点を見つけるには、具体的なnameを指定してください。この探索では、Julia Childというnamehas(持つ)頂点について格納されている頂点情報が取得されます。また、has句には頂点がauthorであるという制約も含まれていることに注意してください。
    gremlin> g.V().has('author','name','Julia Child')
    ==>v[{~label=author, member_id=0, community_id=1878171264}]
  2. 次の探索では、has()は、name = Julia Childによってフィルターされた頂点情報を取得します。探索ステップoutE()は、authoredラベルを持つ頂点から発信される(外向き)辺を見つけます。
    gremlin> g.V().has('name','Julia Child').outE('authored')

    辺情報が返されます。

    ==>e[{out_vertex={~label=author, member_id=0, community_id=1878171264}, 
    local_id=521f5450-0e7b-11e6-b5e4-0febe4822aa4, 
    in_vertex={~label=book, member_id=10, community_id=1878171264}, 
    ~type=authored}][{~label=author, member_id=0, 
    community_id=1878171264}-authored->{~label=book, member_id=10, community_id=1878171264}]
    
    ==>e[{out_vertex={~label=author, member_id=0, community_id=1878171264}, 
    local_id=523155b0-0e7b-11e6-b5e4-0febe4822aa4, 
    in_vertex={~label=book, member_id=12, community_id=1878171264}, 
    ~type=authored}]
    [{~label=author, member_id=0, community_id=1878171264}-authored->{~label=book, member_id=12, community_id=1878171264}]
  3. 代わりに、すべての著者が執筆した本を求めるクエリーでは、最後の例は辺を取得しますが、隣接するbook頂点は取得しません。探索ステップinV()を追加して、外向き辺に接続するすべての頂点を見つけ、さらにこれらの頂点の書名を出力します。探索ステップを連結して、V().outE().inV()を使用して頂点から外向きの辺を経由して隣接頂点に向かって移動しているところに注目してください。外向き辺には、具体的なフィルター値、authoredが割り当てられています。
    gremlin> g.V().outE('authored').inV().values('name')
    ==>The Art of French Cooking, Vol. 1
    ==>Simca's Cuisine:100 Classic French Recipes for Every Occasion
    ==>The Art of French Cooking, Vol. 1
    ==>The French Chef Cookbook
    ==>Simca's Cuisine:100 Classic French Recipes for Every Occasion
    ==>The Art of French Cooking, Vol. 1
    ==>The Art of Simple Food:Notes, Lessons, and Recipes from a Delicious Revolution
    ==>The Art of Simple Food:Notes, Lessons, and Recipes from a Delicious Revolution
    ==>The Art of Simple Food:Notes, Lessons, and Recipes from a Delicious Revolution
    ==>The Art of Simple Food:Notes, Lessons, and Recipes from a Delicious Revolution
  4. 書名は、生成リストで重複して表示されます。これは、著者ごとにリストが返されるためです。本に3人の著者がある場合は、3つのリストが返されます。探索ステップdedup()は重複を排除できます。
    gremlin> g.V().outE('authored').inV().values('name').dedup()
    ==>The Art of French Cooking, Vol. 1
    ==>Simca's Cuisine:100 Classic French Recipes for Every Occasion
    ==>The French Chef Cookbook
    ==>The Art of Simple Food:Notes, Lessons, and Recipes from a Delicious Revolution
  5. 書名は、生成リストで重複して表示されます。これは、著者ごとにリストが返されるためです。本に3人の著者がある場合は、3つのリストが返されます。探索ステップdedup()は重複を排除できます。
    gremlin> g.V().outE('authored').inV().values('name').dedup()
    ==>Simca's Cuisine:100 Classic French Recipes for Every Occasion
    ==>The Art of French Cooking, Vol. 1
    ==>The Art of Simple Food:Notes, Lessons, and Recipes from a Delicious Revolution
    ==>The French Chef Cookbook
  6. 特定の著者についてhas()ステップを再び挿入して探索を絞り込みます。Julia Childが著したすべての本を見つけます。
    gremlin> g.V().has('name','Julia Child').outE('authored').inV().values('name')
    ==>The Art of French Cooking, Vol. 1
    ==>The French Chef Cookbook
  7. 最後の例とこの例では、同じ結果が返されます。ただし、探索ステップの数と探索ステップのタイプがパフォーマンスに影響します。探索ステップoutE()は、辺が明示的に必要な場合にのみ使用してください。この例では、辺を探索して接続されている頂点に関する情報を取得しますが、クエリーにとってこの辺情報は重要ではありません。
    gremlin> g.V().has('name','Julia Child').out('authored').values('name')
    ==>The Art of French Cooking, Vol. 1
    ==>The French Chef Cookbook

    探索ステップout()は、辺情報を取得せずに辺ラベルauthoredに基づいて接続しているbook頂点を取得します。大規模グラフの探索では、探索のこのような微妙な違いはレイテンシー問題に発展します。

  8. 探索ステップを追加するたびに、結果は微調整されていきます。別の連結されたhas探索ステップを追加すると、Julia Childによって書かれ、1967年以降に出版された本のみ検出されます。この例では、gt、つまり、greater than(大なり)関数を使用しています。
    gremlin> g.V().has('name','Julia Child').out('authored').has('year', gt(1967)).values('name')
    ==>The French Chef Cookbook
  9. 開発またはテスト時、各頂点ラベルを持つ頂点の数をチェックすると、そのデータが読み込まれたことを確認できることがよくあります。頂点ラベルごとに頂点の数を見つけるには、探索ステップlabel() の後ろに探索ステップgroupCount()を続けたものを使用します。ステップgroupCount()は、前のステップで得られた結果を集計するのに便利です。
    gremlin> g.V().label().groupCount()
    ==>{meal=3, ingredient=31, author=10, book=4, recipe=8}
  10. 情報を保存または交換するには、出力ファイルにデータを書き込みます。Gryoファイルは、データをDSE Graphへの再読み込みに使用できるバイナリー形式ファイルです。次のコマンドでは、グラフI/Oを使用してグラフ全体をファイルに書き込みます。gryo()graphml() またはgraphson()で置き換えることによって、他のファイル形式に書き込むことができます。
    gremlin> graph.io(gryo()).writeGraph("/tmp/recipe.gryo")
    ==>null
  11. Gryoファイルを読み込むには、読み込みコマンドを使用します。
    gremlin> graph.io(gryo()).readGraph("/tmp/recipe.gryo")
    ==>null

次のタスク

探索に関する他の情報については、探索を使用したクエリーの作成を参照してください。さまざまなロード・オプションに関する情報については、DSE Graph Loaderまたは DSE Graphの使用を参照してください。