Neo4j::Cypher::Abstract - Generate Cypher query statements
TBD
When writing code to automate database queries, sometimes it is
convenient to use a wrapper that generates desired query strings. Then
the user can think conceptually and avoid having to remember precise
syntax or write and debug string manipulations. A good wrapper can
also allow the user to produce query statements dynamically, hide
dialect details, and may include some simple syntax
checking. SQL::Abstract
is an example of a widely-used wrapper for
SQL.
The graph database Neo4j allows SQL-like
declarative queries through its query language
Cypher. Neo4j::Cypher::Abstract
is a Cypher wrapper in the spirit of SQL::Abstract
that creates
very general Cypher productions in an intuitive, Perly way.
A clause is a portion of a complete query statement that plays a specific functional role in the statement and is set off by one or more reserved words. Clauses in Cypher include reading (e.g., MATCH), writing (CREATE), importing (LOAD CSV), and schema (CREATE CONSTRAINT) clauses, among others. They have arguments that define the clause's scope of action.
Cypher::Abstract objects possess methods for every Cypher clause. Each method adds its clause, with arguments, to the object's internal queue. Every method returns the object itself. When an object is rendered as a string, it concatenates its clauses to yield the entire query statement.
These features add up to the following idiom. Suppose we want to render the Cypher statement
MATCH (n:Users) WHERE n.name =~ 'Fred.*' RETURN n.manager
In Cypher::Abstract
, we do
$s = Neo4j::Cypher::Abstract->new()->match('n:Users')
->where("n.name =~ 'Fred.*'")->return('n.manager');
print "$s;\n"; # "" is overloaded by $s->as_string()
Because you may create many such statements in a program, a short alias for the constructor can be imported, and extra variable assignments can be avoided.
use Neo4j::Cypher::Abstract qw/cypher/;
use DBI;
my $dbh = DBI->connect("dbi:Neo4p:http://127.0.0.1:7474;user=foo;pass=bar");
my $sth = $dbh->prepare(
cypher->match('n:Users')->where("n.name =~ 'Fred.*'")->return('n.manager')
);
$sth->execute();
...
Patterns
are representations of subgraphs with constraints that are key
components of Cypher queries. They have their own syntax and are also
amenable to wrapping. In the example above, match()
uses a simple
built-in shortcut:
$s->match('n:User') eq $s->match('(n:User)')
where (n:User)
is the simple pattern for "all nodes with label
'User'". The module Neo4j::Cypher::Pattern handles
complex and arbitrary patterns. It is loaded automatically on use Neo4j::Cypher::Abstract
. Abstract patterns are written in a similar
idiom as Cypher statements. They can be used anywhere a string is
allowed. For example:
use Neo4j::Cypher::Abstract qw/cypher ptn/;
ptn->N(':Person',{name=>'Oliver Stone'})->R("r>")->N('movie') eq
'(:Person {name:'Oliver Stone'})-[r]->(movie)'
$sth = $dbh->prepare(
cypher->match(ptn->N(':Person',{name=>'Oliver Stone'})->R("r>")->N('movie'))
->return('type(r)')
);
See Neo4j::Cypher::Pattern for a full description of how to specify patterns.
As in SQL, Cypher has a WHERE clause that is used to filter returned
results. Rather than having to create custom strings for common WHERE
expressions, SQL::Abstract provides an intuitive system for
constructing valid expressions from Perl data structures made up of
hash, array, and scalar references. Neo4j::Cypher::Abstract
contains a new implementation of the SQL::Abstract expression
"compiler". If the argument to the where()
method (or any other
method, in fact) is an array or hash reference, it is interpreted as
an expression in SQL::Abstract style. (The parser is a complete
reimplementation, so some idioms in that style may not result in
exactly the same productions.)
Parameters in Cypher are named, and given as alphanumeric tokens
prefixed (sadly) with '$'. The Cypher::Abstract
object collects
these in the order they appear in the complete statement. The list of
parameters can be recovered with the parameters()
method.
$c = cypher->match('n:Person')->return('n.name')
->skip('$s')->limit('$l');
@p = $c->parameters; # @p is ('$s', '$l') /;
- match(@ptns)
- optional_match(@ptns)
- where($expr)
- start($ptn)
- create(@ptns), create_unique($ptn)
- merge(@ptns)
- foreach($running_var => $list, cypher-><update statement>)
- set()
- delete(), detach_delete()
- on_create(), on_match()
- limit($num)
- skip($num)
- order_by($identifier)
- return(@items), return_distinct(@items)
- with(@identifiers), with_distinct(@identifiers)
- unwind($list => $identifier)
- union()
- call()
- yield()
- using_index($index)
- using_scan()
- using_join($identifier)
- load_csv($file => $identifier), load_csv_with_headers(...)
- create_constraint_exist($node => $label, $property),create_constraint_unique($node => $label, $property)
- drop_constraint(...)
- create_index($label => $property), drop_index($label => $property)
-
parameters()
Return a list of statement parameters.
-
as_string()
Render the Cypher statement as a string. Overloads
""
.
REST::Neo4p, DBD::Neo4p, SQL::Abstract
Neo4j::Cypher::Pattern - Generate Cypher pattern strings
# express a cypher pattern
use Neo4j::Cypher::Pattern qw/ptn/;
ptn->node();
ptn->N(); #alias
ptn->N("varname");
ptn->N("varname",["label"],{prop => "value"});
ptn->N("varname:label");
ptn->N(["label"],{prop => "value"});
ptn->node('a')->related_to()->node('b'); # (a)--(b)
ptn->N('a')->R()->N('b'); # alias
# additional forms
ptn->N('a')->R("varname","typename",[$minhops,$maxhops],{prop => "value"})
->N('b'); # (a)-[varname:typename*minhops..maxhops { prop:"value }]-(b)
ptn->N('a')->R("varname:typename")->N('b'); # (a)-[varname:typename]-(b)
ptn->N('a')->R(":typename")->N('b'); # (a)-[:typename]-(b)
ptn->N('a')->R("", "typename")->N('b'); # (a)-[:typename]-(b)
# directed relns
ptn->N('a')->R("<:typename")->N('b'); # (a)<-[:typename]-(b)
ptn->N('a')->R("varname:typename>")->N('b'); # (a)-[varname:typename]->(b)
# these return strings
$pattern->path('varname'); # path variable assigned to a pattern
$pattern->as('varname'); # alias
ptn->compound($pattern1, $pattern2); # comma separated patterns
ptn->C($pattern1, $pattern2); # alias
The Cypher
query language of the graph database Neo4j uses
patterns
to represent graph nodes and their relationships, for selecting and
matching in queries. Neo4j::Cypher::Pattern
can be used to create
Cypher pattern productions in Perl in an intuitive way. It is part of
the Neo4j::Cypher::Abstract distribution.
Neo4j::Cypher::Pattern
objects possess methods to represent nodes
and relationships. Each method adds its portion of the pattern, with
arguments, to the object's internal queue. Every method returns the
object itself. When an object is rendered as a string, it concatenates
nodes and relationship productions to yield the entire query statement
as a string.
These features add up to the following idiom. Suppose we want to render the Cypher pattern
(b {name:"Slate"})<-[:WORKS_FOR]-(a {name:"Fred"})-[:KNOWS]->(c {name:"Barney"})
In Neo4j::Cypher::Pattern
, we do
$p = Neo4j::Cypher::Pattern->new()->N('b',{name=>'Slate')
->R('<:WORKS_FOR')->N('a',{name => 'Fred'})
->R(':KNOWS>')->N('c',{name=>'Barney'});
print "$p\n"; # "" is overloaded by $p->as_string()
Because you may create many patterns in a program, a short alias for the constructor can be imported, and extra variable assignments can be avoided.
print ptn->N('b',{name=>'Slate'})
->R('<:WORKS_FOR')->N('a',{name => 'Fred'})
->R(':KNOWS>')->N('c',{name=>'Barney'}), "\n";
In pattern productions, values for properties will be quoted by default with single quotes (single quotes that are present will be escaped) unless the values are numeric.
To prevent quoting Cypher statement list variable names (for example), make the name an argument to the pattern constructor:
ptn('event')->N('y')->R("<:IN")->N('e:Event'=> { id => 'event.id' });
# renders (y)<-[:IN]-(e:Event {id:event.id})
# rather than (y)<-[:IN]-(e:Event {id:"event.id"})
-
Constructor new()
-
pattern(), ptn()
Exportable aliases for the constructor. Arguments are variable names that should not be quoted in rendering values of properties.
-
node(), N()
Render a node. Arguments in any order:
scalar string: variable name or variable:label array ref: array of node labels hash ref: hash of property => value
-
related_to(), R()
Render a relationship. Arguments in any order:
scalar string: variable name or variable:type array ref: variable-length pattern: [$minhops, $maxhops] [] (empty array)- any number of hops [$hops] - exactly $hops hash ref : hash of property => value
-
path(), as()
Render the pattern set equal to a path variable:
$p = ptn->N('a')->_N('b'); print $p->as('pth'); # gives 'pth = (a)--(b)'
-
compound(), C()
Render multiple patterns separated by commas
ptn->compound( ptn->N('a')->to_N('b'), ptn->N('a')->from_N('c')); # (a)-->(b), (a)<--(c)
-
Shortcuts _N, to_N, from_N
ptn->N('a')->_N('b'); # (a)--(b) ptn->N('a')->to_N('b'); # (a)-->(b) pth->N('a')->from_N('b'); # (a)<--(b)
Mark A. Jensen
CPAN: MAJENSEN
majensen -at- cpan -dot- org
(c) 2017 Mark A. Jensen