We are translating a Neo4j cypher query to PHP cypher DSL (Domain-Specific Language) using the php-cypher-dsl library to have more flexibility.
We struggle with this because the library's documentation is not really explaining how to use it in much detail. Given that we have a plain query in cypher Neo4j:
MATCH (a:Resource), (b:Resource)
WHERE a.uri = $uri AND b.uri = $object
CREATE (a)-[r:{$propertyUri}]->(b)
RETURN type(r)
How can we translate this Neo4j cypher query to PHP cypher DSL?
The first thing you need to do is create all the nodes that you want to match on, as well as the parameters you want to use, like so:
$a = node("Resource"); // (a:Resource)
$b = node("Resource"); // (b:Resource)
$uri = parameter("uri"); // $uri
$object = parameter("object"); // $object
The node
function takes as its only argument the label of the node and the parameter
function the name of the parameter. Next, you can start to compose your query. You can start a new query using the query
function, like so:
$query = query();
This returns a Query
object, which has a number of methods to add new clauses to your query, such as match
, set
, where
and create
. These correspond to the clauses that Cypher offers.
We start by matching on the nodes $a
and $b
:
$query = query() // Start a new query
->match([$a, $b]) // Match on both "a" and "b"
...
Next, we want to create the condition to match on. php-cypher-dsl comes with a builder pattern for creating such expressions:
...
->where($a->property("uri")->equals($uri)->and($b->property("uri")->equals($object))
...
Next, before we create our CREATE
clause, we need to create a new variable $r
that gets shared between our CREATE
and RETURN
clause:
$r = variable(); // Create a new variable
To create the CREATE
clause, we need to perform some trickery to remove the :Resource
label from our nodes again. This part of the API still needs some improvement. We can get a new node with the same variable by using node()->withVariable($a->getVariable())
. All together, that looks like:
...
->create(node()->withVariable($a->getVariable())->relationshipTo(node()->withVariable($b->getVariable()), type: $propertyUri, name: $r))
...
Finally, we can return the value we want:
...
->returning(Procedure::raw('type', $r)) // The "type" function is not implemented natively, so we use raw
->toQuery(); // Convert the query to a string
All put together, the code looks like this:
use WikibaseSolutions\CypherDSL\Expressions\Procedures\Procedure;
use function WikibaseSolutions\CypherDSL\node;
use function WikibaseSolutions\CypherDSL\parameter;
use function WikibaseSolutions\CypherDSL\query;
use function WikibaseSolutions\CypherDSL\variable;
$propertyUri = "http://example.com";
$a = node("Resource");
$b = node("Resource");
$uri = parameter("uri");
$object = parameter("object");
$r = variable();
$query = query()
->match([$a, $b])
->where($a->property("uri")->equals($uri)->and($b->property("uri")->equals($object)))
->create(node()->withVariable($a->getVariable())->relationshipTo(node()->withVariable($b->getVariable()), type: $propertyUri, name: $r))
->returning(Procedure::raw('type', $r))
->toQuery();
Which I think is much less readable than using a normal query with variables. If you don't require complex logic to build queries (and only use pre-created queries, such as the one in your question), I'd recommend you to not use this library.