diff --git a/docs/api/graph-clauses.md b/docs/api/graph-clauses.md index c5f3517..aec2c28 100644 --- a/docs/api/graph-clauses.md +++ b/docs/api/graph-clauses.md @@ -181,27 +181,102 @@ Resulting AQL: `WITH users, cities FOR v, e, p ANY "users/1" edge1, INBOUND edge ## PRUNE ``` -prune($leftOperand, $comparisonOperator = null, $rightOperand = null, $logicalOperator = null) +prune($leftOperand, $comparisonOperator = null, $rightOperand = null, $logicalOperator = null, $pruneVariable) ``` Filter out data not matching the given predicate(s). **Example - single predicate:** ``` - $qb = new QueryBuilder(); + $qb = (new QueryBuilder()); $qb->for(['v', 'e', 'p'], '1..5') ->traverse('circles/A', 'OUTBOUND') ->graph("traversalGraph") - ->prune('e.theTruth' '==' true) + ->prune('e.theTruth', '==', true) ->return([ 'vertices' => 'p.vertices[*]._key', 'edges' => 'p.edges[*].label' - ]); + ]) + ->get(); ``` Resulting AQL: ``` -FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph' +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 } + RETURN {"vertices":p.vertices[*]._key,"edges":p.edges[*].label} +``` + +**Example - multiple predicates:** +``` + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune([['e.active', '==', 'true'], ['e.age', '>', 18]]) + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) + ->get(); +``` +Resulting AQL: +``` +FOR v, e, p IN 1..5 OUTBOUND "circles/A" GRAPH "traversalGraph" + PRUNE e.active == true AND e.age > 18 + RETURN {"vertices":p.vertices[*]._key,"edges":p.edges[*].label} +``` + +**Example - store condition in variable:** +``` + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune( + 'e.theTruth', + '==', + true, + 'OR', + 'pruneCondition' + ) + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) + ->get(); +``` +Resulting AQL: +``` + FOR v, e, p IN 1..5 OUTBOUND "circles/A" GRAPH "traversalGraph" + PRUNE pruneCondition = e.theTruth == true + RETURN {"vertices":p.vertices[*]._key,"edges":p.edges[*].label} +``` + + +**Example - multiple predicates stored in variable:** +``` + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune( + [ + 'e.theTruth', + '==', + true + ], + pruneVariable: 'pruneCondition' + ) + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) + ->get(); +``` +Resulting AQL: +``` + FOR v, e, p IN 1..5 OUTBOUND "circles/A" GRAPH "traversalGraph" + PRUNE pruneCondition = 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) diff --git a/src/Traits/ValidatesExpressions.php b/src/Traits/ValidatesExpressions.php index edf12fb..1334468 100644 --- a/src/Traits/ValidatesExpressions.php +++ b/src/Traits/ValidatesExpressions.php @@ -129,7 +129,7 @@ public function isId($value): bool { if ( is_string($value) && - preg_match("/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-]+\/?[a-zA-Z0-9_\-\:\.\@\(\)\+\,\=\;\$\!\*\'\%]+$/", $value) + preg_match("/^[a-zA-Z0-9_-]+\/(\/?[a-zA-Z0-9_\-\:\.\@\(\)\+\,\=\;\$\!\*\'\%])+$/", $value) ) { return true; } diff --git a/tests/Unit/AQL/GraphClausesTest.php b/tests/Unit/AQL/GraphClausesTest.php index b72a826..b35f2e3 100644 --- a/tests/Unit/AQL/GraphClausesTest.php +++ b/tests/Unit/AQL/GraphClausesTest.php @@ -128,23 +128,39 @@ public function testEdgeCollectionListClauseArrayInput() */ public function testPruneClause() { - $result = (new QueryBuilder()) - ->for('u', 'users') - ->prune('u.active', '==', 'true') + $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' + ]) ->get(); - self::assertEquals('FOR u IN users PRUNE u.active == true', $result->query); + self::assertEquals( + '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}', + $qb->query + ); - $result = (new QueryBuilder()) - ->for('u', 'Users') - ->prune('u.active', '==', 'true', 'OR') - ->get(); - self::assertEquals('FOR u IN Users PRUNE u.active == true', $result->query); - - $result = (new QueryBuilder()) - ->for('u', 'Users') - ->prune('u.active', '==', 'true') + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune('e.theTruth', '==', true, 'OR') + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) ->get(); - self::assertEquals('FOR u IN Users PRUNE u.active == true', $result->query); + self::assertEquals( + '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}', + $qb->query + ); } /** @@ -152,11 +168,22 @@ public function testPruneClause() */ public function testPruneClauseWithMultiplePredicates() { - $result = (new QueryBuilder()) - ->for('u', 'Users') - ->prune([['u.active', '==', 'true'], ['u.age', '>', 18]]) - ->get(); - self::assertEquals('FOR u IN Users PRUNE u.active == true AND u.age > 18', $result->query); + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune([['e.active', '==', 'true'], ['e.age', '>', 18]]) + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) + ->get(); + self::assertEquals( + 'FOR v, e, p IN 1..5 OUTBOUND "circles/A" GRAPH "traversalGraph"' + . ' PRUNE e.active == true AND e.age > 18' + . ' RETURN {"vertices":p.vertices[*]._key,"edges":p.edges[*].label}', + $qb->query + ); } /** @@ -164,22 +191,78 @@ public function testPruneClauseWithMultiplePredicates() */ public function testPruneClauseWithVariable() { - $result = (new QueryBuilder()) - ->for('u', 'users') - ->prune('u.active', '==', 'true', null, 'pruneCondition') + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune( + 'e.theTruth', + '==', + true, + 'OR', + 'pruneCondition' + ) + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) ->get(); - self::assertEquals('FOR u IN users PRUNE pruneCondition = u.active == true', $result->query); + self::assertEquals( + 'FOR v, e, p IN 1..5 OUTBOUND "circles/A" GRAPH "traversalGraph"' + . ' PRUNE pruneCondition = e.theTruth == true' + . ' RETURN {"vertices":p.vertices[*]._key,"edges":p.edges[*].label}', + $qb->query + ); - $result = (new QueryBuilder()) - ->for('u', 'Users') - ->prune('u.active', '==', 'true', 'OR', 'pruneCondition') + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune( + [ + 'e.theTruth', + '==', + true + ], + pruneVariable: 'pruneCondition' + ) + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) ->get(); - self::assertEquals('FOR u IN Users PRUNE pruneCondition = u.active == true', $result->query); + self::assertEquals( + 'FOR v, e, p IN 1..5 OUTBOUND "circles/A" GRAPH "traversalGraph"' + . ' PRUNE pruneCondition = e.theTruth == true' + . ' RETURN {"vertices":p.vertices[*]._key,"edges":p.edges[*].label}', + $qb->query + ); - $result = (new QueryBuilder()) - ->for('u', 'Users') - ->prune(['u.active', '==', 'true'], pruneVariable: 'pruneCondition') + $qb = (new QueryBuilder()); + $qb->for(['v', 'e', 'p'], '1..5') + ->traverse('circles/A', 'OUTBOUND') + ->graph("traversalGraph") + ->prune( + [ + [ + 'e.theTruth', '==', true + ], + [ + 'e.theTruth', '!=', false + ] + ], + pruneVariable: 'pruneCondition' + ) + ->return([ + 'vertices' => 'p.vertices[*]._key', + 'edges' => 'p.edges[*].label' + ]) ->get(); - self::assertEquals('FOR u IN Users PRUNE pruneCondition = u.active == true', $result->query); + self::assertEquals( + 'FOR v, e, p IN 1..5 OUTBOUND "circles/A" GRAPH "traversalGraph"' + . ' PRUNE pruneCondition = e.theTruth == true AND e.theTruth != false' + . ' RETURN {"vertices":p.vertices[*]._key,"edges":p.edges[*].label}', + $qb->query + ); } }