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);