Skip to content

Commit

Permalink
Documentation (#10)
Browse files Browse the repository at this point in the history
* Query clauses documentation

* documented statement clauses
documented part of the graph clauses

* graph traversal clauses
supported functions list
  • Loading branch information
LaravelFreelancerNL authored Aug 24, 2020
1 parent 92fbb76 commit 023278c
Show file tree
Hide file tree
Showing 14 changed files with 837 additions and 67 deletions.
113 changes: 95 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,109 @@
# FluentAQL

PHP query builder for the [ArangoDB](https://www.arangodb.com) Query Language ([AQL](https://www.arangodb.com/docs/stable/aql/)).
Fluent PHP query builder for [ArangoDB’s](https://www.arangodb.com) Query Language ([AQL](https://www.arangodb.com/docs/stable/aql/)).

> Version 0.1.x: DO NOT USE IN PRODUCTION YET
(badges)
## Table of contents
1. [Use Cases](#purpose)
2. [Requirements](#requirements)
3. [Installation](#installation)
4. [Usage](#usage)

## Installation
You may use composer to install FluentAQL:

``` composer require laravel-freelancer-nl/fluentaql ```
## Purpose
Using a query builder mainly makes the life of a programmer much easier. You can write cleaner code
and be quicker at it. Which of course comes at the cost of application speed.

*If you need bleeding edge speed you will need to write your own queries.*

The use of a query builder has both pros and cons. It is up to you to decide what you need.

**Cons:**
1) *Sacrificing speed*
3) You still need to understand ArangoDB, AQL and the 'schema' of your database.
2) Slight variations between the query builder API and the raw AQL output which may be confusing

### Version compatibility
**Pros**
1) Programmatically compose queries (e.g. search facets).
2) Easy query decomposition
3) Dry up your code.
4) Reduce AQL syntax bugs
5) Flexible expression input
6) IDE intellisense.

## Requirements
| FluentAQL | ArangoDB | PHP |
| :------------------ | :---------------- | :---------------- |
| 0.x.x | 3.5.x | 7.2 |
| 0.x | 3.x * | ^7.2 |

* ArangoDB regularly adds AQL functions and clauses in minor versions. So be sure to check the AQL documentation for the availability of specific features.

You can use FluentAQL with older versions of ArangoDB, but specific AQL features won't work depending on the version
you're using. The [official AQL documentation](https://www.arangodb.com/docs/stable/aql/) sometimes provides information on
supported versions for clauses, functions and expressions.
## Installation
The easiest way to install FluentAQL is through composer:
```
composer require laravel-freelancer-nl/fluentaql
```

## Usage
The Query Builder (QB) has fluent API where you create a new QueryBuilder object and chain AQL statements to create a query.
First fire up a new query builder then fluently add AQL clauses on top.
### Step 1: create a query builder:
```
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
...
$qb = new QueryBuilder();
```
### Step 2: compose your query:
*For Example:*
```
$qb->for('i', '1..100')->filter('i', '<', 50)->limit(10)->sort('i', 'desc')->return('i');
```

### Step 3: compile the query

```
$qb->get();
```
The query builder now contains the query, binds and collections (*) for you to send to ArangoDB through a client.
```
$query = $qb->query;
$binds = $qb->binds;
$collections = $qb->collections;
```
* Collections must be passed to ArangoDB in order to prevent deadlocks in certain cluster operations and transactions.

The generated AQL for this query is:
```
FOR i IN 1..100 FILTER i < 50 LIMIT 10 SORT i DESC RETURN i
```

## API
See the following pages on details for the API

- [Query clauses](docs/api/query-clauses.md): how to search, select, sort and limit data
- [Statement clauses](docs/api/statement-clauses.md): data manipulation & variable declaration
- [Graph clauses](docs/api/graph-clauses.md): graph traversals
- [Functions](docs/api/functions.md): a list of all supported AQL functions
- [Subqueries](docs/api/subqueries.md): how to create subqueries, joins etc.

### (Always) bind user input
No matter what, never trust user input and always bind it.
```
$qb->bind('your data')
```

Binds are registered in order and given an id. If you want to specify the bind name yourself you can add it:
```
$qb->bind('your data', 'your-bind-id')
```

### Getting the query, bindings and collection list
the ``get()`` method returns an array with the query, the bindings and a list of used collections.
```(new QueryBuilder())->for('i', '1..100')->return('i')->get()```
## References & resources

### Query execution
FluentAQL is solely a query builder so does not include any methods for execution.
### ArangoDB
- [ArangoDB](https://arangodb.com)
- [AQL documentation](https://www.arangodb.com/docs/stable/aql/)

You can execute queries with an [ArangoDB PHP driver](https://github.com/arangodb/arangodb-php).
Create a statement and feed it the returned data, then execute the statement.
### ArangoDB PHP clients
- [Official ArangoDB PHP driver](https://github.com/arangodb/arangodb-php)
- [Community PHP driver](https://github.com/sandrokeil/arangodb-php-client)
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "laravel-freelancer-nl/aql-query-builder",
"name": "laravel-freelancer-nl/fluentaql",
"description": "PHP AQL Query Builder",
"keywords": [
"fluentaql",
Expand Down
60 changes: 60 additions & 0 deletions docs/api/functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# AQL functions

This page gives an overview of the AQL functions supported by this query builder.

If you are missing a function please create an issue requesting it. In the meantime you can use the raw clause.

## Using functions
Functions always return expressions to be used in clauses and other functions.

Example:
```
$qb = new QueryBuilder();
$qb->for('i', '1..100')
->filter('i', '<', $qb->max([0,1,10,25,50]))
->limit(10)
->sort('i', 'desc')
->return('i');
```


## Array functions

| Description | AQL Function |
| :-------------------- | :-------------------------------------------------------------------------------------------- |
| count($value) | [COUNT()](https://www.arangodb.com/docs/stable/aql/functions-array.html#count) |
| countDistinct($value) | [COUNT_DISTINCT()](https://www.arangodb.com/docs/stable/aql/functions-array.html#count) |
| first($value) | [FIRST()](https://www.arangodb.com/docs/stable/aql/functions-array.html#first) |
| last($value) | [LAST()](https://www.arangodb.com/docs/stable/aql/functions-array.html#last) |
| length($value) | [LENGTH()](https://www.arangodb.com/docs/stable/aql/functions-array.html#length) |


## Date functions

| Description | AQL Function |
| :-------------------- | :-------------------------------------------------------------------------------------------- |
| dateNow() | [DATE_NOW()](https://www.arangodb.com/docs/stable/aql/functions-date.html#date_now) |
| dateIso8601(numeric&#124;string&#124;DateTime $date) | [DATE_ISO8601(date)](https://www.arangodb.com/docs/stable/aql/functions-date.html#date_iso8601) |
| dateTimestamp(numeric&#124;string&#124;DateTime $date) | [DATE_TIMESTAMP(date)](https://www.arangodb.com/docs/stable/aql/functions-date.html#date_timestamp) |
| | []() |
| | []() |
| | []() |
| | []() |

## GEO functions

| Description | AQL Function |
| :-------------------- | :-------------------------------------------------------------------------------------------- |
| distance($latitude1, $longitude1, $latitude2, $longitude2) | [DISTANCE(latitude1, longitude1, latitude2, longitude2)](https://www.arangodb.com/docs/stable/aql/functions-geo.html#distance) |

## Miscellaneous functions

| Description | AQL Function |
| :-------------------- | :-------------------------------------------------------------------------------------------- |
| document($collection, $id = null) | [DOCUMENT(collection, id)](https://www.arangodb.com/docs/stable/aql/functions-miscellaneous.html#document) |
| average($value) | []() |
| avg($value) | []() |
| max($value) | []() |
| min($value) | []() |
| rand() | []() |
| sum($value) | []() |
195 changes: 195 additions & 0 deletions docs/api/graph-clauses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Graph clauses
Graph clauses allow you to traverse your database 'many to many' document-collection relationships
along edge-collections.

[More information on the graph database model](https://www.arangodb.com/docs/stable/graphs.html).

With the exception of the 'WITH' clause, graph traversals are mostly an extension of the 'FOR' clause.
[AQL graph traversal documentation](https://www.arangodb.com/docs/stable/aql/graphs.html)

## WITH
```
with(...$collections)
```
Read-lock collections for traversals. This is required for graph traversals in a cluster environment.

**Example:**
```
$qb = new QueryBuilder();
$qb->with('users', 'managers')
```
Resulting AQL: `WITH users, managers`

[ArangoDB WITH documentation](https://www.arangodb.com/docs/stable/aql/operations-with.html)

## FOR
```
for($variableName, $in)
```
The basic 'for' usage is described here. In graph traversals the $variableName will give you the vertexes (documents).
You can also retrieve the edges and paths by inserting an array:
```
for([$vertex, $edge, $path])
```

$in is used to declare a min/man range of traversals.

**Example 1: vertices only**
```
$qb = new QueryBuilder();
$qb->with('users', 'managers')
->for('v')
```
Resulting AQL: `WITH users, managers FOR v`

**Example 2: vertices, edges and paths**
```
$qb = new QueryBuilder();
$qb->with('users', 'managers')
->for(['v', 'e', 'p'])
```
Resulting AQL: `WITH users, managers FOR v, e, p`

**Example 3: min/max depth of the traversal**
```
$qb = new QueryBuilder();
$qb->with('users', 'managers')
->for(['v', 'e', 'p'], '1..3')
```
Resulting AQL: `WITH users, managers FOR v, e, p IN 1..3`


## TRAVERSE
```
traverse($fromVertex, $inDirection = 'outbound', $toVertex = null)
```
The traverse clause follows 'FOR'. With this clause you declare the starting point, direction and optional endpoint of
the traversal.

**Example 1: retrieve all related cities**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->traverse('users/1', 'ANY')
```
Resulting AQL: `WITH users, cities FOR v, e, p ANY "users/1"`

**Example 2: retrieve all related data for all paths from a city to a user**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->traverse('users/1', 'INBOUND', 'cities/10')
```
Resulting AQL: `WITH users, cities FOR v, e, p INBOUND "users/1" "cities/10"`

## SHORTEST-PATH
```
shortestPath($fromVertex, $inDirection, $toVertex)
```
ShortestPath is a replacement for traverse and gives you the shortest path fom A to B in the chosen direction.

**Example 1: retrieve all related cities**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->shortestPath('users/1', 'ANY')
```

**Example 2: retrieve all related data for all paths from a city to a user**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->shortestPath('users/1', 'INBOUND', 'cities/10')
```


## K-SHORTEST-PATHS
```
kShortestPaths($fromVertex, $inDirection, $toVertex)
```
kShortestPaths is a replacement for traverse and gives you all the paths between two points ordered by ascending weight.
The weight is set through the 'OPTIONS' clause.

**Example 1: retrieve all related cities**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->shortestPath('users/1', 'ANY')
```

**Example 2: retrieve all related data for all paths from a city to a user**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->shortestPath('users/1', 'INBOUND', 'cities/10')
```

## GRAPH (named graph)
```
graph(string $graphName)
```
Execute the previously set traversal on a named graph.

**Example:**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->traverse('users/1', 'ANY')
->graph("citizens")
```
Resulting AQL: `WITH users, cities FOR v, e, p ANY "users/1" GRAPH "citizens"`

[ArangoDB NAMED GRAPH documentation](https://www.arangodb.com/docs/stable/aql/graphs-traversals.html#working-with-named-graphs)

## EDGE COLLECTIONS (unnamed graph)
```
edgeCollections(...$edgeCollections)
```
Execute the previously set traversal on the given set of edge collections.

**Example:**
```
$qb = new QueryBuilder();
$qb->with('users', 'cities')
->for(['v', 'e', 'p'])
->traverse('users/1', 'ANY')
->edgeCollections('edge1', 'edge2', 'edge3')
```
Resulting AQL: `WITH users, cities FOR v, e, p ANY "users/1" edge1, edge2, edge3`

[ArangoDB UNNAMED GRAPH documentation](https://www.arangodb.com/docs/stable/aql/graphs-traversals.html#working-with-collection-sets)


## PRUNE
```
prune($leftOperand, $comparisonOperator = null, $rightOperand = null, $logicalOperator = null)
```
Filter out data not matching the given predicate(s).

**Example - single predicate:**
```
$qb = new QueryBuilder();
$qb->for(['v', 'e', 'p'], '1..5')
->traverse('circles/A', 'OUTBOUND')
->graph("traversalGraph")
->prune('e.theTruth' '==' true)
->return([
'vertices' => 'p.vertices[*]._key',
'edges' => 'p.edges[*].label'
]);
```
Resulting AQL:
```
FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph'
PRUNE e.theTruth == true
RETURN { vertices: p.vertices[*]._key, edges: p.edges[*].label }
```

[ArangoDB PRUNE documentation](https://www.arangodb.com/docs/stable/aql/graphs-traversals.html#using-filters-and-the-explainer-to-extrapolate-the-costs)
Loading

0 comments on commit 023278c

Please sign in to comment.