ArangoDBは、グラフを作成するための低レベルの方法としてドキュメントとエッジを提供します。
いくつかの頂点とエッジを持つグラフを設定したとしましょう。グラフは、頂点間の関係を表しています。
v2はv1の子です
v3はv2の子です
v4はv3の子です
v5はv1の子です
v6はv5の子です
クエリできるようにしたい:
- v4からv1へのパス
- v1のすべての子孫
- v1の子
- v4のすべての祖先
- v4の親
PHPでそれを行う方法は?
ArangoDBは、グラフを作成するための低レベルの方法としてドキュメントとエッジを提供します。
いくつかの頂点とエッジを持つグラフを設定したとしましょう。グラフは、頂点間の関係を表しています。
v2はv1の子です
v3はv2の子です
v4はv3の子です
v5はv1の子です
v6はv5の子です
クエリできるようにしたい:
PHPでそれを行う方法は?
これを行う方法は、AQL(ArangoDBクエリ言語)を使用してArangoDBにクエリを実行することです。
AQLでグラフを操作するためのコマンドがいくつかあります。PHPでは、クエリを使用してステートメントを作成し、それを実行する必要があります。
添付のコードは、質問で言及されたクエリに結果を提供するためにTRAVERSAL関数を使用します。
このスクリプトは、ドキュメントとエッジコレクションを設定し、頂点と接続エッジで埋め、最後にクエリを実行して結果を提供します。
そのまま実行でき、すべての結果が出力されます。
<?php
namespace triagens\ArangoDb;
// use this and change it to the path to autoload.php of the arangodb-php client if you're using the client standalone...
// require __DIR__ . '/../vendor/triagens/ArangoDb/autoload.php';
// ...or use this and change it to the path to autoload.php in the vendor directory if you're using Composer/Packagist
require __DIR__ . '/../vendor/autoload.php';
// This function will provide us with our pre-configured connection options.
function getConnectionOptions()
{
$traceFunc = function ($type, $data) {
print "TRACE FOR " . $type . PHP_EOL;
};
return array(
ConnectionOptions::OPTION_ENDPOINT => 'tcp://localhost:8529/',
// endpoint to connect to
ConnectionOptions::OPTION_CONNECTION => 'Close',
// can use either 'Close' (one-time connections) or 'Keep-Alive' (re-used connections)
ConnectionOptions::OPTION_AUTH_TYPE => 'Basic',
// use basic authorization
/*
ConnectionOptions::OPTION_AUTH_USER => '', // user for basic authorization
ConnectionOptions::OPTION_AUTH_PASSWD => '', // password for basic authorization
ConnectionOptions::OPTION_PORT => 8529, // port to connect to (deprecated, should use endpoint instead)
ConnectionOptions::OPTION_HOST => "localhost", // host to connect to (deprecated, should use endpoint instead)
*/
ConnectionOptions::OPTION_TIMEOUT => 5,
// timeout in seconds
//ConnectionOptions::OPTION_TRACE => $traceFunc, // tracer function, can be used for debugging
ConnectionOptions::OPTION_CREATE => false,
// do not create unknown collections automatically
ConnectionOptions::OPTION_UPDATE_POLICY => UpdatePolicy::LAST,
// last update wins
);
}
// This function tries to create vertices and edges for the example
function setupVerticesAndEdges($connection, $vertexCollection, $edgeCollection)
{
echo "We are creating 6 vertices...<br> ";
//create example documents for the vertices
$nameV1 = 'v1';
$documentV1 = Document::createFromArray(
array('name' => $nameV1, '_key' => $nameV1)
);
$nameV2 = 'v2';
$documentV2 = Document::createFromArray(
array('name' => $nameV2, '_key' => $nameV2)
);
$nameV3 = 'v3';
$documentV3 = Document::createFromArray(
array('name' => $nameV3, '_key' => $nameV3)
);
$nameV4 = 'v4';
$documentV4 = Document::createFromArray(
array('name' => $nameV4, '_key' => $nameV4)
);
$nameV5 = 'v5';
$documentV5 = Document::createFromArray(
array('name' => $nameV5, '_key' => $nameV5)
);
$nameV6 = 'v6';
$documentV6 = Document::createFromArray(
array('name' => $nameV6, '_key' => $nameV6)
);
echo "We are creating 5 edges...<br> ";
//create example documents for the edges
$nameE1 = 'e1';
$documentE1 = Edge::createFromArray(
array('name' => $nameE1, 'label' => 'child_of')
);
$nameE2 = 'e2';
$documentE2 = Edge::createFromArray(
array('name' => $nameE2, 'label' => 'child_of')
);
$nameE3 = 'e3';
$documentE3 = Edge::createFromArray(
array('name' => $nameE3, 'label' => 'child_of')
);
$nameE4 = 'e4';
$documentE4 = Edge::createFromArray(
array('name' => $nameE4, 'label' => 'child_of')
);
$nameE5 = 'e5';
$documentE5 = Edge::createFromArray(
array('name' => $nameE5, 'label' => 'child_of')
);
// Get instances of the vertice- and edgehandlers
$documentHandler = new DocumentHandler($connection);
$edgeHandler = new EdgeHandler($connection);
// Save the vertices
try {
// query the given $collectionId by example using the previously declared $exampleDocument array
$result['v'][] = $documentHandler->save($vertexCollection, $documentV1);
$result['v'][] = $documentHandler->save($vertexCollection, $documentV2);
$result['v'][] = $documentHandler->save($vertexCollection, $documentV3);
$result['v'][] = $documentHandler->save($vertexCollection, $documentV4);
$result['v'][] = $documentHandler->save($vertexCollection, $documentV5);
$result['v'][] = $documentHandler->save($vertexCollection, $documentV6);
} catch (Exception $e) {
// any other error
echo ('An error occured. Exception: ' . $e);
}
// Save the edges
try {
echo "$nameV2 is a child of $nameV1<br> ";
$result['e'][] = $edgeHandler->saveEdge(
$edgeCollection,
$vertexCollection . '/' . $nameV2,
$vertexCollection . '/' . $nameV1,
$documentE1,
$options = array()
);
echo "$nameV3 is a child of $nameV2<br> ";
$result['e'][] = $edgeHandler->saveEdge(
$edgeCollection,
$vertexCollection . '/' . $nameV3,
$vertexCollection . '/' . $nameV2,
$documentE2,
$options = array()
);
echo "$nameV4 is a child of $nameV3<br> ";
$result['e'][] = $edgeHandler->saveEdge(
$edgeCollection,
$vertexCollection . '/' . $nameV4,
$vertexCollection . '/' . $nameV3,
$documentE3,
$options = array()
);
echo "$nameV5 is a child of $nameV1<br> ";
$result['e'][] = $edgeHandler->saveEdge(
$edgeCollection,
$vertexCollection . '/' . $nameV5,
$vertexCollection . '/' . $nameV1,
$documentE4,
$options = array()
);
echo "$nameV6 is a child of $nameV5<br> ";
$result['e'][] = $edgeHandler->saveEdge(
$edgeCollection,
$vertexCollection . '/' . $nameV6,
$vertexCollection . '/' . $nameV5,
$documentE5,
$options = array()
);
echo "<font style='font-family: monospace;'>";
echo "$nameV1<br> ";
echo "+ $nameV2<br> ";
echo "| + $nameV3<br> ";
echo "| | + $nameV4<br> ";
echo "+ $nameV5<br> ";
echo "+ $nameV5<br> ";
echo "| + $nameV6<br> ";
echo "</font>";
// return the result;
return $result;
} catch (Exception $e) {
// any other error
echo ('An error occured. Exception: ' . $e);
}
}
// helper function that takes the connection and the query to execute.
function doAQLQuery($connection, $query)
{
// query through AQL
$statement = new \triagens\ArangoDb\Statement($connection, array(
"query" => '',
"count" => true,
"batchSize" => 1000,
"_sanitize" => true,
));
$statement->setQuery($query);
$cursor = $statement->execute();
$result = $cursor->getAll();
return $result;
}
// AQL query example functions
// Function that gets all paths from vertex v4 to v1
function getPathFromV4ToV1($connection)
{
$query = 'FOR p IN PATHS(vertices_20130301_01, edges_20130301_01, "outbound")
FILTER p.source._id == "vertices_20130301_01/v4" && p.destination._id == "vertices_20130301_01/v1"
RETURN p';
$result = doAQLQuery($connection, $query);
return $result;
}
// Function that gets the paths to all descendants of v1
function getPathToAllDescendantsOfV1($connection)
{
$query = 'FOR p IN TRAVERSAL(vertices_20130301_01, edges_20130301_01, "vertices_20130301_01/v1", "inbound", {
strategy: "depthfirst",
minDepth:1,
paths: true,
followEdges: [ { label: "child_of" } ]
})
RETURN p
';
$result = doAQLQuery($connection, $query);
return $result;
}
// Function that gets the paths to all children of v1
function getPathToChildrenOfV1($connection)
{
$query = 'FOR p IN TRAVERSAL(vertices_20130301_01, edges_20130301_01, "vertices_20130301_01/v1", "inbound", {
strategy: "depthfirst",
maxDepth: 1,
minDepth:1,
paths: true,
followEdges: [ { label: "child_of" } ]
})
RETURN p
';
$result = doAQLQuery($connection, $query);
return $result;
}
// Function that gets the paths to all parents of v4
function getPathToParentsOfV4($connection)
{
$query = 'FOR p IN TRAVERSAL(vertices_20130301_01, edges_20130301_01, "vertices_20130301_01/v4", "outbound", {
strategy: "depthfirst",
maxDepth: 1,
minDepth:1,
paths: true,
followEdges: [ { label: "child_of" } ]
})
RETURN p
';
$result = doAQLQuery($connection, $query);
return $result;
}
// Function that gets the paths to all ancestor of v4
function getPathToAllAncestorsOfV4($connection)
{
$query = 'FOR p IN TRAVERSAL(vertices_20130301_01, edges_20130301_01, "vertices_20130301_01/v4", "outbound", {
strategy: "depthfirst",
minDepth:1,
paths: true,
followEdges: [ { label: "child_of" } ]
})
RETURN p
';
$result = doAQLQuery($connection, $query);
return $result;
}
// Function that drops collections given
function dropCollections($connection, $collections)
{
// register a collection handler to work with the 'users' collection
$collectionHandler = new CollectionHandler($connection);
echo "dropping collections...";
try {
foreach ($collections as $collection) {
$collectionHandler->drop($collection);
}
echo "dropped.<br>";
} catch (Exception $e) {
die ('Could not drop collection. Exception: ' . $e . '<br>');
}
}
// *********************************************************************************************************************
// Start example code
// register the connection to ArangoDB
$connection = new Connection(getConnectionOptions());
// register a collection handler to work with the 'users' collection
$collectionHandler = new CollectionHandler($connection);
// assign the collection names...
$vertexCollection = 'vertices_20130301_01';
$edgeCollection = 'edges_20130301_01';
// finally drop the collections...
// remark this line if you want to drop the collections by hand.
dropCollections($connection, array($vertexCollection, $edgeCollection));
// create the vertices and edges collections...
// remark those lines if you want to create the collection by hand.
echo "creating the '$vertexCollection' vertex collection...";
try {
$collection = new Collection();
$collection->setName($vertexCollection);
$collectionHandler->create($collection);
echo "created.<br>";
} catch (Exception $e) {
echo ('Could not create collection. Exception: ' . $e . '<br>');
}
echo "creating the '$edgeCollection' edge collection...";
try {
$collection = new Collection();
$collection->setName($edgeCollection);
$collection->setType(3);
$collectionHandler->create($collection);
echo "created.<br>";
} catch (Exception $e) {
echo ('Could not create collection. Exception: ' . $e . '<br>');
}
// setup our vertices and edges....
echo "trying to setup our vertices and edges... <br>";
$result = setupVerticesAndEdges($connection, $vertexCollection, $edgeCollection);
// AQL Examples
// get the path from vertex v4 to v1
$result = getPathFromV4ToV1($connection);
echo "<br>*****************************************<br>";
echo "get all paths from vertex v4 to v1<br>";
echo "<br>*****************************************<br>";
var_dump($result);
// get the paths to all descendants of v1
$result = getPathToAllDescendantsOfV1($connection);
echo "<br>*****************************************<br>";
echo "get the paths to all descendants of v1<br>";
echo "<br>*****************************************<br>";
var_dump($result);
//get the paths to all children of v1
$result = getPathToChildrenOfV1($connection);
echo "<br>*****************************************<br>";
echo "get the paths to all children of v1<br>";
echo "<br>*****************************************<br>";
var_dump($result);
// get the paths to all ancestors of v4
$result = getPathToAllAncestorsOfV4($connection);
echo "<br>*****************************************<br>";
echo "get the paths to all ancestors of v4<br>";
echo "<br>*****************************************<br>";
var_dump($result);
//get all paths to all parents of v4
$result = getPathToParentsOfV4($connection);
echo "<br>*****************************************<br>";
echo "get all paths to all parents of v4<br>";
echo "<br>*****************************************<br>";
var_dump($result);