File Coverage

blib/lib/REST/Neo4p/Query.pm
Criterion Covered Total %
statement 220 344 63.9
branch 90 188 47.8
condition 28 74 37.8
subroutine 30 35 85.7
pod 9 13 69.2
total 377 654 57.6


line stmt bran cond sub pod time code
1 36     36   1067 use v5.10;
  36         492  
2             package REST::Neo4p::Query;
3 36     36   14966 use REST::Neo4p::Path;
  36         90  
  36         1134  
4 36     36   239 use REST::Neo4p::Exceptions;
  36         73  
  36         652  
5 36     36   170 use JSON::XS;
  36         59  
  36         1853  
6 36     36   14350 use REST::Neo4p::ParseStream;
  36         93  
  36         2176  
7 36     36   256 use HOP::Stream qw/drop/;
  36         67  
  36         1340  
8 36     36   19318 use Tie::IxHash;
  36         106660  
  36         1345  
9 36     36   284 use File::Temp qw(:seekable);
  36         64  
  36         5636  
10 36     36   261 use Carp qw(croak carp);
  36         72  
  36         1694  
11 36     36   211 use strict;
  36         80  
  36         810  
12 36     36   191 use warnings;
  36         67  
  36         1241  
13 36     36   219 no warnings qw(once);
  36         84  
  36         1089  
14       36     BEGIN {
15             }
16              
17             our $BUFSIZE = 50000;
18              
19             sub new {
20 2     2 1 3526 my $class = shift;
21 2         9 my ($q_string, $params) = @_;
22 2 50 33     32 unless (defined $q_string and !ref $q_string) {
23 0         0 REST::Neo4p::LocalException->throw( "First argument must be the query string\n");
24             }
25 2 50 66     17 unless (!defined $params || ref($params) eq 'HASH') {
26 0         0 REST::Neo4p::LocalException->throw( "Second argment must be a hashref of query parameters\n" );
27             }
28 2         22 $q_string =~ s/\s/ /g;
29 2         19 ($q_string) = $q_string =~ m/^\s*(.*)\s*$/;
30 2 100 100     27 bless { '_query' => $q_string,
31             '_params' => $params || {},
32             '_handle' => REST::Neo4p->handle, # current handle
33             'Statement' => $q_string,
34             'NUM_OF_PARAMS' => $params ? scalar keys %$params : 0,
35             # 'ParamValues' => $params,
36             'ResponseAsObjects' => 1,
37             '_tempfile' => ''
38             }, $class;
39             }
40 1     1 0 8 sub tmpf { shift->{_tempfile} }
41 10     10   55 sub _handle { shift->{_handle} }
42             sub execute {
43 10     10 1 3972 my $self = shift;
44 10         24 my @params = @_;
45 10         19 my %params;
46 10 50       36 if (@params) {
47 0 0       0 %params = ref $params[0] ? %{$params[0]} : @params;
  0         0  
48 0         0 $self->{_params} = \%params;
49             }
50             # current handle
51 10         24 local $REST::Neo4p::HANDLE;
52 10         33 REST::Neo4p->set_handle($self->_handle);
53 10 50       39 REST::Neo4p::CommException->throw("Not connected\n") unless REST::Neo4p->connected;
54 10         77 my $agent = REST::Neo4p->agent;
55              
56 10 50       112 if ($agent->batch_mode) {
57 0         0 REST::Neo4p::NotSuppException->throw("Query execution not supported in batch mode\n");
58             }
59 10         935 delete $self->{_error};
60 10         25 delete $self->{_error_list};
61 10         18 delete $self->{_decoded_resp};
62 10         23 delete $self->{NAME};
63              
64 10         46 my $endpt = 'post_'.REST::Neo4p->q_endpoint;
65 10         60 $self->{_tempfile} = File::Temp->new;
66 10 50       4899 unless ($self->tmpf) {
67 0         0 REST::Neo4p::LocalException->throw(
68             "Can't create query result tempfile : $!\n"
69             );
70             }
71 10         2387 eval {
72 10         40 for ($endpt) {
73 10 100       49 /cypher/ && do {
74 5         18 $agent->$endpt(
75             [],
76             { query => $self->query, params => $self->params },
77             {':content_file' => $self->tmpf->filename}
78             );
79 5         443 last;
80             };
81 5 50       31 /transaction/ && do {
82             # unfortunately, the order of 'statement' and 'parameters'
83             # is strict in the content (2.0.0-M06)
84 5         63 tie my %stmt, 'Tie::IxHash';
85 5         119 $stmt{statement} = $self->query;
86 5         123 $stmt{parameters} = $self->params;
87 5         89 $agent->$endpt(
88             [REST::Neo4p->_transaction],
89             {
90             statements => [ \%stmt ]
91             },
92             {':content_file' => $self->tmpf->filename}
93             );
94 5         440 last;
95             };
96 0         0 do {
97 0         0 REST::Neo4p::TxException->throw(
98             "Unknown query REST endpoint '".REST::Neo4p->q_endpoint."'\n"
99             );
100             }
101             }
102             };
103 10 50       95 if (my $e = REST::Neo4p::Neo4jException->caught ) {
    50          
    50          
104 0         0 $self->{_error} = $e;
105 0 0       0 $e->can('error_list') && ($self->{_error_list} = $e->error_list);
106 0 0       0 $e->rethrow if ($self->{RaiseError});
107 0         0 return;
108             }
109             elsif ($e = REST::Neo4p::Exception->caught()) {
110 0         0 $self->{_error} = $e;
111 0 0       0 $e->rethrow if ($self->{RaiseError});
112 0         0 return;
113             }
114             elsif ( $e = Exception::Class->caught) {
115 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
116             }
117 10 50       278 if ( ref(REST::Neo4p->agent) !~ /Neo4j::Driver/ ) {
118 10         97 $self->_parse_response;
119             }
120             else { # Neo4j::Driver
121 0         0 $self->_wrap_statement_result;
122             }
123 5         118 1;
124             }
125              
126             sub fetchrow_arrayref {
127 29     29 1 52 my $self = shift;
128 29 50       84 unless ( defined $self->{_iterator} ) {
129 0         0 REST::Neo4p::LocalException->throw("Can't run fetch(), query not execute()'d yet\nCheck query object for error with err()/errstr()\n");
130             }
131 29         61 $self->{_iterator}->();
132             }
133              
134 29     29 1 26204 sub fetch { shift->fetchrow_arrayref(@_) }
135              
136             sub column_names {
137 0     0 0 0 my $self = shift;
138 0   0     0 return $self->{_column_names} && @{$self->{_column_names}};
139             }
140              
141             sub err {
142 16     16 1 12842 my $self = shift;
143 16   66     56 return $self->{_error} && ($self->{_error}->code || 599);
144             }
145              
146             sub errstr {
147 0     0 1 0 my $self = shift;
148 0   0     0 return $self->{_error} && ( $self->{_error}->message || $self->{_error}->neo4j_message );
149             }
150              
151 6     6 1 1054 sub errobj { shift->{_error} }
152              
153             sub err_list {
154 0     0 1 0 my $self = shift;
155 0   0     0 return $self->{_error} && $self->{_error_list};
156             }
157              
158              
159 11     11 0 69 sub query { shift->{_query} }
160 11     11 0 63 sub params { shift->{_params} }
161              
162              
163             sub _wrap_statement_result {
164 0     0   0 my $self = shift;
165 0         0 my $result = REST::Neo4p->agent->last_result;
166 0         0 my $errors = REST::Neo4p->agent->last_errors;
167 0         0 $self->{NAME} = $result->keys;
168 0         0 my $n = $self->{NUM_OF_FIELDS} = scalar @{$self->{NAME}};
  0         0  
169             $self->{_iterator} = sub {
170 0     0   0 my @row;
171 0         0 my $rec = $result->fetch;
172 0 0       0 return unless $rec;
173 0         0 eval {
174 0         0 my $as_object = $self->{ResponseAsObjects};
175 0         0 for (my $i=0;$i<$n;$i++) {
176 0         0 my $elt = $rec->get($i);
177             my $cvt = sub {
178 0 0       0 return $_[0] unless ref($_[0]) =~ /Driver/;
179 0         0 my ($type) = ref($_[0]) =~ /::([^:]+)$/;
180 0         0 my $cls = "REST::Neo4p::$type";
181 0 0       0 return $as_object ? $cls->new_from_json_response($_[0]) :
182             $cls->simple_from_json_response($_[0]);
183 0         0 };
184 0         0 for (ref($elt)) {
185 0 0       0 /Driver/ && do {
186 0         0 $elt = $cvt->($elt);
187             };
188 0 0       0 /HASH/ && do {
189 0         0 for (keys %$elt) {
190 0         0 $elt->{$_} = $cvt->($elt->{$_})
191             }
192             };
193 0 0       0 /ARRAY/ && do {
194 0         0 for (@$elt) {
195 0         0 $_ = $cvt->($_);
196             }
197             };
198             #else
199 0         0 push @row, $elt;
200             }
201             }
202             };
203 0 0       0 if (my $e = Exception::Class->caught()) {
204 0 0       0 if ($e =~ /j_parse|json/i) {
205 0         0 $e = REST::Neo4p::StreamException->new(message => $e);
206 0         0 $self->{_error} = $e;
207 0 0       0 $e->throw if $self->{RaiseError};
208 0         0 return;
209             }
210             else {
211 0         0 die $e;
212             }
213             }
214             # flatten if single array ref returned
215 0 0 0     0 if (@row==1 and ref($row[0]) eq 'ARRAY') {
216 0         0 return $row[0];
217             }
218             else {
219 0         0 return \@row;
220             }
221 0         0 };
222 0         0 return;
223             }
224              
225             # _parse_response sets up an iterator that pulls a row's worth of objects from
226             # the servers JSON stream, parses the row into objects, and returns the row.
227             # this iterator is placed in $self->{_iterator} as a side effect.
228             # It is hit in fetchrow_arrayref.
229              
230             sub _parse_response {
231 10     10   25 my $self = shift;
232 10         103 my $jsonr = JSON::XS->new->utf8;
233 10         33 my ($buf,$res,$str,$rowstr,$obj);
234 10         0 my $row_count;
235 10         42 $self->tmpf->read($buf, $BUFSIZE);
236 10         257 $jsonr->incr_parse($buf);
237 10         22 eval { # capture j_parse errors
238 10         37 $res = j_parse($jsonr);
239 10 50       31 die 'j_parse: No text to parse' unless $res;
240 10 100       79 die 'j_parse: JSON is not a query or txn response' unless $res->[0] =~ /QUERY|TXN/;
241 9         21 for ($res->[0]) {
242 9 100       31 /QUERY/ && do {
243 4         11 $obj = drop($str = $res->[1]->());
244 2 50 33     42 die 'j_parse: columns key not present' unless $obj && ($obj->[0] eq 'columns');
245 2         6 $self->{NAME} = $obj->[1];
246 2         4 $self->{NUM_OF_FIELDS} = scalar @{$obj->[1]};
  2         5  
247 2         5 $obj = drop($str);
248 2 50       29 die 'j_parse: data key not present' unless $obj->[0] eq 'data';
249 2         13 $rowstr = $obj->[1]->();
250             # query iterator
251             $self->{_iterator} = sub {
252 6 50   6   56 return unless defined $self->tmpf;
253 6         46 my $row;
254             my $item;
255 6         14 $item = drop($rowstr);
256 5 50       63 unless ($item) {
257 0         0 undef $rowstr;
258 0         0 return;
259             }
260 5         10 $row = $item->[1];
261 5 50       12 if (ref $row) {
262 0         0 return $self->_process_row($row);
263             }
264             else {
265 5         6 my $ret;
266 5         9 eval {
267 5 50       12 if ($row eq 'PENDING') {
268 5 100       12 if ($self->tmpf->read($buf, $BUFSIZE)) {
269 4         93 $jsonr->incr_parse($buf);
270 4         24 $ret = $self->{_iterator}->();
271             }
272             else {
273 1         14 $item = drop($rowstr);
274 1         22 $ret = $self->_process_row($item->[1]);
275             }
276              
277             }
278             else {
279 0         0 die "j_parse: barf(qry)"
280             }
281             };
282 5 100       24 if (my $e = Exception::Class->caught()) {
283 2 50       26 if ($e =~ /j_parse|json/i) {
284 2         757 $e = REST::Neo4p::StreamException->new(message => $e);
285 2         2109 $self->{_error} = $e;
286 2 50       10 $e->throw if $self->{RaiseError};
287 0         0 return;
288             }
289             else {
290 0         0 die $e;
291             }
292             }
293 3         27 return $ret;
294             }
295 2         36 };
296             # error check
297 2         6 last;
298             };
299 5 50       19 /TXN/ && do {
300 5         17 $obj = drop($str = $res->[1]->());
301 5 50 33     108 die 'j_parse: commit key not present' unless $obj && ($obj->[0] eq 'commit');
302 5         15 $obj = drop($str);
303 5 50 33     100 die 'j_parse: results key not present' unless $obj && ($obj->[0] eq 'results');
304 5         17 my $res_str = $obj->[1]->();
305 5         41 my $row_str;
306 5         11 my $item = drop($res_str);
307             $self->{_iterator} = sub {
308 33 50   33   106 return unless defined $self->tmpf;
309 33         287 my $row;
310 33 50       74 unless ($item) {
311 0         0 undef $row_str;
312 0         0 undef $res_str;
313 0         0 return;
314             }
315 33         47 my $ret;
316 33         110 eval {
317 33 100       88 if ($item->[0] eq 'columns') {
318 3         9 $self->{NAME} = $item->[1];
319 3         7 $self->{NUM_OF_FIELDS} = scalar @{$item->[1]};
  3         8  
320 3         11 $item = drop($res_str); # move to data
321 3 50       45 die 'j_parse: data key not present' unless $item->[0] eq 'data';
322             }
323 33 100 66     167 if ($item->[0] eq 'data' && ref($item->[1])) {
324 30         77 $row_str = $item->[1]->();
325             }
326 33 100       360 if ($row_str) {
327 30         112 $row = drop($row_str);
328 30 100 100     467 if (ref $row && ref $row->[1]) {
    100          
329 24         91 $ret = $self->_process_row($row->[1]->{row}, $row->[1]->{meta});
330             }
331             elsif (!defined $row) {
332 3         9 $item = drop($res_str);
333 3         62 $ret = $self->{_iterator}->();
334             }
335             else {
336 3 50       13 if ($row->[1] eq 'PENDING') {
337 3         9 $self->tmpf->read($buf, $BUFSIZE);
338 3         120 $jsonr->incr_parse($buf);
339 3         30 $ret = $self->{_iterator}->();
340             }
341             else {
342              
343 0         0 die "j_parse: barf(txn)";
344             }
345             }
346             }
347             else { # $row_str undef
348 3         10 $item = drop($res_str);
349 3 50       49 $item = drop($res_str) if $item->[1] =~ /STREAM/;
350             }
351 33 100 66     123 return if $ret || ($self->err && $self->errobj->isa('REST::Neo4p::TxQueryException'));
      100        
352 4 100 66     20 if ($item && $item->[0] eq 'transaction') {
353 3         9 $item = drop($res_str) # skip
354             }
355 4 100 66     53 if ($item && $item->[0] eq 'errors') {
356 3         8 my $err_str = $item->[1]->();
357 3         20 my @error_list;
358 3         10 while (my $err_item = drop($err_str)) {
359 139         1582 my $err = $err_item->[1];
360 139 100       250 if (ref $err) {
    50          
361 135         374 push @error_list, $err;
362             }
363             elsif ($err eq 'PENDING') {
364 4         15 $self->tmpf->read($buf,$BUFSIZE);
365 4         66 $jsonr->incr_parse($buf);
366             }
367             else {
368 0         0 die 'j_parse: error parsing txn error list';
369             }
370             }
371 3 100       95 my $e = REST::Neo4p::TxQueryException->new(
372             message => "Query within transaction returned errors (see error_list)\n",
373             error_list => \@error_list, code => '304'
374             ) if @error_list;
375 3         2173 $item = drop($item);
376 3 100       61 $e->throw if $e;
377             }
378             };
379 33 100       128 if (my $e = Exception::Class->caught()) {
380 2 50       23 if (ref $e) {
    0          
381 2         8 $self->{_error} = $e;
382 2 50       17 $e->rethrow if $self->{RaiseError};
383             }
384             elsif ($e =~ /j_parse|json/i) {
385 0         0 $e = REST::Neo4p::StreamException->new(message => $e);
386 0         0 $self->{_error} = $e;
387 0 0       0 $e->throw if $self->{RaiseError};
388 0         0 return;
389             }
390             else {
391 0         0 die $e;
392             }
393             }
394 33         237 return $ret;
395              
396 3         123 };
397 3         10 last;
398             };
399             # default
400 0         0 REST::Neo4p::StreamException->throw( "j_parse: unknown item" );
401             }
402             };
403 10 50       49 if (my $e = Exception::Class->caught('REST::Neo4p::LocalException')) {
    100          
404 0         0 $self->{_error} = $e;
405 0 0       0 $e->rethrow if ($self->{RaiseError});
406 0         0 return;
407             }
408             elsif ($e = Exception::Class->caught()) {
409 5 50       72 if (ref $e) {
410 0         0 $e->rethrow;
411             }
412             else {
413 5 50       50 if ($e =~ /j_parse|json/i) {
414 5         42 $e = REST::Neo4p::StreamException->new(message => $e);
415 5         3991 $self->{_error} = $e;
416 5 50       29 $e->throw if $self->{RaiseError};
417 0         0 return;
418             }
419             else {
420 0         0 die $e;
421             }
422             }
423             }
424             }
425             sub _response_entity {
426 75     75   148 my ($resp,$meta) = @_;
427 75 50 33     314 if ( ref($resp) eq '' ) { #handle arrays of barewords
    50 33        
    100          
    50          
428 0         0 return 'bareword';
429             }
430             elsif ($meta) {
431 0         0 my $type = $meta->{type};
432 0         0 $type =~ s/^(.)/\U$1\E/;
433 0         0 return $type;
434             }
435             elsif (defined $resp->{self}) {
436 3         7 for ($resp->{self}) {
437 3 100       9 m|data/node| && do {
438 2         15 return 'Node';
439             };
440 1 50       5 m|data/relationship| && do {
441 1         2 return 'Relationship';
442             };
443 0         0 do {
444 0         0 REST::Neo4p::QueryResponseException->throw(message => "Can't identify object type by JSON response\n");
445             };
446             }
447             }
448             elsif (defined $resp->{start} && defined $resp->{end}
449             && defined $resp->{nodes}) {
450 0         0 return 'Path';
451             }
452             else {
453 72         184 return 'Simple';
454             }
455             }
456              
457             sub _process_row {
458 25     25   50 my $self = shift;
459 25         72 my ($row,$meta) = @_;
460 25         40 my @ret;
461 25         50 foreach my $elt (@$row) {
462 75         101 my $info;
463 75 50       137 if ($meta) {
464 0         0 $info = shift @$meta;
465             }
466 75         119 for ($elt) {
467 75 50       153 !ref && do { #bareword
468 0         0 push @ret, $elt;
469 0         0 last;
470             };
471 75 50       236 (ref =~ /HASH/) && do {
472 75         105 my $entity_type;
473 75         145 eval {
474 75 50 33     158 if ($info && $info->{type}) {
475 0         0 $elt->{self} = "$$info{type}/$$info{id}";
476 0         0 $entity_type = $info->{type};
477 0         0 $entity_type =~ s/^(.)/\U$1\E/;
478             }
479             else {
480 75         126 $entity_type = _response_entity($elt);
481             }
482             };
483 75         119 my $e;
484 75 50       181 if ($e = Exception::Class->caught()) {
485 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
486             }
487 75         439 my $entity_class = 'REST::Neo4p::'.$entity_type;
488             push @ret, $self->{ResponseAsObjects} ?
489 75 50       279 $entity_class->new_from_json_response($elt) :
490             $entity_class->simple_from_json_response($elt);
491 75         168 last;
492             };
493 0 0       0 (ref =~ /ARRAY/) && do {
494 0         0 my $array;
495 0         0 for my $ary_elt (@$elt) {
496 0         0 my $entity_type;
497 0         0 eval {
498 0 0 0     0 if ($info && $info->{type}) {
499 0         0 $elt->{self} = "$$info{type}/$$info{id}";
500 0         0 $entity_type = $info->{type};
501 0         0 $entity_type =~ s/^(.)/\U$1\E/;
502             }
503             else {
504 0         0 $entity_type = _response_entity($ary_elt);
505             }
506             };
507 0         0 my $e;
508 0 0       0 if ($e = Exception::Class->caught()) {
509 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
510             }
511 0 0       0 if ($entity_type eq 'bareword') {
512 0         0 push @$array, $ary_elt;
513             }
514             else {
515 0         0 my $entity_class = 'REST::Neo4p::'.$entity_type;
516             push @$array, $self->{ResponseAsObjects} ?
517 0 0       0 $entity_class->new_from_json_response($ary_elt) :
518             $entity_class->simple_from_json_response($ary_elt) ;
519             }
520             }
521 0         0 push @ret, $array;
522 0         0 last;
523             };
524 0         0 do {
525 0         0 REST::Neo4p::QueryResponseException->throw("Can't parse query response (row doesn't make sense)\n");
526 0         0 last;
527             };
528             }
529             }
530             # guess whether to flatten response:
531             # if more than one row element, don't flatten,
532             # return an array reference in the response
533 25 50 33     96 return (@ret == 1 and ref($ret[0]) eq 'ARRAY') ? $ret[0] : \@ret;
534             }
535              
536             sub finish {
537 1     1 1 9 my $self = shift;
538 1         5 delete $self->{_iterator};
539 1 50       4 unlink $self->tmpf->filename if ($self->tmpf);
540 1         12 delete $self->{_tempfile};
541 1         299 return 1;
542             }
543              
544 1     1   724 sub DESTROY { shift->finish }
545              
546             =head1 NAME
547              
548             REST::Neo4p::Query - Execute Neo4j Cypher queries
549              
550             =head1 SYNOPSIS
551              
552             REST::Neo4p->connect('http:/127.0.0.1:7474');
553             $query = REST::Neo4p::Query->new('MATCH (n) WHERE n.name = "Boris" RETURN n');
554             $query->execute;
555             $node = $query->fetch->[0];
556             $node->relate_to($other_node, 'link');
557              
558             =head1 DESCRIPTION
559              
560             REST::Neo4p::Query encapsulates Neo4j Cypher language queries,
561             executing them via L and returning an iterator
562             over the rows, in the spirit of L.
563              
564             =head2 Streaming
565              
566             L|/execute()> captures the Neo4j query response in a temp
567             file. L|/fetch()> iterates (in a non-blocking way if
568             possible) over the JSON in the response using the incremental parser
569             of L (see L if
570             interested). So go ahead and make those 100 meg queries. The tempfile
571             is unlinked after the iterator runs out of rows, or upon object
572             destruction, whichever comes first.
573              
574             =head2 Parameters
575              
576             C understands Cypher L
577             parameters|http://docs.neo4j.org/chunked/stable/cypher-parameters.html>. These
578             are represented in Cypher, unfortunately, as dollar-prefixed tokens.
579              
580             MATCH (n) WHERE n.first_name = $name RETURN n
581              
582             Here, C<$name> is the named parameter.
583              
584             Don't forget to escape the dollar sign if you're also doing string interpolation:
585              
586             $prop = "n.name";
587             $qry = "MATCH (n) WHERE $prop = \$name RETURN n";
588            
589             A single query object can be executed multiple times with different parameter values:
590              
591             my $q = REST::Neo4p::Query->new(
592             'MATCH (n) WHERE n.first_name = $name RETURN n'
593             );
594             foreach (@names) {
595             $q->execute(name => $_);
596             while ($row = $q->fetch) {
597             ...process
598             }
599             }
600              
601             This is very highly recommended over creating multiple query objects like so:
602              
603             foreach (@names) {
604             my $q = REST::Neo4p::Query->new(
605             "MATCH (n) WHERE n.first_name = '$_' RETURN n"
606             );
607             $q->execute;
608             ...
609             }
610              
611             As with any database engine, a large amount of overhead is saved by
612             planning a parameterized query once. In addition, the REST side of the
613             Neo4j server will balk at handling 1000s of individual queries in a row.
614             Parameterizing queries gets around this issue.
615              
616             =head2 Paths
617              
618             If your query returns a path, L|/fetch()> returns a
619             L object from which you can obtain the Nodes and
620             Relationships.
621              
622             =head2 Transactions
623              
624             See L.
625              
626             =head1 METHODS
627              
628             =over
629              
630             =item new()
631              
632             $stmt = 'MATCH (n) WHERE id(n) = $node_id RETURN n';
633             $query = REST::Neo4p::Query->new($stmt,{node_id => 1});
634              
635             Create a new query object. First argument is the Cypher query
636             (required). Second argument is a hashref of parameters (optional).
637              
638             =item execute()
639              
640             $numrows = $query->execute;
641             $numrows = $query->execute( param1 => 'value1', param2 => 'value2');
642             $numrows = $query->execute( $param_hashref );
643              
644             Execute the query on the server. Not supported in batch mode.
645              
646             =item fetch()
647              
648             =item fetchrow_arrayref()
649              
650             $query = REST::Neo4p::Query->new('MATCH (n) RETURN n, n.name LIMIT 10');
651             $query->execute;
652             while ($row = $query->fetch) {
653             print 'It works!' if ($row->[0]->get_property('name') == $row->[1]);
654             }
655              
656             Fetch the next row of returned data (as an arrayref). Nodes are
657             returned as L objects,
658             relationships are returned as
659             L objects,
660             scalars are returned as-is.
661              
662             =item err(), errstr(), errobj()
663              
664             $query->execute;
665             if ($query->err) {
666             printf "status code: %d\n", $query->err;
667             printf "error message: %s\n", $query->errstr;
668             printf "Exception class was %s\n", ref $query->errobj;
669             }
670              
671             Returns the HTTP error code, Neo4j server error message, and exception
672             object if an error was encountered on execution.
673              
674             =item err_list()
675              
676             =item finish()
677              
678             while (my $row = $q->fetch) {
679             if ($row->[0] eq 'What I needed') {
680             $q->finish();
681             last;
682             }
683             }
684              
685             Call finish() to unlink the tempfile before all items have been
686             fetched.
687              
688             =back
689              
690             =head2 ATTRIBUTES
691              
692             =over
693              
694             =item RaiseError
695              
696             $q->{RaiseError} = 1;
697              
698             Set C<$query-E{RaiseError}> to die immediately (e.g., to catch the exception in an C block).
699              
700             =item ResponseAsObjects
701              
702             $q->{ResponseAsObjects} = 0;
703             $row_as_plain_perl = $q->fetch;
704              
705             If set to true (the default), query reponses are returned as
706             REST::Neo4p objects. If false, nodes, relationships and paths are
707             returned as simple perl structures. See
708             L,
709             L,
710             L for details.
711              
712             =item Statement
713              
714             $stmt = $q->{Statement};
715              
716             Get the Cypher statement associated with the query object.
717              
718             =back
719              
720             =head1 SEE ALSO
721              
722             L, L, L, L.
723              
724             =head1 AUTHOR
725              
726             Mark A. Jensen
727             CPAN ID: MAJENSEN
728             majensen -at- cpan -dot- org
729              
730             =head1 LICENSE
731              
732             Copyright (c) 2012-2020 Mark A. Jensen. This program is free software; you
733             can redistribute it and/or modify it under the same terms as Perl
734             itself.
735              
736             =cut
737             1;