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   1091 use v5.10;
  36         469  
2             package REST::Neo4p::Query;
3 36     36   16001 use REST::Neo4p::Path;
  36         130  
  36         1166  
4 36     36   243 use REST::Neo4p::Exceptions;
  36         69  
  36         639  
5 36     36   179 use JSON::XS;
  36         65  
  36         1888  
6 36     36   16093 use REST::Neo4p::ParseStream;
  36         98  
  36         2279  
7 36     36   265 use HOP::Stream qw/drop/;
  36         69  
  36         1357  
8 36     36   20580 use Tie::IxHash;
  36         92807  
  36         1581  
9 36     36   277 use File::Temp qw(:seekable);
  36         74  
  36         5923  
10 36     36   315 use Carp qw(croak carp);
  36         72  
  36         1847  
11 36     36   222 use strict;
  36         67  
  36         739  
12 36     36   188 use warnings;
  36         69  
  36         1053  
13 36     36   180 no warnings qw(once);
  36         64  
  36         1943  
14              
15             BEGIN {
16 36     36   149943 $REST::Neo4p::Query::VERSION = '0.4003';
17             }
18              
19             our $BUFSIZE = 50000;
20              
21             sub new {
22 2     2 1 3633 my $class = shift;
23 2         7 my ($q_string, $params) = @_;
24 2 50 33     23 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     34 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         19 $q_string =~ s/\s/ /g;
31 2         14 ($q_string) = $q_string =~ m/^\s*(.*)\s*$/;
32 2 100 100     23 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 59 sub tmpf { shift->{_tempfile} }
43 10     10   51 sub _handle { shift->{_handle} }
44             sub execute {
45 10     10 1 4525 my $self = shift;
46 10         22 my @params = @_;
47 10         18 my %params;
48 10 50       30 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         24 local $REST::Neo4p::HANDLE;
54 10         27 REST::Neo4p->set_handle($self->_handle);
55 10 50       35 REST::Neo4p::CommException->throw("Not connected\n") unless REST::Neo4p->connected;
56 10         66 my $agent = REST::Neo4p->agent;
57              
58 10 50       108 if ($agent->batch_mode) {
59 0         0 REST::Neo4p::NotSuppException->throw("Query execution not supported in batch mode\n");
60             }
61 10         869 delete $self->{_error};
62 10         19 delete $self->{_error_list};
63 10         16 delete $self->{_decoded_resp};
64 10         20 delete $self->{NAME};
65              
66 10         36 my $endpt = 'post_'.REST::Neo4p->q_endpoint;
67 10         48 $self->{_tempfile} = File::Temp->new;
68 10 50       16643 unless ($self->tmpf) {
69 0         0 REST::Neo4p::LocalException->throw(
70             "Can't create query result tempfile : $!\n"
71             );
72             }
73 10         2290 eval {
74 10         43 for ($endpt) {
75 10 100       55 /cypher/ && do {
76 5         19 $agent->$endpt(
77             [],
78             { query => $self->query, params => $self->params },
79             {':content_file' => $self->tmpf->filename}
80             );
81 5         486 last;
82             };
83 5 50       23 /transaction/ && do {
84             # unfortunately, the order of 'statement' and 'parameters'
85             # is strict in the content (2.0.0-M06)
86 5         99 tie my %stmt, 'Tie::IxHash';
87 5         106 $stmt{statement} = $self->query;
88 5         103 $stmt{parameters} = $self->params;
89 5         90 $agent->$endpt(
90             [REST::Neo4p->_transaction],
91             {
92             statements => [ \%stmt ]
93             },
94             {':content_file' => $self->tmpf->filename}
95             );
96 5         416 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       76 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       257 if ( ref(REST::Neo4p->agent) !~ /Neo4j::Driver/ ) {
120 10         93 $self->_parse_response;
121             }
122             else { # Neo4j::Driver
123 0         0 $self->_wrap_statement_result;
124             }
125 5         120 1;
126             }
127              
128             sub fetchrow_arrayref {
129 29     29 1 56 my $self = shift;
130 29 50       82 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         62 $self->{_iterator}->();
134             }
135              
136 29     29 1 34372 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 16148 my $self = shift;
145 16   66     58 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 1130 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 69 sub query { shift->{_query} }
162 11     11 0 59 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   15 my $self = shift;
234 10         102 my $jsonr = JSON::XS->new->utf8;
235 10         34 my ($buf,$res,$str,$rowstr,$obj);
236 10         0 my $row_count;
237 10         24 $self->tmpf->read($buf, $BUFSIZE);
238 10         228 $jsonr->incr_parse($buf);
239 10         18 eval { # capture j_parse errors
240 10         34 $res = j_parse($jsonr);
241 10 50       27 die 'j_parse: No text to parse' unless $res;
242 10 100       76 die 'j_parse: JSON is not a query or txn response' unless $res->[0] =~ /QUERY|TXN/;
243 9         37 for ($res->[0]) {
244 9 100       27 /QUERY/ && do {
245 4         13 $obj = drop($str = $res->[1]->());
246 2 50 33     46 die 'j_parse: columns key not present' unless $obj && ($obj->[0] eq 'columns');
247 2         6 $self->{NAME} = $obj->[1];
248 2         11 $self->{NUM_OF_FIELDS} = scalar @{$obj->[1]};
  2         7  
249 2         6 $obj = drop($str);
250 2 50       30 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   59 return unless defined $self->tmpf;
255 6         52 my $row;
256             my $item;
257 6         14 $item = drop($rowstr);
258 5 50       65 unless ($item) {
259 0         0 undef $rowstr;
260 0         0 return;
261             }
262 5         11 $row = $item->[1];
263 5 50       12 if (ref $row) {
264 0         0 return $self->_process_row($row);
265             }
266             else {
267 5         7 my $ret;
268 5         7 eval {
269 5 50       12 if ($row eq 'PENDING') {
270 5 100       13 if ($self->tmpf->read($buf, $BUFSIZE)) {
271 4         102 $jsonr->incr_parse($buf);
272 4         25 $ret = $self->{_iterator}->();
273             }
274             else {
275 1         14 $item = drop($rowstr);
276 1         24 $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       24 if ($e =~ /j_parse|json/i) {
286 2         770 $e = REST::Neo4p::StreamException->new(message => $e);
287 2         1689 $self->{_error} = $e;
288 2 50       10 $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         32 };
298             # error check
299 2         6 last;
300             };
301 5 50       18 /TXN/ && do {
302 5         16 $obj = drop($str = $res->[1]->());
303 5 50 33     96 die 'j_parse: commit key not present' unless $obj && ($obj->[0] eq 'commit');
304 5         13 $obj = drop($str);
305 5 50 33     85 die 'j_parse: results key not present' unless $obj && ($obj->[0] eq 'results');
306 5         13 my $res_str = $obj->[1]->();
307 5         36 my $row_str;
308 5         12 my $item = drop($res_str);
309             $self->{_iterator} = sub {
310 33 50   33   112 return unless defined $self->tmpf;
311 33         240 my $row;
312 33 50       75 unless ($item) {
313 0         0 undef $row_str;
314 0         0 undef $res_str;
315 0         0 return;
316             }
317 33         44 my $ret;
318 33         54 eval {
319 33 100       82 if ($item->[0] eq 'columns') {
320 3         6 $self->{NAME} = $item->[1];
321 3         5 $self->{NUM_OF_FIELDS} = scalar @{$item->[1]};
  3         8  
322 3         9 $item = drop($res_str); # move to data
323 3 50       44 die 'j_parse: data key not present' unless $item->[0] eq 'data';
324             }
325 33 100 66     155 if ($item->[0] eq 'data' && ref($item->[1])) {
326 30         83 $row_str = $item->[1]->();
327             }
328 33 100       379 if ($row_str) {
329 30         62 $row = drop($row_str);
330 30 100 100     458 if (ref $row && ref $row->[1]) {
    100          
331 24         96 $ret = $self->_process_row($row->[1]->{row}, $row->[1]->{meta});
332             }
333             elsif (!defined $row) {
334 3         7 $item = drop($res_str);
335 3         49 $ret = $self->{_iterator}->();
336             }
337             else {
338 3 50       11 if ($row->[1] eq 'PENDING') {
339 3         10 $self->tmpf->read($buf, $BUFSIZE);
340 3         80 $jsonr->incr_parse($buf);
341 3         23 $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         7 $item = drop($res_str);
351 3 50       46 $item = drop($res_str) if $item->[1] =~ /STREAM/;
352             }
353 33 100 66     109 return if $ret || ($self->err && $self->errobj->isa('REST::Neo4p::TxQueryException'));
      100        
354 4 100 66     17 if ($item && $item->[0] eq 'transaction') {
355 3         8 $item = drop($res_str) # skip
356             }
357 4 100 66     53 if ($item && $item->[0] eq 'errors') {
358 3         7 my $err_str = $item->[1]->();
359 3         19 my @error_list;
360 3         8 while (my $err_item = drop($err_str)) {
361 139         1596 my $err = $err_item->[1];
362 139 100       236 if (ref $err) {
    50          
363 135         380 push @error_list, $err;
364             }
365             elsif ($err eq 'PENDING') {
366 4         14 $self->tmpf->read($buf,$BUFSIZE);
367 4         65 $jsonr->incr_parse($buf);
368             }
369             else {
370 0         0 die 'j_parse: error parsing txn error list';
371             }
372             }
373 3 100       83 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         1976 $item = drop($item);
378 3 100       56 $e->throw if $e;
379             }
380             };
381 33 100       117 if (my $e = Exception::Class->caught()) {
382 2 50       24 if (ref $e) {
    0          
383 2         6 $self->{_error} = $e;
384 2 50       10 $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         224 return $ret;
397              
398 3         120 };
399 3         8 last;
400             };
401             # default
402 0         0 REST::Neo4p::StreamException->throw( "j_parse: unknown item" );
403             }
404             };
405 10 50       39 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       71 if (ref $e) {
412 0         0 $e->rethrow;
413             }
414             else {
415 5 50       32 if ($e =~ /j_parse|json/i) {
416 5         43 $e = REST::Neo4p::StreamException->new(message => $e);
417 5         3774 $self->{_error} = $e;
418 5 50       30 $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   145 my ($resp,$meta) = @_;
429 75 50 33     303 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         8 for ($resp->{self}) {
439 3 100       11 m|data/node| && do {
440 2         7 return 'Node';
441             };
442 1 50       5 m|data/relationship| && do {
443 1         4 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         153 return 'Simple';
456             }
457             }
458              
459             sub _process_row {
460 25     25   48 my $self = shift;
461 25         71 my ($row,$meta) = @_;
462 25         41 my @ret;
463 25         94 foreach my $elt (@$row) {
464 75         99 my $info;
465 75 50       151 if ($meta) {
466 0         0 $info = shift @$meta;
467             }
468 75         118 for ($elt) {
469 75 50       151 !ref && do { #bareword
470 0         0 push @ret, $elt;
471 0         0 last;
472             };
473 75 50       214 (ref =~ /HASH/) && do {
474 75         102 my $entity_type;
475 75         100 eval {
476 75 50 33     153 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         143 $entity_type = _response_entity($elt);
483             }
484             };
485 75         92 my $e;
486 75 50       181 if ($e = Exception::Class->caught()) {
487 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
488             }
489 75         454 my $entity_class = 'REST::Neo4p::'.$entity_type;
490             push @ret, $self->{ResponseAsObjects} ?
491 75 50       254 $entity_class->new_from_json_response($elt) :
492             $entity_class->simple_from_json_response($elt);
493 75         160 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     92 return (@ret == 1 and ref($ret[0]) eq 'ARRAY') ? $ret[0] : \@ret;
536             }
537              
538             sub finish {
539 1     1 1 4 my $self = shift;
540 1         2 delete $self->{_iterator};
541 1 50       5 unlink $self->tmpf->filename if ($self->tmpf);
542 1         6 delete $self->{_tempfile};
543 1         385 return 1;
544             }
545              
546 1     1   906 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-2022 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;