今回はエッジと呼ばれるタイプのドキュメントを紹介して、グラフを扱ってみます。エッジドキュメントは名前の通り、2つのドキュメントを関連付けるためのドキュメントで、向きを持ちます。
エッジコレクション
コレクションにはタイプと呼ばれる属性があります。たとえば、前回まで作成したコレクションのタイプはすべてdocumentです。このタイプのコレクションには、エッジドキュメントを入れることはできません。
1 2 |
arangosh> db.cities [ArangoCollection 2946001642, "cities" (type document, status loaded)] |
エッジドキュメント入れるための専用のコレクションをあらたに作成する必要があります。type属性がdocumentではなく、edgeになっていることが確認できます。
1 2 |
db._createEdgeCollection("paths"); [ArangoCollection 266153388778, "paths" (type edge, status loaded)] |
エッジドキュメント
前回作成したcitiesコレクションの”Tokyo”と”Osaka”を関連付けるエッジドキュメント作成します。エッジドキュメントのtime属性は、移動に必要な時間のつもりですが、正しい値ではありません。あしからず。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
arangosh> tokyo = db.cities.document("16332843754"); { "locId" : 204198, "latitude" : 35.685, "longitude" : 139.7514, "country" : "JP", "region" : "40", "city" : "Tokyo", "postalCode" : "105-0003", "_id" : "cities/16332843754", "_rev" : "16332843754", "_key" : "16332843754" } arangosh> osaka = db.cities.document("16332909290"); { "locId" : 204199, "latitude" : 34.6667, "longitude" : 135.5, "country" : "JP", "region" : "32", "city" : "Osaka", "postalCode" : "530-0001", "_id" : "cities/16332909290", "_rev" : "16332909290", "_key" : "16332909290" } arangosh> p1 = db.paths.save(tokyo, osaka, {time:155}); { "error" : false, "_id" : "paths/266154765034", "_rev" : "266154765034", "_key" : "266154765034" } |
作成したエッジドキュメントを見ると、通常のドキュメントにはない(内部)属性_fromと_toがあり、その値は指定したドキュメントの_idになっています。
1 2 3 4 5 6 7 8 9 10 11 |
arangosh> db.paths.all().toArray(); [ { "_id" : "paths/266154765034", "_rev" : "266154765034", "_key" : "266154765034", "_from" : "cities/16332843754", "_to" : "cities/16332909290", "time" : 155 } ] |
エッジドキュメントは、ドキュメント間にいくつでも作成できますし、始点と終点が同じ同一のドキュメントであっても作成可能です。
検索
通常のドキュメントをノード、エッジドキュメントをエッジとして有向グラフを作成することができます。ノードとエッジには属性(ラベル)を付けることもできます。
“Tokyo”から”Osaka”へのエッジを作りましたが、いくつかエッジを追加して以下のようなグラフを作ります。
AQLを使っていくつか検索してみます。
ある都市(ノード)から直接移動できる都市を検索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
arangosh> stmt = db._createStatement({ "query" : "FOR c IN PATHS(cities, paths, 'outbound') " + "FILTER c.source._id == @id && LENGTH(c.edges) == 1 " + "RETURN c.vertices[*].city"}); [object ArangoStatement] arangosh> stmt.bind("id", tokyo._id); arangosh> cursor = stmt.execute(); [object ArangoQueryCursor] arangosh> while(cursor.hasNext()) { print(cursor.next()); } [ "Tokyo", "Osaka" ] [ "Tokyo", "Nagoya" ] [ "Tokyo", "Sapporo" ] [ "Tokyo", "Fukuoka" ] arangosh> stmt.bind("id", fukuoka._id); arangosh> cursor = stmt.execute(); [object ArangoQueryCursor] arangosh> while(cursor.hasNext()) { print(cursor.next()); } [ "Fukuoka", "Tokyo" ] |
200分以内で直接移動できる都市を検索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
arangosh> stmt = db._createStatement({ "query" : "FOR c IN PATHS(cities, paths, 'outbound') " + "FILTER c.source._id == @id && LENGTH(c.edges) == 1 && c.edges[0].time < 200 " + "RETURN c.vertices[*].city"}); [object ArangoStatement] arangosh> stmt.bind("id", tokyo._id); arangosh> cursor = stmt.execute(); [object ArangoQueryCursor] arangosh> while(cursor.hasNext()) {print(cursor.next());} [ "Tokyo", "Osaka" ] [ "Tokyo", "Nagoya" ] |
2都市間の移動経路を検索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
arangosh> stmt = db._createStatement({ "query" : "FOR c IN PATHS(cities, paths, 'outbound') " + "FILTER c.source._id == @src && c.destination._id == @dest " + "RETURN c.vertices[*].city"}); [object ArangoStatement] arangosh> stmt.bind({"src": tokyo._id, "dest":nagoya._id}); arangosh> cursor = stmt.execute(); [object ArangoQueryCursor] arangosh> while(cursor.hasNext()) { print(cursor.next()); } [ "Tokyo", "Osaka", "Nagoya" ] [ "Tokyo", "Nagoya" ] |
経路情報も比較的簡単なクエリで検索できます。