単純な探索

単純な探索でも複雑な場合がありますが、再帰や分岐などの特殊な手法は採用しません。

レシピ・サンプル・グラフに戻り、グラフを拡張してレビュー担当者と格付けを含めましょう。次のスクリプトを読み込んでreviewer(レビュー担当者)頂点とrecipe-reviewer(レシピとレビュー担当者間)辺を追加します。このスクリプトを読み込む前にRecipe(レシピ)頂点が存在するように、generateRecipe.groovyスクリプトを実行しておく必要があります。
// レシピ・サンプル・グラフのreview(レビュー)頂点と辺を生成
// :load /tmp/generateReviews.groovy

// reviewer(レビュー担当者)頂点
johnDoe = graph.addVertex(label, 'reviewer', 'name','John Doe')
johnSmith = graph.addVertex(label, 'reviewer', 'name', 'John Smith')
janeDoe = graph.addVertex(label, 'reviewer', 'name','Jane Doe')
sharonSmith = graph.addVertex(label, 'reviewer', 'name','Sharon Smith')
betsyJones = graph.addVertex(label, 'reviewer', 'name','Betsy Jones')

beefBourguignon = g.V().has('recipe', 'name','Beef Bourguignon').tryNext().orElseGet {graph.addVertex(label, 'recipe', 'name', 'Beef Bourguignon')}
spicyMeatLoaf = g.V().has('recipe', 'name','Spicy Meatloaf').tryNext().orElseGet {graph.addVertex(label, 'recipe', 'name', 'Spicy Meatloaf')}
carrotSoup = g.V().has('recipe', 'name','Carrot Soup').tryNext().orElseGet {graph.addVertex(label, 'recipe', 'name', 'Carrot Soup')}

// reviewer - recipe間の辺
johnDoe.addEdge('rated', beefBourguignon, 'timestamp', '2014-01-01T05:15:00.00Z', 'stars', 5, 'comment', 'Pretty tasty!')
johnSmith.addEdge('rated', beefBourguignon, 'timestamp', '2014-01-23T00:00:00.00Z', 'stars', 4)
janeDoe.addEdge('rated', beefBourguignon, 'timestamp', '2014-02-01T00:00:00.00Z', 'stars', 5, 'comment', 'Yummy!')
sharonSmith.addEdge('rated', beefBourguignon, 'timestamp', '2015-01-01T00:00:00.00Z', 'stars', 3, 'comment', 'It was okay.')
johnDoe.addEdge('rated', spicyMeatLoaf, 'timestamp', '2015-12-31T10:56:00.00Z', 'stars', 4, 'comment', 'Really spicy - be careful!')
sharonSmith.addEdge('rated', spicyMeatLoaf, 'timestamp', '2014-07-23T00:30:00.00Z', 'stars', 3, 'comment', 'Too spicy for me. Use less garlic.')
janeDoe.addEdge('rated', carrotSoup, 'timestamp', '2015-12-30T01:20:00.00Z', 'stars', 5, 'comment', 'Loved this soup!Yummy vegetarian!')
まずスクリプトを特定してからリモートでそれを実行することにより、スクリプトを実行します。
gremlin> :load /tmp/generateReviews.groovy

前に入力したレシピがクエリーされ、レシピ変数に結果が割り当てられます。その後、変数を使用してreviewer-recipe間に辺が作成されます。これらのクエリーでは、Apache TinkerpopメソッドtryNext()およびorElseGet()を使用します。詳細については、Apache Tinkerpop Java APIを参照してください。

レシピ格付けの探索

頂点がreviewer(レビュー担当者)ラベルの付いた頂点の数をカウントすることによって作成されたかどうかを確認します。
gremlin>  g.V().hasLabel('reviewer').count()
==>5
valuesを使用してすべてのレビュー担当者をリストします:
// すべてのレビュー担当者の名前を取得 
gremlin> g.V().hasLabel('reviewer').values('name')
==>John Smith
==>Sharon Smith
==>Betsy Jones
==>Jane Doe
==>John Doe

レビュー担当者が作成されたかどうかを確認することは有益ですが、クエリーに応える探索を作成することの方が重要です。たとえば、John Doeはレシピについて何と言っていますか。

nameの値がJohn Doeであるreviewerとして頂点のラベルを特定するクエリーを使用します。
g.V().has('reviewer', 'name','John Doe').outE('rated').values('comment')
John Doeが格付けたすべてのレシピを検出するために外向き辺コマンドoutE('rated')を使用すると、プロパティcommentsの値を取得できます。
==>Pretty tasty!
==>Really spicy - be careful!

John Doeがどのレシピをレビューしたかを知っておくと別の探索を使用できるので、便利です。

g.V().has('reviewer', 'name','John Doe').outE('rated').inV().values('name')
結果:
==>Beef Bourguignon
==>Spicy Meatloaf
レシピを3つ星以上として評価をしたすべてのレビューを検出することは妥当です。gt(3)またはgreater than 3を使用してstarsの値をフィルターしてみてください。
gremlin> g.E().hasLabel('rated').has('stars', gt(3)).valueMap()
==>[stars:4, timestamp:2014-01-23T00:00:00Z]
==>[comment:Loved this soup!Yummy vegetarian!, stars:5, timestamp:2015-12-30T00:00:00Z]
==>[comment:Yummy!, stars:5, timestamp:2014-02-01T00:00:00Z]
==>[comment:Pretty tasty!, stars:5, timestamp:2014-01-01T00:00:00Z]
==>[comment:Really spicy - be careful!, stars:4, timestamp:2015-12-31T00:00:00Z]
ここに示した探索では、ratedというラベルが付けられた各辺を検出し、4または5のstar格付けの辺のみを出力する辺をフィルターします。しかし、この探索では元の質問に対する答えは出力されません。この探索は、inV()によって内向き頂点を取得し、values('name')によってそれらの内向き頂点を名前でリストするように変更する必要があります。
gremlin> g.E().hasLabel('rated').has('stars', gt(3)).inV().values('name')
==>Beef Bourguignon
==>Spicy Meatloaf
==>Beef Bourguignon
==>Carrot Soup
==>Beef Bourguignon
リストにはレビュー担当者の情報はまったくなく、レシピ・タイトルの複製だけですが、結果はBeef Bourguignonが3回格付けされたことを示しています。
前のクエリーに戻って、より最近のレビューを探してみましょう。timestampでフィルターするために探索ステップを1つ追加すると、gte(4)またはgreater than or equal to 4を使用して、レビュー日が2015年1月1日以降の4つ星および5つ星の格付けを検出することができます。
gremlin>  g.E().hasLabel('rated').has('stars',gte(4)).has('timestamp', gte(Instant.parse('2015-01-01T00:00:00.00Z'))).valueMap()
==>[comment:Loved this soup!Yummy vegetarian!, timestamp:2015-12-30T00:00:00Z, stars:5]
==>[comment:Really spicy - be careful!, timestamp:2015-12-31T00:00:00Z, stars:4]
探索ステップを連結すると、非常に正確な結果が得られます。たとえば、最後のクエリーにinV().values('name')を追加した場合、結果を絞り込んで2015年の年頭以降すべての4〜5つ星レビューを検出します。
統計関数で格付けを操作すると、興味深い答えが得られます。たとえば、すべてのレシピ格付けの平均値は何でしょう。
gremlin>  g.E().hasLabel('rated').values('stars').mean()
==>4.142857142857143
結果は、レビュー担当者はそれぞれ好んだレシピをレビューしていることを示しており、このサンプルではレビュー担当者は好ましいと思わなかったレシピのレビューを書かなかったことが立証されます。
パワー・レビュー担当者であれば、より広範にレビューすることでしょう。単一のレビュー担当者が書いた最大数のレビューを見つけます。
gremlin>  g.V().hasLabel('reviewer').map(outE('rated').count()).max()
==>2
この探索では、各レビュー担当者のoutE('rated')を使用してすべての外向き辺をマッピングしてカウントしてから、max()を使用してカウントの最高値を特定します。

調査の対象とすることのできるもう1つの測定値は各レビュー担当者の平均格付けです。この探索クエリーでは、複数のApache TinkerPop探索ステップを使用します。

as()ステップでは、2つの項目に対して表示ラベルを作成できます。レビュー担当者の名前と、各レビュー担当者の平均stars値のリストです。これらの表示ラベルreviewerおよびstarCountは、その後select()ステップで使用され、各値(まずby('name')を使用してレビュー担当者の名前、次にby(outE('rated').values('stars').mean()を使用してstarCount)が取得されます。select()ステップでは、各reviewer頂点を確認してから、探索して関連するstarCount値を検出します。
gremlin>  g.V().hasLabel('reviewer').as('reviewer','starCount').
select('reviewer','starCount').
by('name').
by(outE('rated').values('stars').mean())
==>[reviewer:Jane Doe, starCount:5.0]
==>[reviewer:Betsy Jones, starCount:NaN]
==>[reviewer:John Doe, starCount:4.5]
==>[reviewer:John Smith, starCount:4.0]
==>[reviewer:Sharon Smith, starCount:3.0]
Betsy Jonesはレビュー担当者としてリストされていますが、レシピをどれもレビューしていないことに注目してください。このレビュー担当者のstarCountにはNaN(数値ではない)がリストされています。Jane Doeは少なくとも1つのレシピがとても気に入りましたが、Sharon Smithはそのレシピを好まないことが結果からわかります。
starCount、または星格付け平均で結果を順序付けると、最高格付けと最低格付けのレシピを確認することができます。ここでは、探索ステップorder().by(select('starCount').decr()select('starCount')ステップの出力を使用して、降順に表示します。
gremlin>  g.V().hasLabel('reviewer').as('reviewer','starCount').
select('reviewer','starCount').
by('name').
by(outE('rated').values('stars').mean()).
order().by(select('starCount'), decr)
==>[reviewer:Betsy Jones, starCount:NaN]
==>[reviewer:Jane Doe, starCount:5.0]
==>[reviewer:John Doe, starCount:4.5]
==>[reviewer:John Smith, starCount:4.0]
==>[reviewer:Sharon Smith, starCount:3.0]
Betsy Jonesと、その格付けの欠如によって、リストは不正確になります。Betsyがリストされていなければ、探索に探索ステップlimit(1)を追加して、最高格付けのJane Doeを取得することができます。
NaNを0値に変更するには、扱いにくい探索ステップのcoalesce()を使用します。
gremlin>  g.V().hasLabel('reviewer').as('reviewer','starCount').
select('reviewer','starCount').
by('name').
by(coalesce(outE('rated').values('stars'),constant(0)).mean()).
order().by(select('starCount'), decr)
==>[reviewer:Jane Doe, starCount:5.0]
==>[reviewer:John Doe, starCount:4.5]
==>[reviewer:John Smith, starCount:4.0]
==>[reviewer:Sharon Smith, starCount:3.0]
==>[reviewer:Betsy Jones, starCount:0.0]
これでBetsy JonesのstarCount0.0となり、真の値となります。
レシピに書くレビュー担当者が与えた星格付けを見つけます。
g.V().hasLabel('reviewer').as('reviewer','rating').out().as('recipe').
select('reviewer','rating','recipe').
by('name').
by(outE('rated').values('stars')).
by(values('name'))
reviewer 頂点からレビュー担当者と格付けにas('reviewer','rating')によってラベルが付けられた後で、レシピ名が探索され、その名前にステップ・モジュレータas('recipe')が付けられていることに注目してください。出力リスト内の最初の2つの項目は、reviewer頂点で始まり取得されていますが、3番目の項目は隣接するrecipe頂点から取得されています。
==>{reviewer=John Doe, rating=5, recipe=Beef Bourguignon}
==>{reviewer=John Doe, rating=5, recipe=Spicy Meatloaf}
==>{reviewer=John Smith, rating=4, recipe=Beef Bourguignon}
==>{reviewer=Jane Doe, rating=5, recipe=Beef Bourguignon}
==>{reviewer=Jane Doe, rating=5, recipe=Carrot Soup}
==>{reviewer=Sharon Smith, rating=3, recipe=Beef Bourguignon}
==>{reviewer=Sharon Smith, rating=3, recipe=Spicy Meatloaf}
一般的に、レビューの最も興味深い統計が、特定のレシピをレビューしたレビュー担当者の数や、特定のレシピの平均格付けなどに関する質問に答えています。今回は、グラフ探索はrecipe頂点から始まり、レシピ名を取得し、さらにinE('rated').count()で内向き辺をカウントしてレビュー数を、inE('rated').values('stars').mean()を使用して内向き辺の平均を取得します。前述のcoalesce()ステップを、meanRatingのすべてのNaN値を0に変更するために使用できます。
g.V().hasLabel('recipe').as('recipe','numberOfReviews','meanRating').
select('recipe','numberOfReviews','meanRating').
by('name').
by(inE('rated').count()).
by(inE('rated').values('stars').mean())
==>{recipe=Beef Bourguignon, numberOfReviews=4, meanRating=4.25}
==>{recipe=Wild Mushroom Stroganoff, numberOfReviews=0, meanRating=NaN}
==>{recipe=Spicy Meatloaf, numberOfReviews=2, meanRating=3.5}
==>{recipe=Rataouille, numberOfReviews=0, meanRating=NaN}
==>{recipe=Salade Nicoise, numberOfReviews=0, meanRating=NaN}
==>{recipe=Roast Pork Loin, numberOfReviews=0, meanRating=NaN}
==>{recipe=Oysters Rockefeller, numberOfReviews=0, meanRating=NaN}
==>{recipe=Carrot Soup, numberOfReviews=1, meanRating=5.0}

レシピの検索

レシピの一般的なクエリーでは、特定の材料を含むレシピを検出します。
g.V().hasLabel('recipe').out().has('name','beef').in().hasLabel('recipe').values('name')
==>Beef Bourguignon
変更を加えることで、いずれか一方の材料を含むクエリーを実行することができます。
g.V().hasLabel('recipe').out().has('name',within('beef','carrots')).in().hasLabel('recipe').values('name')
==>Beef Bourguignon
==>Carrot Soup
特定のレシピのすべての材料の検索は一般的なクエリーです。
g.V().match(
__.as('a').hasLabel('ingredient'),
__.as('a').in('includes').has('name','Beef Bourguignon')).
select('a').by('name')
このクエリーでは、match()ステップを使用してBeef Bourguignon(牛肉のブルゴーニュ風煮込み)を作るために使用する材料に一致するものを検出します。この探索では、材料を見つけるためにすべての頂点をフィルターすることから開始し、in('includes')を使用してincludes辺に沿って探索します。さらにこのクエリーはGroovyダブル・アンダースコア変数をmatchメソッドのプライベート変数として使用します。次のように表示されます。
==>tomato paste
==>beef
==>onion
==>mashed garlic
==>butter

出力のグループ化

group()探索ステップを使用してグラフ探索からの出力をグループ化します。たとえば、ラベルでグループ化して、すべての頂点を名前別に表示します。
g.V().group().by(label).by('name')
結果では、食事、材料、著者、本、レシピ、およびレビュー担当者はすべてグループにまとめられています。
==>[meal:[JuliaDinner, Saturday Feast, EverydayDinner], ingredient:[olive oil, chicken broth, 
eggplant, pork sausage, green bell pepper, yellow onion, celery, hard-boiled egg, shallots, 
zucchini, butter, green beans, mashed garlic, onion, mushrooms, bacon, parsley, oyster, 
tomato, thyme, pork loin, tuna, tomato paste, ground beef, red wine, fennel, Pernod, 
chervil, egg noodles, carrots, beef], author:[Louisette Bertholie, Kelsie Kerr, 
Alice Waters, Julia Child, Emeril Lagasse, Simone Beck, Patricia Curtan, Patricia Simon, 
James Beard, Fritz Streiff], book:[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, The Art of French Cooking, Vol. 1], recipe:[Wild Mushroom 
Stroganoff, Roast Pork Loin, Spicy Meatloaf, Rataouille, Beef Bourguignon, Oysters 
Rockefeller, Salade Nicoise, Carrot Soup], reviewer:[Sharon Smith, John Smith, Jane Doe, 
Betsy Jones, John Doe]]
もう1つの例では、すべての本が年度でグループ化されており、本が出版された各年度、その後に本のタイトルが続くリストが表示されます。
g.V().hasLabel('book').group().by('year').by('name')
and lists:
==>{1968=[The French Chef Cookbook, The French Chef Cookbook], 
1972=[Simca's Cuisine:100 Classic French Recipes for Every Occasion, Simca's Cuisine: 100 
Classic French Recipes for Every Occasion], 2007=[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], 1961=[The Art of French Cooking, Vol. 1, The Art of French 
Cooking, Vol. 1]}

local()を使用した処理のためのグループ化

グラフ探索での特定のステップのためにローカル処理を行うことが重要であることがよくあります。次の2つの例では、limit()コマンドを使用してlocal()で、クエリーに入るストリーム全体から、クエリーの一部へと処理を変更する方法を示しています。まず、2人の著者と、その著者たちが本を出版した年度を見つけます。
g.V().hasLabel('author').as('author').out().properties('year').as('year').
select('author','year').
by('name').
by().
limit(2)
このクエリーは、データベース内の最初の2つのレコードが返されます。
==>{author=Julia Child, year=vp[year->1961]}
==>{author=Julia Child, year=vp[year->1968]}
local()を使用して、グラフ内の各著者が出版した最初の2冊の本を見つけるようにこのクエリーを変更します。
g.V().hasLabel('author').as('author').
local(out().properties('year').as('year').limit(2)).
select('author','year').
by('name').
by()
著者ごとに最大2冊までの本が表示されることに注意してください。
==>{author=Julia Child, year=vp[year->1961]}
==>{author=Julia Child, year=vp[year->1968]}
==>{author=Simone Beck, year=vp[year->1961]}
==>{author=Simone Beck, year=vp[year->1972]}
==>{author=Louisette Bertholie, year=vp[year->1961]}
==>{author=Patricia Simon, year=vp[year->1972]}
==>{author=Alice Waters, year=vp[year->2007]}
==>{author=Patricia Curtan, year=vp[year->2007]}
==>{author=Kelsie Kerr, year=vp[year->2007]}
==>{author=Fritz Streiff, year=vp[year->2007]}
探索ステップlocal()には、次の処理に進む前に結果を返す、グラフ探索内のグラフのサブセクションの処理において多くの用途があります。