File Coverage

blib/lib/REST/Neo4p/Query.pm
Criterion Covered Total %
statement 221 345 64.0
branch 90 188 47.8
condition 28 74 37.8
subroutine 30 35 85.7
pod 9 13 69.2
total 378 655 57.7


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