【问题标题】:How to traverse a graph with ArangoDB and PHP如何使用 ArangoDB 和 PHP 遍历图
【发布时间】:2013-03-01 12:10:27
【问题描述】:

ArangoDB 提供文档和边作为生成图的低级方式。

假设我们已经建立了一个包含一些顶点和边的图。 图表示顶点之间的关系。

v2 是 v1 的子代
v3 是 v2 的子代
v4 是 v3 的子版本
v5 是 v1 的子代
v6 是 v5 的子节点

我们希望能够查询:

  1. v4 到 v1 的路径
  2. v1 的所有后代
  3. v1 的孩子
  4. v4 的所有祖先
  5. v4 的父母

如何在 PHP 中做到这一点?

【问题讨论】:

    标签: arangodb arangodb-php


    【解决方案1】:

    这样做的方法是使用 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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多