入門 - DataStax Studioのクイック・スタート

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

グラフ・データベースは、オブジェクト間の単純および複雑な関係の検出に役立ちます。関係は、オブジェクトが他のオブジェクトおよび環境と対話する方法にとって重要です。グラフ・データベースは、オブジェクト間の関係を完全に表現します。

グラフ・データベースは以下の3つの要素で構成されます。
頂点
頂点は、人、場所、自動車、レシピなどのオブジェクトで、名詞と考えられるあらゆるものがあります。
エッジ
エッジは、2つの頂点間の関係を定義します。人はソフトウェアを作成し、著者は本を執筆します。エッジを定義する際は動詞を考えてください。
プロパティ
頂点またはエッジのいずれかの属性をいくつか記述したキーと値のペア。プロパティ・キーは、キーと値のペアでキーを記述するために使用します。DSE Graphではすべてのプロパティはグローバルです。これは、どの頂点についてもプロパティを使用できることを意味します。たとえば、グラフ内のすべての頂点に対して「name」というプロパティを使用できます。
頂点、エッジ、およびプロパティはそれぞれプロパティを持つことができます。このため、DSE Graphはプロパティ・グラフに分類されます。要素のプロパティは、プロパティ・グラフに情報を保存したり、クエリーを実行したりするための重要な要素です。

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

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

要素には、頂点ラベルエッジ・ラベルを使用して、グラフ・データベース内の頂点およびエッジのタイプを区別するためのラベルが付けられます。authorというラベルが付いている頂点は、著者に関する情報を保持します。authorとbook間のエッジには、authoredというラベルが付いています。適切なラベルを指定することは、グラフ・データ・モデリングにおける重要なステップです。

通常、頂点とエッジはプロパティを持っています。たとえば、author頂点はnameとgenderというプロパティを持つことができます。エッジもプロパティを持つことができます。createdエッジは、隣接するrecipe頂点が作成された年を識別するyearプロパティを持つことができます。

グラフ・データベース内の情報は、グラフ探索を使用して取得します。グラフ探索では、結果が返されるまで、定義された開始点から1つまたは一連の探索ステップによってグラフを巡回し、各ステップをフィルターします。

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

手順

  1. DataStax Enterpriseをインストールします。
  2. DSE Graphを有効にした状態で、DataStax Enterpriseを起動します。
  3. DataStax Studio 2.0のインストールと実行。必要に応じてStudioノートブックも作成します。このチュートリアルは、Studioノートブック、Studio 1.0のDSE QuickStart、およびStudio 2.0のDSE QuickStart v2として存在します。
  4. DataStax Studioで新しい接続を作成します。グラフ名を選択します。使ったことのないグラフ名であれば何でもかまいません。

    Studio内の接続は、グラフを定義し、そのグラフにグラフ探索gを割り当てます。グラフ探索とは、そのグラフ探索で定義されたフィルターに基づき、グラフ内の各頂点に移動するメカニズムです。DSE Graphをクエリーするには、グラフ探索gが特定のグラフに割り当てられている必要があります。Studioは接続によってこの割り当てを管理します。

  5. DataStax Studioで新しいノートブックを作成します。最後のステップで作成された接続を選択します。各ノートブックは、特定のグラフに接続されています。複数のノートブックが同じグラフに接続されている場合もあります。

    セルが1つある空白のノートブックが開きます。DSE Graphは、各DataStax EnterpriseノードでGremlin Server tinkerpop.serverを実行します。DataStax StudioはGremlin Serverに自動的に接続します。存在しない場合は、接続情報を使用してグラフを作成します。graphはDSEデータベース・キースペースごとに1つのグラフ・インスタンスとして格納されます。グラフが存在すると、グラフ探索gが構成され、それによってグラフ探索を実行してグラフをクエリーできます。グラフ探索は、デフォルトで標準OLTP探索エンジンである特定の探索ソースに結合します。graphコマンドを使用すると、データベースに頂点やエッジを追加したり、他のグラフ情報を取得したりできます。gコマンドは、頂点やエッジに対してクエリーを実行したり、頂点やエッジを追加したりできます。

  6. 最初に、スキーマ・モードを[Development]に設定します。[Development]は、テスト中にいつでもスキーマを追加できる緩やかなモードです。また、テストの目的で、広範なグラフ探索によってデータを調べるためにフル・スキャンを可能にします。実稼働については、動作異常の原因となるような対話型のスキーマ変更を回避するために、スキーマ・モードを[Production]に設定し、フル・スキャンはオフにしてください。
    schema.config().option('graph.schema_mode').set('Development')
    schema.config().option('graph.allow_scan').set('true')
  7. グラフ内に存在する頂点の数を確認するには、探索ステップcount()を使用します。現時点ではまだデータを追加していないため、頂点の数はゼロです。すべての頂点を取得するにはグラフ探索gをV()で連結し、頂点の数を計算するにはをcount()で連結します。連結することによって、最も効率の良い順序でシーケンシャルな探索手順が実行されます。
    g.V().count()
    注意: 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]の各ビューを表示するボタンを使用して結果を確認します。
    juliaChild = graph.addVertex(label,'author', 'name','Julia Child', 'gender','F')
    各ビューには同じ情報が表示されます。
    • member_id、community_id、およびlabelで構成される自動生成のid

      グラフ・ストレージ構造内のmember_idおよびcommunity_idグループの頂点(グラフ探索の構造を参照)

    • 頂点ラベル
    • nameとgenderプロパティ、およびそれらの値
    注: DSE 6.0では、標準の自動生成IDは廃止されました。カスタムIDにはいくつかの変更が加えられ、partitionKeyおよびclusteringKeyを使用して頂点IDを指定する方法が一般的になる可能性が高くなります。
    次のコマンドで示すように、プロパティ・キーは、さまざまなタイプの情報に再利用できます。プロパティは、複数の頂点ラベルとともに使用できるという意味でグローバルです。ただし、グラフ探索においては、プロパティとともに頂点ラベルを指定する必要があることを理解することが重要です。

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

  2. グラフ内にbook頂点を作成します。
    artOfFrenchCookingVolOne = graph.addVertex(label, 'book','name', 'The Art of French Cooking, Vol. 1', 'year', 1961)

    author頂点と同様に、作成したbook頂点に関するすべての情報を表示できます。[Graph]ビュー[Settings]ボタン(歯車アイコン)を使用し、Chef {{name}}と入力してauthorの表示ラベルを変更します。{{label}}:{{name}}を使用してbookの表示ラベルを変更します。

  3. 次の2つのコマンドを実行します。最初のコマンドは、author頂点とbook頂点間のエッジを作成します。2つ目のコマンドは、2つの頂点とそれらを結ぶエッジを取得するグラフ探索です。関係を表示するには、[Graph]ビューを使用します。詳細情報を表示するには、要素上でスクロールします。
    juliaChild.addEdge('authored', artOfFrenchCookingVolOne)
    g.V()

    データが追加されました。

  4. 頂点ラベルauthorとプロパティname = Julia Childを使用し、has()ステップをチェックして、著者について挿入されたデータが正しいことを確認します。具体的な情報によってグラフの検索が絞り込まれるため、このグラフ探索は、さらに複雑な探索の基本的な開始点となります。
    g.V().has('author', 'name', 'Julia Child')
    結果を表示するために、[Raw]ビューよりもはるかに見やすい[Table]ビューを使用します。

    Julia Childのauthor頂点の頂点情報が表示されます。頂点ラベルは頂点の種類を指定し、キーと値のペアは、nameとgenderのプロパティ・キーとその値を指定します。自動的に生成されるidは、1つの頂点ラベルと、グラフ内のその頂点の位置に関連付けられた2つの構成要素で構成されます。「グラフ探索の構造」では、そのidの構成要素について解説しています。

  5. もう1つの有用な探索は、指定された頂点のプロパティ値ごとにキーと値のリストを出力するvalueMap()です。
    g.V().hasLabel('author').valueMap()
    注意: クエリー対象の頂点またはエッジに非常に多くのプロパティ・キーがある場合、プロパティを指定せずにvalueMap()を指定すると、スロー・クエリーのレイテンシーが発生する可能性があります。valueMap('name')のように特定のプロパティを指定できます。
  6. 特定のプロパティ・キーの値のみ必要な場合は、values()探索ステップを使用してください。次の例は、すべての頂点のnameを取得します。
    g.V().values('name')

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

  7. エッジ情報も取得できます。次のコマンドは、すべてのエッジをフィルターして、エッジ・ラベルauthoredを持つエッジを見つけます。エッジ情報には、内向き頂点および外向き頂点のほか、idlabel、およびtypeのエッジ・パラメーターに関する詳細が表示されます。
    g.E().hasLabel('authored')
    {
      "id": "{out_vertex={member_id=0, community_id=1372852736, ~label=author}, 
                local_id=ca2fad30-0e55-11e6-b5e4-0febe4822aa4, 
                in_vertex={member_id=0, community_id=14617472, ~label=book}, ~type=authored}",
      "label": "authored",
      "type": "edge",
      "inVLabel": "book",
      "outVLabel": "author",
      "inV": "book:14617472:0",
      "outV": "author:1372852736:0"
    }
  8. 探索ステップcount()は、頂点とエッジの両方の数をカウントするのに便利です。エッジ数をカウントするには、V()E()に置き換えてください。エッジは1つになります。
    g.E().count()
  9. このチュートリアルの冒頭で行った頂点のカウント数探索を再実行すると、今度は、2つの頂点が返されます。
    g.V().count()

スキーマの作成

グラフにデータを追加する前に、スキーマについて説明します。スキーマは、グラフについて考えられるプロパティとそのデータ型を定義します。これらのプロパティはさらに、頂点ラベルおよびエッジ・ラベルの定義に使用されます。スキーマ作成における最後の重要ステップは、インデックス作成です。インデックスは、グラフ探索を効率良く高速で行ううえで重要な役割を果たします。詳細については、「スキーマの作成」と「インデックスの作成」を参照してください。

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

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

  1. 新しいスキーマを作成します。
    1. スキーマを消去します。
      schema.clear()
    2. 新しいプロパティ・キーのスキーマを作成します。
      // Property Keys 
      // Check for previous creation of property key with 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() is optional, as it is the default
      schema.propertyKey('comment').Text().single().create()
      // Example of a multiple property that can have several values
      // schema.propertyKey('nickname').Text().multiple().create() // Next 2 lines define two properties, then create a meta-property 'livedIn' on 'country'  
      // A meta-property is a property of a property
      // EX: 'livedIn': '1999-2005' 'country': 'Belgium' 
      schema.propertyKey('livedIn').Text().create()                        
      schema.propertyKey('country').Text().multiple().properties('livedIn').create()

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

      また、プロパティはそれ自身のプロパティ、つまり、メタプロパティを持つことができます。メタプロパティは、1段だけ深くネストすることができ、個々のプロパティに情報を割り当てるのに便利です。プロパティ・キーは、ifNotExists()メソッドを追加して作成できます。このメソッドでは、すでに存在する可能性のある定義を上書きしないようにすることができます。プロパティ・キーを作成したら、頂点ラベルとエッジ・ラベルを定義できます。

  2. 頂点ラベルとエッジ・ラベルのスキーマを作成します。
    // Vertex Labels
    schema.vertexLabel('author').ifNotExists().create()
    schema.vertexLabel('recipe').create()
    // Example of creating vertex label with properties
    // schema.vertexLabel('recipe').properties('name','instructions').create()
    // Example of adding properties to a previously created vertex label      
    // schema.vertexLabel('recipe').properties('name','instructions').add()         
    schema.vertexLabel('ingredient').create()
    schema.vertexLabel('book').create()
    schema.vertexLabel('meal').create()
    schema.vertexLabel('reviewer').create()
    // Example of custom vertex id:
    // schema.propertyKey('city_id').Int().create()
    // schema.propertyKey('sensor_id').Uuid().create()
    // schema().vertexLabel('FridgeSensor').partitionKey('city_id').clusteringKey('sensor_id').create()
                    
    // Edge Labels
    schema.edgeLabel('authored').ifNotExists().create()
    schema.edgeLabel('created').create()
    schema.edgeLabel('includes').create()
    schema.edgeLabel('includedIn').create()
    schema.edgeLabel('rated').connection('reviewer','recipe').create()

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

    頂点IDは自動的に作成されますが、必要に応じてカスタム頂点IDを作成できます。以下に示す、カスタム頂点IDの例は、パーティション・キークラスター化キーを定義します。

    DSE Graphでは、グラフごとに入力できる頂点ラベル数は200までです。

    エッジ・ラベルのスキーマはtypeラベルを定義し、エッジ・ラベルによってconnection()で接続されている2つの頂点ラベルを任意で定義します。ratedエッジ・ラベルは、頂点ラベルreviewerおよびrecipeを持つ隣接頂点間のエッジを定義します。デフォルトでは、エッジには複数のカーディナリティがありますが、1つのカーディナリティを使用してエッジを定義することができます。複数のカーディナリティを使用すると、同じエッジ・ラベルなのにプロパティ値が異なる複数のエッジを割り当てることができます。

  3. インデックス・スキーマを作成します。
    // Vertex Indexes
    // Secondary
    schema.vertexLabel('author').index('byName').secondary().by('name').add()
    // Materialized	  		
    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()
    // Search
    // schema.vertexLabel('recipe').index('search').search().by('instructions').asText().add()
    // schema.vertexLabel('recipe').index('search').search().by('instructions').asString().add()
    // If more than one property key is search indexed
    // schema.vertexLabel('recipe').index('search').search().by('instructions').asText().by('category').asString().add()
    
    // Property index using meta-property 'livedIn': 
    schema.vertexLabel('author').index('byLocation').property('country').by('livedIn').add()
    
    // Edge Index
    schema.vertexLabel('reviewer').index('ratedByStars').outE('rated').by('stars').add()

    インデックス作成は、複雑で非常に重要なテーマです。ここでは、数種類のインデックスを作成します。セカンダリ・インデックスとマテリアライズド・インデックスは、簡潔に言うと、DSEデータベース組み込みインデックス作成を使用した2種類のインデックスです。検索インデックスは、SolrベースのDSE Searchを使用します。頂点ラベルごとに作成できる検索インデックスは1つだけですが、複数のプロパティを含めることができます。プロパティ・インデックスを使用すると、メタプロパティにインデックスを作成できます。エッジ・インデックスを使用すると、エッジ上のプロパティにインデックスを作成できます。以前作成した頂点ラベルにインデックスを追加するには、add()を使用します。

  4. スキーマを確認します。
    schema.describe()

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

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

  5. describe()リスト内の特定タイプの項目のスキーマだけを見つけるには、次のコマンドを使用します。
    schema.describe().split('\n').grep(~/.*index.*/)

    ステップを追加すると、出力が改行で分割され、indexで示されるような文字列をグレップ検索できます。ここで使用するGremlinバリアントは、Apache Groovyをベースとしているため、グラフ探索の操作にはどのようなGroovyコマンドでも使用できます。Apache Groovyは、Javaをスムーズに統合してスクリプト記述機能を提供する言語です。

データの追加

  1. スキーマが作成されました。次は、レシピ・データ・モデル内でさらに接続を探索するために、次のスクリプトを使用してグラフに頂点とエッジを追加します。1つのDataStax Studioセル内に次の行を入力して実行します。最初のコマンドg.V().drop().iterate()は、新しいデータを読み込む前に頂点およびエッジのデータをすべて削除します。スクリプト実行後は、必ず[Graph]ビューを選択してください。
    // generateRecipe.groovy
    
    // Add all vertices and edges for Recipe
    g.V().drop().iterate()
    
    // author vertices
    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 vertices
    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 vertices
    beefBourguignon = graph.addVertex(label, 'recipe', 'name', 'Beef Bourguignon', 'instructions', 'Braise the beef. Saute the onions and carrots. Add wine and cook in a dutch oven at 425 degrees for 1 hour.')
    ratatouille = graph.addVertex(label, 'recipe', 'name', 'Rataouille', 'instructions', 'Peel and cut the eggplant. Make sure you cut eggplant into lengthwise slices that are about 1-inch wide, 3-inches long, and 3/8-inch thick')
    saladeNicoise = graph.addVertex(label, 'recipe', 'name', 'Salade Nicoise', 'instructions', 'Take a salad bowl or platter and line it with lettuce leaves, shortly before serving. Drizzle some olive oil on the leaves and dust them with salt.')
    wildMushroomStroganoff = graph.addVertex(label, 'recipe', 'name', 'Wild Mushroom Stroganoff', 'instructions', 'Cook the egg noodles according to the package directions and keep warm. Heat 1 1/2 tablespoons of the olive oil in a large saute pan over medium-high heat.')
    spicyMeatloaf = graph.addVertex(label, 'recipe', 'name', 'Spicy Meatloaf', 'instructions', 'Preheat the oven to 375 degrees F. Cook bacon in a large skillet over medium heat until very crisp and fat has rendered, 8-10 minutes.')
    oystersRockefeller = graph.addVertex(label, 'recipe', 'name', 'Oysters Rockefeller', 'instructions', 'Saute the shallots, celery, herbs, and seasonings in 3 tablespoons of the butter for 3 minutes. Add the watercress and let it wilt.')
    carrotSoup = graph.addVertex(label, 'recipe', 'name', 'Carrot Soup', 'instructions', 'In a heavy-bottomed pot, melt the butter. When it starts to foam, add the onions and thyme and cook over medium-low heat until tender, about 10 minutes.')
    roastPorkLoin = graph.addVertex(label, 'recipe', 'name', 'Roast Pork Loin', 'instructions', 'The day before, separate the meat from the ribs, stopping about 1 inch before the end of the bones. Season the pork liberally inside and out with salt and pepper and refrigerate overnight.')
    
    // ingredients vertices
    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 vertices
    // timestamp can also be entered as '2015-01-01' without Instant.parse()
    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 edges
    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 edges
    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 edges
    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 edges
    beefBourguignon.addEdge('includedIn', artOfFrenchCookingVolOne)
    saladeNicoise.addEdge('includedIn', artOfFrenchCookingVolOne)
    carrotSoup.addEdge('includedIn', artOfSimpleFood)
    
    // meal - recipe edges
    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 edges
    EverydayDinner.addEdge('includedIn', artOfSimpleFood)
    SaturdayFeast.addEdge('includedIn', simcasCuisine)
    JuliaDinner.addEdge('includedIn', artOfFrenchCookingVolOne)
    g.V()
    図: レシピ・サンプル・グラフのデータ

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

    スクリプトの最後にあるg.V()コマンドは、作成されたすべての頂点を表示します。

  2. 頂点カウントを実行すると、頂点のカウント数が56に増えます。頂点カウントを再実行します。
    g.V().count()

    データを読み込むためのスクリプト記述には、DSE Graph Loaderが便利です。これは、データの読み込みに推奨される方法です。

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

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

    DataStax Studioでは、[Raw]ビューエッジ情報のリストか、

    頂点を横切ると詳細情報が表示される、視覚化された[Graph]ビューのグラフが表示されます。

  3. すべての著者が書いた本に対してクエリーを実行する場合は、クエリーを変更する必要があります。前の例ではエッジを取得しましたが、隣接するbook頂点は取得しませんでした。探索ステップinV()を追加して、外向きエッジにつながるすべての頂点を見つけ、さらにこれらの頂点の本の名前を出力します。探索ステップを連結して、V().outE().inV()を使用して頂点から外向きエッジを経由して隣接頂点に移動しているところに注目してください。外向きエッジには、具体的なフィルター値のauthoredが割り当てられています。
    g.V().outE('authored').inV().values('name')
  4. 本の名前は、生成リストに重複して表示されます。これは、著者ごとにリストが返されるためです。本に3人の著者が存在する場合は、3つのリストが返されます。探索ステップdedup()を使用すると、重複を排除できます。
    g.V().outE('authored').inV().values('name').dedup()
  5. 特定の著者についてhas()ステップを再び挿入して探索を絞り込みます。Julia Childが書いたすべての本を見つけます。
    g.V().has('name','Julia Child').outE('authored').inV().values('name')
  6. 前の例とこの例で返される結果は同じです。ただし、探索ステップの数と探索ステップのタイプがパフォーマンスに影響する可能性があります。探索ステップoutE()は、エッジが明示的に必要な場合にのみ使用してください。この例では、エッジを探索して、接続されている頂点に関する情報を取得しますが、クエリーにとってこのエッジ情報は重要ではありません。
    g.V().has('name','Julia Child').out('authored').values('name')

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

  7. 探索ステップを追加するたびに、結果が微調整されていきます。別の連結されたhas探索ステップを追加すると、Julia Childによって書かれ、1967年以降に出版された本のみが検出されます。この例では、gt、つまり大なり関数を使用していることも示されています。
    g.V().has('name','Julia Child').out('authored').has('year', gt(1967)).values('name')
  8. 開発またはテスト時は、各頂点ラベルを持つ頂点の数をチェックすると、そのデータが読み込まれたことを確認できる場合がよくあります。頂点ラベルごとに頂点の数を見つけるには、探索ステップlabel()の後ろに探索ステップgroupCount()を続けたものを使用します。ステップgroupCount()は、前のステップで得た結果を集計するのに便利です。
    g.V().label().groupCount()
  9. 情報を保存または交換するには、出力ファイルにデータを書き込みます。Gryoファイルは、データをDSE Graphに再読み込みできるバイナリー形式ファイルです。次のコマンドのグラフI/Oは、グラフ全体をファイルに書き込みます。gryo()graphml()またはgraphson()で置き換えることによって、他のファイル形式に書き込むことができます。
    graph.io(gryo()).writeGraph("/tmp/recipe.gryo")
    注: graph.io()はサンドボックス・モードでは無効です。
  10. Gryoファイルを読み込むには、マッピング・スクリプトの作成後、graphloaderを使用します。
    $ graphloader mappingGRYO.groovy -graph recipe -address localhost
    Gryoデータの読み込みについては、「DSE Graph Loaderの使用」の「Gryoデータの読み込み」を参照してください。
  11. Studioで作業した後にGremlin Consoleを使用する場合は、以下の2つのコマンドが便利です。すべてのグラフのリストを取得するにはsystem.graphs()を使用し、別のグラフに切り換えるにはremote config alias g some_graph.gを使用します。

次のタスク

お疲れさまでした。これで、DSE Graphを使用してデータ探索できるようになりました。

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