File Coverage

blib/lib/Attean/Algebra.pm
Criterion Covered Total %
statement 1086 1267 85.7
branch 64 130 49.2
condition 18 34 52.9
subroutine 255 302 84.4
pod 0 118 0.0
total 1423 1851 76.8


line stmt bran cond sub pod time code
1 50     50   518 use v5.14;
  50         158  
2 50     50   241 use warnings;
  50         107  
  50         1177  
3 50     50   263 use utf8;
  50         120  
  50         508  
4              
5             =head1 NAME
6              
7             Attean::Algebra - Representation of SPARQL algebra operators
8              
9             =head1 VERSION
10              
11             This document describes Attean::Algebra version 0.033
12              
13             =head1 SYNOPSIS
14              
15             use v5.14;
16             use Attean;
17              
18             =head1 DESCRIPTION
19              
20             This is a utility package that defines all the Attean query algebra classes
21             in the Attean::Algebra namespace:
22              
23             =over 4
24              
25             =cut
26              
27 50     50   1569 use Attean::API::Query;
  50         113  
  50         2214  
28              
29             use AtteanX::SPARQL::Constants;
30 50     50   330 use AtteanX::SPARQL::Token;
  50         129  
  50         8540  
31 50     50   394 use Types::Standard qw(Bool ArrayRef HashRef ConsumerOf);
  50         117  
  50         1517  
32 50     50   270 use Moo;
  50         98  
  50         424  
33 50     50   37287 use namespace::clean;
  50         106  
  50         303  
34 50     50   16997  
  50         123  
  50         403  
35             has 'dataset' => (is => 'ro', isa => HashRef[ArrayRef[ConsumerOf['Attean::API::Term']]], default => sub { +{} });
36             has 'subquery' => (is => 'ro', isa => Bool, default => 0);
37              
38             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
39              
40             my $self = shift;
41             my $name = $self->subquery ? 'SubQuery' : 'Query';
42 9     9 0 14
43 9 50       30 my %dataset = %{ $self->dataset };
44             my @default = @{ $dataset{ default } || [] };
45 9         23 my @named = @{ $dataset{ named } || [] };
  9         29  
46 9 50       11 my $has_dataset = (scalar(@default) + scalar(@named));
  9         36  
47 9 50       12
  9         25  
48 9         15 my $s = $name;
49             if ($has_dataset) {
50 9         14 my @parts;
51 9 50       15 if (scalar(@default)) {
52 0         0 push(@parts, 'Default graph(s): ' . join(', ', map { chomp; $_ } map { $_->as_sparql } @default));
53 0 0       0 }
54 0         0 if (scalar(@named)) {
  0         0  
  0         0  
  0         0  
55             push(@parts, 'Named graph(s): ' . join(', ', map { chomp; $_ } map { $_->as_sparql } @named));
56 0 0       0 }
57 0         0 $s .= ' { ' . join('; ', @parts) . ' }';
  0         0  
  0         0  
  0         0  
58             }
59 0         0 return $s;
60             }
61 9         29  
62             my $self = shift;
63             my $child = $self->child;
64             my $l = AtteanX::SPARQL::Token->lbrace;
65 8     8 0 130 my $r = AtteanX::SPARQL::Token->rbrace;
66 8         25 my $from = AtteanX::SPARQL::Token->keyword('FROM');
67 8         31 my $named = AtteanX::SPARQL::Token->keyword('NAMED');
68 8         311  
69 8         270 my %dataset = %{ $self->dataset };
70 8         264 my @default = @{ $dataset{ default } || [] };
71             my @named = @{ $dataset{ named } || [] };
72 8         259 my $has_dataset = (scalar(@default) + scalar(@named));
  8         35  
73 8 100       15 if ($child->does('Attean::API::SPARQLQuerySerializable')) {
  8         31  
74 8 100       12 if ($self->subquery) {
  8         29  
75 8         12 my @tokens;
76 8 100       34 push(@tokens, $l);
77 7 50       110 push(@tokens, $child->sparql_tokens->elements);
78 0         0 push(@tokens, $r);
79 0         0 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
80 0         0 } else {
81 0         0 my %args;
82 0         0 if ($has_dataset) {
83             $args{dataset} = $self->dataset;
84 7         11 }
85 7 100       14 return $child->query_tokens(%args);
86 4         7 }
87             } else {
88 7         27 my $sel = AtteanX::SPARQL::Token->keyword('SELECT');
89             my $star = AtteanX::SPARQL::Token->star;
90             my $where = AtteanX::SPARQL::Token->keyword('WHERE');
91 1         20
92 1         36 my @tokens;
93 1         34 if ($self->subquery) {
94             push(@tokens, $l);
95 1         32 }
96 1 50       5 push(@tokens, $sel, $star);
97 0         0 if ($has_dataset) {
98             foreach my $i (sort { $a->as_string cmp $b->as_string } @default) {
99 1         2 push(@tokens, $from);
100 1 50       4 push(@tokens, $i->sparql_tokens->elements);
101 0         0 }
  0         0  
102 0         0 foreach my $i (sort { $a->as_string cmp $b->as_string } @named) {
103 0         0 push(@tokens, $from);
104             push(@tokens, $named);
105 0         0 push(@tokens, $i->sparql_tokens->elements);
  0         0  
106 0         0 }
107 0         0 }
108 0         0 push(@tokens, $where);
109             push(@tokens, $l);
110             push(@tokens, $child->sparql_tokens->elements);
111 1         2 push(@tokens, $r);
112 1         2 if ($self->subquery) {
113 1         4 push(@tokens, $r);
114 1         3 }
115 1 50       5 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
116 0         0 }
117             }
118 1         13 }
119              
120             use AtteanX::SPARQL::Constants;
121             use AtteanX::SPARQL::Token;
122             use Types::Standard qw(Bool);
123             use Moo;
124 50     50   164795 use namespace::clean;
  50         116  
  50         7258  
125 50     50   319  
  50         105  
  50         1026  
126 50     50   228 with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
  50         124  
  50         343  
127 50     50   22438  
  50         115  
  50         240  
128 50     50   16536  
  50         118  
  50         261  
129             my $self = shift;
130             my $child = $self->child;
131             return $child->sparql_tokens;
132 3     3 0 9 }
133             }
134              
135 6     6 0 11 =item * L<Attean::Algebra::Sequence>
136 6         20  
137 6         22 =cut
138              
139             use AtteanX::SPARQL::Constants;
140             use AtteanX::SPARQL::Token;
141             use Moo;
142             use namespace::clean;
143              
144             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::QueryTree';
145              
146 50     50   127283 my $self = shift;
  50         127  
  50         6495  
147 50     50   302 return scalar(@{ $self->children });
  50         102  
  50         760  
148 50     50   208 }
  50         96  
  50         230  
149 50     50   14868
  50         118  
  50         241  
150              
151             my $self = shift;
152             my $semi = AtteanX::SPARQL::Token->semicolon;
153              
154 0     0 0 0 my @tokens;
155 0         0 foreach my $t (@{ $self->children }) {
  0         0  
156             push(@tokens, $t->sparql_tokens->elements);
157             push(@tokens, $semi);
158 1     1 0 3 }
159             pop(@tokens); # remove last SEMICOLON token
160             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
161 1     1 0 2 }
162 1         5 }
163              
164 1         49 =item * L<Attean::Algebra::Join>
165 1         1  
  1         4  
166 2         5 =cut
167 2         5  
168             use AtteanX::SPARQL::Constants;
169 1         3 use AtteanX::SPARQL::Token;
170 1         13 use Moo;
171             use namespace::clean;
172              
173             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::QueryTree';
174              
175              
176             my $self = shift;
177             my $l = AtteanX::SPARQL::Token->lbrace;
178             my $r = AtteanX::SPARQL::Token->rbrace;
179 50     50   132555  
  50         119  
  50         6491  
180 50     50   312 my @tokens;
  50         110  
  50         750  
181 50     50   219 push(@tokens, $l);
  50         103  
  50         240  
182 50     50   15434 foreach my $t (@{ $self->children }) {
  50         114  
  50         232  
183             push(@tokens, $t->sparql_subtokens->elements);
184             }
185             push(@tokens, $r);
186 2     2 0 7 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
187             }
188             }
189 5     5 0 12  
190 5         18 =item * L<Attean::Algebra::LeftJoin>
191 5         190  
192             =cut
193 5         162  
194 5         7 use AtteanX::SPARQL::Constants;
195 5         7 use AtteanX::SPARQL::Token;
  5         14  
196 8         23 use Moo;
197             use Types::Standard qw(ConsumerOf);
198 5         7 use namespace::clean;
199 5         60  
200             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::BinaryQueryTree';
201              
202             has 'expression' => (is => 'ro', isa => ConsumerOf['Attean::API::Expression'], required => 1, default => sub { Attean::ValueExpression->new( value => Attean::Literal->true ) });
203             my $self = shift;
204             return sprintf('LeftJoin { %s }', $self->expression->as_string);
205             }
206             my $self = shift;
207             my $opt = AtteanX::SPARQL::Token->keyword('OPTIONAL');
208 50     50   129598 my $l = AtteanX::SPARQL::Token->lbrace;
  50         123  
  50         6340  
209 50     50   312 my $r = AtteanX::SPARQL::Token->rbrace;
  50         99  
  50         756  
210 50     50   215 my ($lhs, $rhs) = @{ $self->children };
  50         104  
  50         234  
211 50     50   16080
  50         108  
  50         339  
212 50     50   23029 my @tokens;
  50         149  
  50         244  
213             push(@tokens, $l);
214             push(@tokens, $lhs->sparql_subtokens->elements);
215             push(@tokens, $r, $opt, $l);
216             push(@tokens, $rhs->sparql_subtokens->elements);
217            
218 0     0 0 0 my $expr = $self->expression;
219 0         0 my $is_true = 0;
220             if ($expr->isa('Attean::ValueExpression')) {
221 0     0 0 0 my $value = $expr->value;
222             if ($value->equals(Attean::Literal->true)) {
223 4     4 0 12 $is_true = 1;
224 4         16 }
225 4         166 }
226 4         139
227 4         132 unless ($is_true) {
  4         12  
228             my $f = AtteanX::SPARQL::Token->keyword('FILTER');
229 4         8 my $lparen = AtteanX::SPARQL::Token->lparen;
230 4         5 my $rparen = AtteanX::SPARQL::Token->rparen;
231 4         12 push(@tokens, $f);
232 4         13 push(@tokens, $lparen);
233 4         10 push(@tokens, $expr->sparql_tokens->elements);
234             push(@tokens, $rparen);
235 4         13 }
236 4         7
237 4 100       24 push(@tokens, $r);
238 2         5 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
239 2 50       8 }
240 2         14
241             }
242              
243             =item * L<Attean::Algebra::Filter>
244 4 100       7  
245 2         8 =cut
246 2         76  
247 2         69 use AtteanX::SPARQL::Constants;
248 2         66 use AtteanX::SPARQL::Token;
249 2         3 use Moo;
250 2         8 use Types::Standard qw(ConsumerOf);
251 2         6 use namespace::clean;
252              
253             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
254 4         6  
255 4         51 has 'expression' => (is => 'ro', isa => ConsumerOf['Attean::API::Expression'], required => 1);
256             my $self = shift;
257             return sprintf('Filter { %s }', $self->expression->as_string);
258             }
259             my $self = shift;
260             my $f = AtteanX::SPARQL::Token->keyword('FILTER');
261             my $l = AtteanX::SPARQL::Token->lparen;
262             my $r = AtteanX::SPARQL::Token->rparen;
263             my ($child) = @{ $self->children };
264             my $expr = $self->expression;
265 50     50   144589 my @tokens;
  50         116  
  50         7091  
266 50     50   338 push(@tokens, $child->sparql_tokens->elements);
  50         97  
  50         892  
267 50     50   225 push(@tokens, $f);
  50         99  
  50         241  
268 50     50   16257 push(@tokens, $l);
  50         113  
  50         361  
269 50     50   22505 push(@tokens, $expr->sparql_tokens->elements);
  50         104  
  50         241  
270             push(@tokens, $r);
271             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
272             }
273             }
274              
275 2     2 0 4 =item * L<Attean::Algebra::Union>
276 2         15  
277             =cut
278 0     0 0 0  
279             use AtteanX::SPARQL::Constants;
280 4     4 0 19 use AtteanX::SPARQL::Token;
281 4         17 use Moo;
282 4         174 use Types::Standard qw(ConsumerOf);
283 4         140 use namespace::clean;
284 4         134  
  4         12  
285 4         8 with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::BinaryQueryTree';
286 4         4  
287 4         12 my $self = shift;
288 4         9 my $union = AtteanX::SPARQL::Token->keyword('UNION');
289 4         6 my $l = AtteanX::SPARQL::Token->lbrace;
290 4         16 my $r = AtteanX::SPARQL::Token->rbrace;
291 4         12 my ($lhs, $rhs) = @{ $self->children };
292 4         63
293             my @tokens;
294             push(@tokens, $l);
295             push(@tokens, $lhs->sparql_subtokens->elements);
296             push(@tokens, $r, $union, $l);
297             push(@tokens, $rhs->sparql_subtokens->elements);
298             push(@tokens, $r);
299             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
300             }
301 50     50   133669 }
  50         124  
  50         6572  
302 50     50   305  
  50         111  
  50         804  
303 50     50   212 =item * L<Attean::Algebra::Graph>
  50         95  
  50         546  
304 50     50   27134  
  50         149  
  50         316  
305 50     50   22106 =cut
  50         107  
  50         229  
306              
307             use AtteanX::SPARQL::Constants;
308             use AtteanX::SPARQL::Token;
309 1     1 0 4 use Moo;
310             use Types::Standard qw(ConsumerOf);
311 2     2 0 7 use namespace::clean;
312 2         9  
313 2         90 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
314 2         69  
315 2         65 has 'graph' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariable'], required => 1);
  2         6  
316              
317 2         3 my $self = shift;
318 2         4 my $graph = $self->graph;
319 2         7 my ($child) = @{ $self->children };
320 2         6 my @vars = $child->in_scope_variables;
321 2         6 if ($graph->does('Attean::API::Variable')) {
322 2         5 return Set::Scalar->new(@vars, $graph->value)->elements;
323 2         25 } else {
324             return @vars;
325             }
326             }
327             my $self = shift;
328             return sprintf('Graph %s', $self->graph->as_string);
329             }
330             my $self = shift;
331             my $graph = AtteanX::SPARQL::Token->keyword('GRAPH');
332 50     50   132961 my $l = AtteanX::SPARQL::Token->lbrace;
  50         124  
  50         6621  
333 50     50   300 my $r = AtteanX::SPARQL::Token->rbrace;
  50         97  
  50         804  
334 50     50   220 my ($child) = @{ $self->children };
  50         488  
  50         8842  
335 50     50   15578
  50         92  
  50         268  
336 50     50   20856 my @tokens;
  50         338  
  50         231  
337             push(@tokens, $graph);
338             push(@tokens, $self->graph->sparql_tokens->elements);
339             push(@tokens, $l);
340             push(@tokens, $child->sparql_subtokens->elements);
341             push(@tokens, $r);
342             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
343 6     6 0 9 }
344 6         19 }
345 6         10  
  6         21  
346 6         15 =item * L<Attean::Algebra::Extend>
347 6 100       258  
348 2         52 =cut
349              
350 4         95 use AtteanX::SPARQL::Constants;
351             use AtteanX::SPARQL::Token;
352             use Moo;
353             use Types::Standard qw(ConsumerOf);
354 1     1 0 2 use namespace::clean;
355 1         6
356             my $self = shift;
357 0     0 0 0 my ($child) = @{ $self->children };
358             my @vars = $child->in_scope_variables;
359 2     2 0 6 return Set::Scalar->new(@vars, $self->variable->value)->elements;
360 2         9 }
361 2         86 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
362 2         80  
363 2         65 has 'variable' => (is => 'ro', isa => ConsumerOf['Attean::API::Variable'], required => 1);
  2         6  
364             has 'expression' => (is => 'ro', isa => ConsumerOf['Attean::API::Expression'], required => 1);
365 2         2  
366 2         5 my $self = shift;
367 2         7 return sprintf('Extend { %s ← %s }', $self->variable->as_string, $self->expression->as_string);
368 2         6 }
369 2         8 my $self = shift;
370 2         4 my $bind = AtteanX::SPARQL::Token->keyword('BIND');
371 2         26 my $as = AtteanX::SPARQL::Token->keyword('AS');
372             my $l = AtteanX::SPARQL::Token->lparen;
373             my $r = AtteanX::SPARQL::Token->rparen;
374             my ($child) = @{ $self->children };
375             my $var = $self->variable;
376             my $expr = $self->expression;
377            
378             my @tokens;
379             push(@tokens, $child->sparql_tokens->elements);
380 50     50   137186 push(@tokens, $bind);
  50         137  
  50         6435  
381 50     50   288 push(@tokens, $l);
  50         96  
  50         895  
382 50     50   713 push(@tokens, $expr->sparql_tokens->elements);
  50         96  
  50         233  
383 50     50   15761 push(@tokens, $as);
  50         100  
  50         498  
384 50     50   21825 push(@tokens, $var->sparql_tokens->elements);
  50         102  
  50         222  
385             push(@tokens, $r);
386             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
387 12     12 0 18 }
388 12         16 }
  12         34  
389 12         26  
390 12         387 =item * L<Attean::Algebra::Minus>
391              
392             =cut
393              
394             use AtteanX::SPARQL::Constants;
395             use AtteanX::SPARQL::Token;
396             use Moo;
397             use Types::Standard qw(ConsumerOf);
398 1     1 0 2 use namespace::clean;
399 1         5  
400             with 'Attean::API::Algebra', 'Attean::API::BinaryQueryTree';
401 0     0 0 0  
402             my $self = shift;
403 2     2 0 843 my ($child) = @{ $self->children };
404 2         6 return $child->in_scope_variables;
405 2         73 }
406 2         81  
407 2         73 my $self = shift;
408 2         64 my $minus = AtteanX::SPARQL::Token->keyword('MINUS');
  2         6  
409 2         5 my $l = AtteanX::SPARQL::Token->lbrace;
410 2         6 my $r = AtteanX::SPARQL::Token->rbrace;
411             my ($lhs, $rhs) = @{ $self->children };
412 2         3
413 2         17 my @tokens;
414 2         6 push(@tokens, $l);
415 2         14 push(@tokens, $lhs->sparql_subtokens->elements);
416 2         6 push(@tokens, $r, $minus, $l);
417 2         5 push(@tokens, $rhs->sparql_subtokens->elements);
418 2         6 push(@tokens, $r);
419 2         6 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
420 2         24 }
421            
422             }
423              
424             =item * L<Attean::Algebra::Distinct>
425              
426             =cut
427              
428             use Moo;
429 50     50   140090 use namespace::clean;
  50         116  
  50         6588  
430 50     50   309  
  50         234  
  50         852  
431 50     50   204 with 'Attean::API::SPARQLQuerySerializable';
  50         86  
  50         233  
432 50     50   15958 with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
  50         124  
  50         291  
433 50     50   22186  
  50         99  
  50         221  
434             }
435              
436             =item * L<Attean::Algebra::Reduced>
437              
438 2     2 0 3 =cut
439 2         2  
  2         6  
440 2         6 use Moo;
441             use namespace::clean;
442              
443 1     1 0 2 with 'Attean::API::SPARQLQuerySerializable';
444             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
445 2     2 0 6  
446 2         10 }
447 2         86  
448 2         71 =item * L<Attean::Algebra::Slice>
449 2         66  
  2         8  
450             =cut
451 2         3  
452 2         4 use Moo;
453 2         6 use Types::Standard qw(Int);
454 2         5 use namespace::clean;
455 2         7  
456 2         6 with 'Attean::API::SPARQLQuerySerializable';
457 2         27 with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
458              
459             has 'limit' => (is => 'ro', isa => Int, default => -1);
460             has 'offset' => (is => 'ro', isa => Int, default => 0);
461             my $self = shift;
462             my @str = ('Slice');
463             push(@str, "Limit=" . $self->limit) if ($self->limit >= 0);
464             push(@str, "Offset=" . $self->offset) if ($self->offset > 0);
465             return join(' ', @str);
466             }
467 50     50   133963 }
  50         114  
  50         265  
468 50     50   16053  
  50         106  
  50         217  
469             =item * L<Attean::Algebra::Project>
470              
471             =cut
472              
473 1     1 0 4 use Types::Standard qw(ArrayRef ConsumerOf);
474             use Moo;
475             use namespace::clean;
476              
477             with 'Attean::API::SPARQLQuerySerializable';
478             with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
479              
480             has 'variables' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Variable']], required => 1);
481 50     50   22785  
  50         109  
  50         182  
482 50     50   13341 my $self = shift;
  50         115  
  50         245  
483             my ($child) = @{ $self->children };
484             my $set = Set::Scalar->new( $child->in_scope_variables );
485             my $proj = Set::Scalar->new( map { $_->value } @{ $self->variables } );
486             return $set->intersection($proj)->elements;
487 1     1 0 3 }
488             my $self = shift;
489             return sprintf('Project { %s }', join(' ', map { '?' . $_->value } @{ $self->variables }));
490             }
491             }
492              
493             =item * L<Attean::Algebra::Comparator>
494              
495 50     50   24195 =cut
  50         148  
  50         220  
496 50     50   14177  
  50         127  
  50         337  
497 50     50   22694 use Moo;
  50         107  
  50         198  
498             use AtteanX::SPARQL::Constants;
499             use AtteanX::SPARQL::Token;
500             use Types::Standard qw(Bool ConsumerOf);
501             use namespace::clean;
502              
503              
504             has 'ascending' => (is => 'ro', isa => Bool, default => 1);
505 0     0 0 0 has 'expression' => (is => 'ro', isa => ConsumerOf['Attean::API::Expression'], required => 1);
506 0         0  
507 0 0       0 my $self = shift;
508 0 0       0 if ($self->ascending) {
509 0         0 return 'ASC(' . $self->expression->as_string . ')';
510             } else {
511             return 'DESC(' . $self->expression->as_string . ')';
512             }
513             }
514              
515             my $self = shift;
516             my $asc = AtteanX::SPARQL::Token->keyword('ASC');
517             my $desc = AtteanX::SPARQL::Token->keyword('DESC');
518 50     50   30630 my $l = AtteanX::SPARQL::Token->lparen;
  50         161  
  50         238  
519 50     50   24531 my $r = AtteanX::SPARQL::Token->rparen;
  50         100  
  50         230  
520 50     50   14778
  50         127  
  50         233  
521             my @tokens;
522             if ($self->ascending) {
523             push(@tokens, $self->expression->sparql_tokens->elements);
524             } else {
525             push(@tokens, $desc, $l);
526             push(@tokens, $self->expression->sparql_tokens->elements);
527             push(@tokens, $r);
528 2     2 0 3 }
529 2         3 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  2         4  
530 2         6 }
531 2         217 }
  4         10  
  2         76  
532 2         145  
533             =item * L<Attean::Algebra::OrderBy>
534              
535 5     5 0 6 =cut
536 5         8  
  8         39  
  5         16  
537             use Moo;
538 0     0 0 0 use AtteanX::SPARQL::Constants;
539             use AtteanX::SPARQL::Token;
540             use Types::Standard qw(ArrayRef InstanceOf);
541             use namespace::clean;
542            
543             with 'Attean::API::SPARQLQuerySerializable';
544             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
545            
546 50     50   35899 has 'comparators' => (is => 'ro', isa => ArrayRef[InstanceOf['Attean::Algebra::Comparator']], required => 1);
  50         111  
  50         218  
547 50     50   14469
  50         119  
  50         7873  
548 50     50   321 my $self = shift;
  50         92  
  50         1301  
549 50     50   304 return sprintf('Order { %s }', join(', ', map { $_->as_string } @{ $self->comparators }));
  50         94  
  50         274  
550 50     50   25776 }
  50         98  
  50         209  
551             }
552              
553             =item * L<Attean::Algebra::BGP>
554              
555             =cut
556 0     0 0 0  
557             use Moo;
558 1     1 0 2 use Attean::RDF;
559 1 50       3 use Set::Scalar;
560 1         6 use AtteanX::SPARQL::Constants;
561             use AtteanX::SPARQL::Token;
562 0         0 use Types::Standard qw(ArrayRef ConsumerOf);
563             use namespace::clean;
564              
565             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree', 'Attean::API::CanonicalizingBindingSet';
566            
567 2     2 0 1569 has 'triples' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TriplePattern']], default => sub { [] });
568 2         7
569 2         78 my $self = shift;
570 2         70 my $set = Set::Scalar->new();
571 2         68 foreach my $t (@{ $self->triples }) {
572             my @vars = $t->referenced_variables();
573 2         65 $set->insert(@vars);
574 2 50       7 }
575 0         0 return $set->elements;
576             }
577 2         5
578 2         8 my $self = shift;
579 2         5 my @tokens;
580             my $dot = AtteanX::SPARQL::Token->dot;
581 2         26 foreach my $t (@{ $self->triples }) {
582             push(@tokens, $t->sparql_tokens->elements);
583             push(@tokens, $dot);
584             }
585             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
586             }
587            
588             my $self = shift;
589             return 'BGP { ' . join(', ', map { $_->as_string } @{ $self->triples }) . ' }';
590 50     50   136050 }
  50         101  
  50         236  
591 50     50   14421
  50         106  
  50         7112  
592 50     50   298 my $self = shift;
  50         92  
  50         1099  
593 50     50   212 return @{ $self->triples };
  50         84  
  50         254  
594 50     50   26323 }
  50         119  
  50         234  
595            
596             my $self = shift;
597             my ($algebra, $mapping) = $self->canonical_bgp_with_mapping();
598             my @proj = sort map { sprintf("(?v%03d AS $_)", $mapping->{$_}{id}) } grep { $mapping->{$_}{type} eq 'variable' } (keys %$mapping);
599             foreach my $var (keys %$mapping) {
600             $algebra = Attean::Algebra::Extend->new(
601 0     0 0 0 children => [$algebra],
602             variable => variable($var),
603 1     1 0 2 expression => Attean::ValueExpression->new( value => variable($mapping->{$var}{id}) ),
604 1         2 );
  1         3  
  1         4  
605             }
606             }
607            
608             my $self = shift;
609             my ($triples, $mapping) = $self->canonical_set_with_mapping();
610             my $algebra = Attean::Algebra::BGP->new( triples => $triples );
611             return ($algebra, $mapping);
612             }
613 50     50   132152 }
  50         114  
  50         245  
614 50     50   14675  
  50         109  
  50         405  
615 50     50   61203 =item * L<Attean::Algebra::Service>
  50         467134  
  50         1952  
616 50     50   366  
  50         100  
  50         6615  
617 50     50   316 =cut
  50         100  
  50         1064  
618 50     50   229  
  50         82  
  50         383  
619 50     50   28762 use AtteanX::SPARQL::Constants;
  50         114  
  50         362  
620             use AtteanX::SPARQL::Token;
621             use Moo;
622             use Types::Standard qw(ConsumerOf Bool);
623             use namespace::clean;
624              
625             with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree', 'Attean::API::UnionScopeVariables';
626 77     77 0 125  
627 77         475 has 'endpoint' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariable'], required => 1);
628 77         5154 has 'silent' => (is => 'ro', isa => Bool, default => 0);
  77         258  
629 91         1200
630 91         294 my $self = shift;
631             my $endpoint = $self->endpoint->as_sparql;
632 77         3230 chomp($endpoint);
633             return sprintf('Service %s', $endpoint);
634             }
635              
636 81     81 0 155  
637 81         114 my $self = shift;
638 81         246 my $service = AtteanX::SPARQL::Token->keyword('SERVICE');
639 81         3113 my $l = AtteanX::SPARQL::Token->lbrace;
  81         291  
640 91         279 my $r = AtteanX::SPARQL::Token->rbrace;
641 91         248 my ($child) = @{ $self->children };
642            
643 81         1024 my @tokens;
644             push(@tokens, $service);
645             if ($self->silent) {
646             push(@tokens, AtteanX::SPARQL::Token->keyword('SILENT'));
647 19     19 0 27 }
648 19         26 push(@tokens, $self->endpoint->sparql_tokens->elements);
  24         67  
  19         48  
649             push(@tokens, $l);
650             push(@tokens, $child->sparql_subtokens->elements);
651             push(@tokens, $r);
652 4     4 0 5 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
653 4         5 }
  4         13  
654             }
655              
656             =item * L<Attean::Algebra::Path>
657 0     0 0 0  
658 0         0 =cut
659 0         0  
  0         0  
  0         0  
660 0         0 use Moo;
661             use AtteanX::SPARQL::Constants;
662             use AtteanX::SPARQL::Token;
663             use Types::Standard qw(ArrayRef ConsumerOf);
664 0         0 use namespace::clean;
665              
666             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree';
667              
668             has 'subject' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariableOrTriplePattern'], required => 1);
669             has 'path' => (is => 'ro', isa => ConsumerOf['Attean::API::PropertyPath'], required => 1);
670 4     4 0 1058 has 'object' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariableOrTriplePattern'], required => 1);
671 4         19  
672 4         56 my $self = shift;
673 4         10 my @vars = map { $_->value } grep { $_->does('Attean::API::Variable') } ($self->subject, $self->object);
674             return Set::Scalar->new(@vars)->elements;
675 5     5 0 13 }
676              
677              
678             my $self = shift;
679             return 'Path { ' . join(', ', map { $_->as_string } map { $self->$_() } qw(subject path object)) . ' }';
680             }
681              
682             my $self = shift;
683 50     50   170602 my @tokens;
  50         120  
  50         6488  
684 50     50   297 foreach my $t ($self->subject, $self->path, $self->object) {
  50         100  
  50         853  
685 50     50   222 push(@tokens, $t->sparql_tokens->elements);
  50         86  
  50         360  
686 50     50   16835 }
  50         107  
  50         302  
687 50     50   27070 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         102  
  50         238  
688             }
689             }
690              
691             =item * L<Attean::Algebra::Group>
692              
693             =cut
694              
695 1     1 0 2 use utf8;
696 1         20 use Moo;
697 1         18 use Attean::API::Query;
698 1         8 use AtteanX::SPARQL::Constants;
699             use AtteanX::SPARQL::Token;
700             use Types::Standard qw(ArrayRef ConsumerOf);
701 0     0 0 0 use namespace::clean;
702            
703             with 'Attean::API::SPARQLQuerySerializable';
704 3     3 0 9 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
705 3         12  
706 3         116 has 'groupby' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Expression']]);
707 3         102 has 'aggregates' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::AggregateExpression']]);
708 3         96
  3         11  
709             my $self = shift;
710 3         5 foreach my $a (@{ $self->aggregates }) {
711 3         6 my $op = $a->operator;
712 3 50       9 if ($op eq 'RANK') {
713 0         0 if (scalar(@{ $self->aggregates }) > 1) {
714             die "Cannot use both aggregates and RANKing in grouping operator";
715 3         50 }
716 3         8 }
717 3         9 }
718 3         8 }
719 3         41
720             my $self = shift;
721             my $aggs = $self->aggregates // [];
722             my $groups = $self->groupby // [];
723             my %vars;
724             foreach my $a (@$aggs) {
725             $vars{ $a->variable->value }++;
726             }
727             foreach my $e (@$groups) {
728 50     50   139413 if ($e->isa('Attean::ValueExpression')) {
  50         104  
  50         245  
729 50     50   15120 my $value = $e->value;
  50         102  
  50         7016  
730 50     50   307 if ($value->does('Attean::API::Variable')) {
  50         94  
  50         1117  
731 50     50   227 $vars{ $value->value }++;
  50         97  
  50         288  
732 50     50   26666 }
  50         102  
  50         240  
733             }
734             }
735            
736             return keys %vars;
737             }
738            
739             my $self = shift;
740             my @aggs;
741 2     2 0 3 my $aggs = $self->aggregates // [];
742 2         8 my $groups = $self->groupby // [];
  2         38  
  4         30  
743 2         5 foreach my $a (@$aggs) {
744             my $v = $a->variable->as_string;
745             my $op = $a->operator;
746 0     0 0 0 my $d = $a->distinct ? "DISTINCT " : '';
747             my ($e) = ((map { $_->as_string } @{ $a->children }), '');
748             my $s = "$v ← ${op}($d$e)";
749 1     1 0 2 push(@aggs, $s);
750 1         2 }
  3         35  
  3         8  
751             return sprintf('Group { %s } aggregate { %s }', join(', ', map { $_->as_string() } @$groups), join(', ', @aggs));
752             }
753              
754 13     13 0 22 }
755 13         16  
756 13         51 =item * L<Attean::Algebra::NegatedPropertySet>
757 39         105  
758             =cut
759 13         170  
760             use Moo;
761             use AtteanX::SPARQL::Constants;
762             use AtteanX::SPARQL::Token;
763             use Types::Standard qw(ArrayRef ConsumerOf);
764             use namespace::clean;
765              
766             with 'Attean::API::PropertyPath';
767              
768 50     50   140749 has 'predicates' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::IRI']], required => 1);
  50         109  
  50         369  
769 50     50   1054
  50         98  
  50         228  
770 50     50   14964 my $self = shift;
  50         96  
  50         1092  
771 50     50   221 return sprintf("!(%s)", join('|', map { $_->ntriples_string } @{ $self->predicates }));
  50         88  
  50         7069  
772 50     50   316 }
  50         88  
  50         1214  
773 50     50   242 my $self = shift;
  50         94  
  50         279  
774 50     50   27086 return "!(" . join('|', map { $_->as_sparql } @{$self->predicates}) . ")";
  50         122  
  50         245  
775             }
776              
777             my $self = shift;
778             my $bang = AtteanX::SPARQL::Token->op_bang;
779             my $or = AtteanX::SPARQL::Token->path_or;
780             my $l = AtteanX::SPARQL::Token->lparen;
781             my $r = AtteanX::SPARQL::Token->rparen;
782              
783 9     9 0 181 my @tokens;
784 9         12 push(@tokens, $bang, $l);
  9         27  
785 9         20 foreach my $t (@{ $self->predicates }) {
786 9 100       41 push(@tokens, $t->sparql_tokens->elements);
787 3 50       4 push(@tokens, $or);
  3         20  
788 0         0 }
789             pop(@tokens); # remove last OR token
790             push(@tokens, $r);
791             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
792             }
793             }
794              
795 4     4 0 7 =item * L<Attean::Algebra::PredicatePath>
796 4   50     10  
797 4   50     7 =cut
798 4         6  
799 4         6 use Moo;
800 4         24 use Types::Standard qw(ConsumerOf);
801             use namespace::clean;
802 4         6  
803 4 50       10 with 'Attean::API::PropertyPath';
804 4         7  
805 4 50       8 has 'predicate' => (is => 'ro', isa => ConsumerOf['Attean::API::IRI'], required => 1);
806 4         54 my $self = shift;
807             return $self->predicate->ntriples_string;
808             }
809             my $self = shift;
810             return 'Property Path ' . $self->as_string;
811 4         12 }
812             my $self = shift;
813             return $self->predicate->as_sparql;
814             }
815 6     6 0 7  
816 6         7 my $self = shift;
817 6   50     16 return $self->predicate->sparql_tokens;
818 6   50     14 }
819 6         10
820 6         24 }
821 6         13  
822 6 50       16 =item * L<Attean::Algebra::InversePath>
823 6         7  
  6         16  
  6         16  
824 6         26 =cut
825 6         14  
826             use Moo;
827 6         11 use AtteanX::SPARQL::Constants;
  6         11  
828             use AtteanX::SPARQL::Token;
829             use Types::Standard qw(ConsumerOf);
830 0     0 0 0 use namespace::clean;
831              
832             with 'Attean::API::UnaryPropertyPath';
833              
834             my $self = shift;
835             my ($path) = @{ $self->children };
836             return '^' . $self->path->as_sparql;
837             }
838 50     50   149578  
  50         102  
  50         249  
839 50     50   15015 my $self = shift;
  50         99  
  50         6984  
840 50     50   310 my $hat = AtteanX::SPARQL::Token->path_hat;
  50         83  
  50         1130  
841 50     50   237 my $l = AtteanX::SPARQL::Token->lparen;
  50         95  
  50         313  
842 50     50   26681 my $r = AtteanX::SPARQL::Token->rparen;
  50         100  
  50         238  
843              
844             my @tokens;
845             foreach my $t (@{ $self->children }) {
846             push(@tokens, $t->sparql_tokens->elements);
847             }
848            
849 1     1 0 360 if (scalar(@tokens) > 1) {
850 1         2 unshift(@tokens, $hat, $l);
  2         35  
  1         4  
851             push(@tokens, $r);
852 1     1 0 3 } else {
853 1     1 0 15 unshift(@tokens, $hat);
854             }
855 0     0 0 0
856 0         0 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  0         0  
  0         0  
857             }
858             }
859              
860 2     2 0 109 =item * L<Attean::Algebra::SequencePath>
861 2         11  
862 2         83 =cut
863 2         69  
864 2         65 use Moo;
865             use AtteanX::SPARQL::Constants;
866 2         64 use AtteanX::SPARQL::Token;
867 2         3 use namespace::clean;
868 2         3  
  2         6  
869 4         10 with 'Attean::API::NaryPropertyPath';
870 4         11  
871             my $self = shift;
872 2         4 my @paths = @{ $self->children };
873 2         3 return '(' . join('/', map { $_->as_sparql } @paths) . ')';
874 2         26 }
875              
876             my $self = shift;
877             my $slash = AtteanX::SPARQL::Token->slash;
878              
879             my @tokens;
880             foreach my $t (@{ $self->children }) {
881             push(@tokens, $t->sparql_tokens->elements);
882             push(@tokens, $slash);
883 50     50   141072 }
  50         114  
  50         252  
884 50     50   15951 pop(@tokens); # remove last SLASH token
  50         97  
  50         276  
885 50     50   21164 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         105  
  50         238  
886             }
887             }
888              
889             =item * L<Attean::Algebra::AlternativePath>
890              
891 18     18 0 374 =cut
892 18         259  
893             use Moo;
894             use AtteanX::SPARQL::Constants;
895 4     4 0 5 use AtteanX::SPARQL::Token;
896 4         7 use namespace::clean;
897              
898 4     4 0 6 with 'Attean::API::NaryPropertyPath';
899              
900 0     0 0 0 my $self = shift;
901 0         0 my @paths = @{ $self->children };
902             return '(' . join('|', map { $_->as_sparql } @paths) . ')';
903             }
904              
905 36     36 0 116 my $self = shift;
906 36         106 my $or = AtteanX::SPARQL::Token->path_or;
907              
908             my @tokens;
909             foreach my $t (@{ $self->children }) {
910             push(@tokens, $t->sparql_tokens->elements);
911             push(@tokens, $or);
912             }
913             pop(@tokens); # remove last OR token
914             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
915             }
916 50     50   31629 }
  50         100  
  50         214  
917 50     50   14489  
  50         114  
  50         7864  
918 50     50   329 =item * L<Attean::Algebra::ZeroOrMorePath>
  50         109  
  50         1313  
919 50     50   267  
  50         112  
  50         265  
920 50     50   20061 =cut
  50         131  
  50         212  
921              
922             use Moo;
923             use AtteanX::SPARQL::Constants;
924 4     4 0 20 use AtteanX::SPARQL::Token;
925             use Types::Standard qw(ConsumerOf);
926 0     0 0 0 use namespace::clean;
927 0         0  
  0         0  
928 0         0 with 'Attean::API::UnaryPropertyPath';
929              
930             my $self = shift;
931             my ($path) = @{ $self->children };
932 10     10 0 1677 return $self->path->as_sparql . '*';
933 10         27 }
934 10         386
935 10         329 my $self = shift;
936             my $star = AtteanX::SPARQL::Token->star;
937 10         322 my $l = AtteanX::SPARQL::Token->lparen;
938 10         11 my $r = AtteanX::SPARQL::Token->rparen;
  10         27  
939 10         24  
940             my @tokens;
941             foreach my $t (@{ $self->children }) {
942 10 100       22 push(@tokens, $t->sparql_tokens->elements);
943 8         18 }
944 8         13
945             if (scalar(@tokens) > 1) {
946 2         3 unshift(@tokens, $l);
947             push(@tokens, $r);
948             }
949 10         123 push(@tokens, $star);
950            
951             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
952             }
953             }
954              
955             =item * L<Attean::Algebra::OneOrMorePath>
956              
957             =cut
958 50     50   133964  
  50         134  
  50         229  
959 50     50   14159 use Moo;
  50         103  
  50         6612  
960 50     50   303 use AtteanX::SPARQL::Constants;
  50         101  
  50         777  
961 50     50   193 use AtteanX::SPARQL::Token;
  50         89  
  50         228  
962             use Types::Standard qw(ConsumerOf);
963             use namespace::clean;
964              
965 5     5 0 12 with 'Attean::API::UnaryPropertyPath';
966              
967 0     0 0 0 my $self = shift;
968 0         0 my ($path) = @{ $self->children };
  0         0  
969 0         0 return $self->path->as_sparql . '+';
  0         0  
970             }
971              
972             my $self = shift;
973 6     6 0 1390 my $plus = AtteanX::SPARQL::Token->op_plus;
974 6         20 my $l = AtteanX::SPARQL::Token->lparen;
975             my $r = AtteanX::SPARQL::Token->rparen;
976 6         219  
977 6         8 my @tokens;
  6         16  
978 10         20 foreach my $t (@{ $self->children }) {
979 10         21 push(@tokens, $t->sparql_tokens->elements);
980             }
981 6         9
982 6         73 if (scalar(@tokens) > 1) {
983             unshift(@tokens, $l);
984             push(@tokens, $r);
985             }
986             push(@tokens, $plus);
987            
988             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
989             }
990             }
991 50     50   130904  
  50         133  
  50         224  
992 50     50   14455 =item * L<Attean::Algebra::ZeroOrOnePath>
  50         104  
  50         6474  
993 50     50   348  
  50         108  
  50         784  
994 50     50   205 =cut
  50         89  
  50         226  
995              
996             use Moo;
997             use AtteanX::SPARQL::Constants;
998 2     2 0 8 use AtteanX::SPARQL::Token;
999             use Types::Standard qw(ConsumerOf);
1000 0     0 0 0 use namespace::clean;
1001 0         0  
  0         0  
1002 0         0 with 'Attean::API::UnaryPropertyPath';
  0         0  
1003              
1004             my $self = shift;
1005             my ($path) = @{ $self->children };
1006 12     12 0 1576 return $self->path->as_sparql . '?';
1007 12         36 }
1008              
1009 12         418 my $self = shift;
1010 12         14 my $q = AtteanX::SPARQL::Token->question;
  12         31  
1011 22         61 my $l = AtteanX::SPARQL::Token->lparen;
1012 22         51 my $r = AtteanX::SPARQL::Token->rparen;
1013              
1014 12         15 my @tokens;
1015 12         149 foreach my $t (@{ $self->children }) {
1016             push(@tokens, $t->sparql_tokens->elements);
1017             }
1018            
1019             if (scalar(@tokens) > 1) {
1020             unshift(@tokens, $l);
1021             push(@tokens, $r);
1022             }
1023             push(@tokens, $q);
1024 50     50   131631
  50         128  
  50         246  
1025 50     50   13891 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         94  
  50         6322  
1026 50     50   301 }
  50         109  
  50         981  
1027 50     50   229 }
  50         95  
  50         284  
1028 50     50   21882  
  50         107  
  50         223  
1029             =item * L<Attean::Algebra::Table>
1030              
1031             =cut
1032 3     3 0 11  
1033             use Moo;
1034 0     0 0 0 use AtteanX::SPARQL::Constants;
1035 0         0 use AtteanX::SPARQL::Token;
  0         0  
1036 0         0 use Types::Standard qw(ArrayRef ConsumerOf);
1037             use namespace::clean;
1038              
1039             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree';
1040 2     2 0 1614  
1041 2         9 has variables => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Variable']]);
1042 2         75 has rows => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Result']]);
1043 2         65  
1044             my $self = shift;
1045 2         62 return map { $_->value } @{ $self->variables };
1046 2         3 }
  2         6  
1047 2         3  
1048             my $self = shift;
1049             my $values = AtteanX::SPARQL::Token->keyword('VALUES');
1050 2 50       6 my $lparen = AtteanX::SPARQL::Token->lparen;
1051 2         3 my $rparen = AtteanX::SPARQL::Token->rparen;
1052 2         3 my $lbrace = AtteanX::SPARQL::Token->lbrace;
1053             my $rbrace = AtteanX::SPARQL::Token->rbrace;
1054 2         4  
1055             my @tokens;
1056 2         26 push(@tokens, $values);
1057             push(@tokens, $lparen);
1058             foreach my $var (@{ $self->variables }) {
1059             push(@tokens, $var->sparql_tokens->elements);
1060             }
1061             push(@tokens, $rparen);
1062            
1063             push(@tokens, $lbrace);
1064             foreach my $row (@{ $self->rows }) {
1065 50     50   134480 push(@tokens, $lparen);
  50         103  
  50         256  
1066 50     50   14458 foreach my $val ($row->values) {
  50         96  
  50         6484  
1067 50     50   292 # TODO: verify correct serialization of UNDEF
  50         103  
  50         950  
1068 50     50   227 push(@tokens, $val->sparql_tokens->elements);
  50         92  
  50         265  
1069 50     50   21895 }
  50         111  
  50         223  
1070             push(@tokens, $rparen);
1071             }
1072             push(@tokens, $rbrace);
1073 0     0 0 0
1074             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
1075 0     0 0 0 }
1076 0         0 }
  0         0  
1077 0         0  
1078             =item * L<Attean::Algebra::Ask>
1079              
1080             =cut
1081 2     2 0 1591  
1082 2         8 use Moo;
1083 2         102 use AtteanX::SPARQL::Constants;
1084 2         83 use AtteanX::SPARQL::Token;
1085             use namespace::clean;
1086 2         64
1087 2         2 with 'Attean::API::SPARQLQuerySerializable';
  2         7  
1088 2         5 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
1089            
1090              
1091 2 50       5 }
1092 2         4  
1093 2         3 =item * L<Attean::Algebra::Construct>
1094              
1095 2         4 =cut
1096              
1097 2         26 use Moo;
1098             use AtteanX::SPARQL::Constants;
1099             use AtteanX::SPARQL::Token;
1100             use Types::Standard qw(ArrayRef ConsumerOf);
1101             use namespace::clean;
1102            
1103             with 'Attean::API::SPARQLQuerySerializable';
1104             with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
1105              
1106 50     50   133597 has 'triples' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TriplePattern']]);
  50         106  
  50         302  
1107 50     50   14736  
  50         120  
  50         6559  
1108 50     50   320 my $self = shift;
  50         112  
  50         1091  
1109 50     50   244 my $triples = $self->triples;
  50         100  
  50         266  
1110 50     50   21954 return sprintf('Construct { %s }', join(' . ', map { $_->as_string } @$triples));
  50         140  
  50         222  
1111             }
1112             }
1113              
1114 0     0 0 0 =item * L<Attean::Algebra::Describe>
1115              
1116 0     0 0 0 =cut
1117 0         0  
  0         0  
1118 0         0 use Moo;
1119             use AtteanX::SPARQL::Constants;
1120             use AtteanX::SPARQL::Token;
1121             use Types::Standard qw(ArrayRef ConsumerOf);
1122 2     2 0 1642 use namespace::clean;
1123 2         9
1124 2         89 with 'Attean::API::SPARQLQuerySerializable';
1125 2         68 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
1126              
1127 2         64 has 'terms' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TermOrVariable']]);
1128 2         3  
  2         7  
1129 2         5 }
1130              
1131             =item * L<Attean::Algebra::Load>
1132 2 50       6  
1133 2         4 =cut
1134 2         2  
1135             use Moo;
1136 2         3 use AtteanX::SPARQL::Constants;
1137             use AtteanX::SPARQL::Token;
1138 2         26 use Types::Standard qw(Bool ConsumerOf);
1139             use namespace::clean;
1140            
1141             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree';
1142              
1143             has 'silent' => (is => 'ro', isa => Bool, default => 0);
1144             has 'url' => (is => 'ro', isa => ConsumerOf['Attean::API::IRI'], required => 1);
1145             has 'graph' => (is => 'ro', isa => ConsumerOf['Attean::API::Term'], predicate => 'has_graph');
1146              
1147 50     50   133212 my $self = shift;
  50         117  
  50         241  
1148 50     50   14439 return 'Load ' . $self->url->as_string;
  50         120  
  50         6434  
1149 50     50   314 }
  50         99  
  50         967  
1150 50     50   240  
  50         83  
  50         279  
1151 50     50   26538 my $self = shift;
  50         108  
  50         237  
1152              
1153             my @tokens;
1154             push(@tokens, AtteanX::SPARQL::Token->keyword('LOAD'));
1155             if ($self->silent) {
1156             push(@tokens, AtteanX::SPARQL::Token->keyword('SILENT'));
1157             }
1158             push(@tokens, $self->url->sparql_tokens->elements);
1159 2     2 0 3
1160 2         3 if ($self->has_graph) {
  2         5  
  2         6  
1161             push(@tokens, AtteanX::SPARQL::Token->keyword('INTO'));
1162 0     0 0 0 push(@tokens, AtteanX::SPARQL::Token->keyword('GRAPH'));
1163 1     1 0 2 push(@tokens, $self->graph->sparql_tokens->elements);
1164             }
1165             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
1166 2     2 0 7 }
1167 2         9 }
1168 2         84  
1169 2         72 =item * L<Attean::Algebra::Clear>
1170 2         69  
1171 2         67 =cut
1172              
1173 2         66 use Moo;
1174 2         3 use Scalar::Util qw(blessed);
1175 2         2 use AtteanX::SPARQL::Constants;
1176 2         3 use AtteanX::SPARQL::Token;
  2         6  
1177 2         6 use Types::Standard qw(Enum Bool ConsumerOf);
1178             use namespace::clean;
1179 2         4
1180             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree';
1181 2         4  
1182 2         2 has 'drop' => (is => 'ro', isa => Bool, default => 0);
  2         6  
1183 4         8 has 'silent' => (is => 'ro', isa => Bool, default => 0);
1184 4         10 has 'target' => (is => 'ro', isa => Enum[qw(GRAPH DEFAULT NAMED ALL)], required => 1);
1185             has 'graph' => (is => 'ro', isa => ConsumerOf['Attean::API::Term']);
1186 4         12  
1187             my $self = shift;
1188 4         8 if ($self->target eq 'GRAPH') {
1189             unless (blessed($self->graph)) {
1190 2         2 die "Attean::Algebra::Clear operations with a GRAPH target must include a graph IRI";
1191             }
1192 2         26 }
1193             }
1194              
1195             my $self = shift;
1196             return $self->drop ? 'Drop' : 'Clear';
1197             }
1198              
1199             my $self = shift;
1200              
1201 50     50   140701 my @tokens;
  50         106  
  50         255  
1202 50     50   14516 push(@tokens, AtteanX::SPARQL::Token->keyword($self->drop ? 'DROP' : 'CLEAR'));
  50         106  
  50         6854  
1203 50     50   302 if ($self->silent) {
  50         164  
  50         852  
1204 50     50   232 push(@tokens, AtteanX::SPARQL::Token->keyword('SILENT'));
  50         95  
  50         221  
1205             }
1206            
1207             if ($self->target =~ /^(DEFAULT|NAMED|ALL)$/) {
1208             push(@tokens, AtteanX::SPARQL::Token->keyword($self->target));
1209 0     0 0 0 } else {
1210             push(@tokens, AtteanX::SPARQL::Token->keyword('GRAPH'));
1211 1     1 0 3 push(@tokens, $self->graph->sparql_tokens->elements);
1212             }
1213             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
1214             }
1215             }
1216              
1217             =item * L<Attean::Algebra::Create>
1218              
1219 50     50   125048 =cut
  50         106  
  50         241  
1220 50     50   14176  
  50         110  
  50         6556  
1221 50     50   289 use Moo;
  50         109  
  50         1058  
1222 50     50   219 use Scalar::Util qw(blessed);
  50         99  
  50         281  
1223 50     50   26342 use AtteanX::SPARQL::Constants;
  50         111  
  50         210  
1224             use AtteanX::SPARQL::Token;
1225             use Types::Standard qw(Bool ConsumerOf);
1226             use namespace::clean;
1227            
1228             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree';
1229              
1230 0     0 0 0 has 'silent' => (is => 'ro', isa => Bool, default => 0);
1231 0     0 0 0 has 'graph' => (is => 'ro', isa => ConsumerOf['Attean::API::Term'], required => 1);
1232              
1233 3     3 0 5  
1234 3         8 my $self = shift;
1235 3         4  
  3         10  
1236             my @tokens;
1237             push(@tokens, AtteanX::SPARQL::Token->keyword('CREATE'));
1238             if ($self->silent) {
1239             push(@tokens, AtteanX::SPARQL::Token->keyword('SILENT'));
1240             }
1241             push(@tokens, AtteanX::SPARQL::Token->keyword('GRAPH'));
1242             push(@tokens, $self->graph->sparql_tokens->elements);
1243             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
1244 50     50   133539 }
  50         140  
  50         237  
1245 50     50   15710 }
  50         91  
  50         6314  
1246 50     50   287  
  50         98  
  50         954  
1247 50     50   218 =item * L<Attean::Algebra::Add>
  50         98  
  50         267  
1248 50     50   25695  
  50         106  
  50         261  
1249             =cut
1250              
1251             use Moo;
1252             use Scalar::Util qw(blessed);
1253             use AtteanX::SPARQL::Constants;
1254             use AtteanX::SPARQL::Token;
1255 0     0 0 0 use Types::Standard qw(Enum Bool ConsumerOf);
1256 0     0 0 0 use namespace::clean;
1257 0     0 0 0
1258             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree';
1259              
1260             has 'silent' => (is => 'ro', isa => Bool, default => 0);
1261             has 'drop_source' => (is => 'ro', isa => Bool, default => 0);
1262             has 'drop_destination' => (is => 'ro', isa => Bool, default => 0);
1263            
1264             has 'source' => (is => 'ro', isa => ConsumerOf['Attean::API::Term'], predicate => 'has_source');
1265 50     50   127886 has 'destination' => (is => 'ro', isa => ConsumerOf['Attean::API::Term'], predicate => 'has_destination');
  50         108  
  50         240  
1266 50     50   14368  
  50         122  
  50         6516  
1267 50     50   320 my $self = shift;
  50         108  
  50         1076  
1268 50     50   239 return ($self->drop_source and $self->drop_destination) ? 'Move' : ($self->drop_destination) ? 'Copy' : 'Add';
  50         99  
  50         254  
1269 50     50   27000 }
  50         101  
  50         215  
1270              
1271             my $self = shift;
1272              
1273             my @tokens;
1274             my $op = ($self->drop_source and $self->drop_destination) ? 'MOVE' : ($self->drop_destination) ? 'COPY' : 'ADD';
1275             push(@tokens, AtteanX::SPARQL::Token->keyword($op));
1276             if ($self->silent) {
1277 0     0 0 0 push(@tokens, AtteanX::SPARQL::Token->keyword('SILENT'));
1278 0     0 0 0 }
1279            
1280 0     0 0 0 if ($self->has_source) {
1281 0         0 push(@tokens, AtteanX::SPARQL::Token->keyword('GRAPH'));
1282             push(@tokens, $self->source->sparql_tokens->elements);
1283             } else {
1284             push(@tokens, AtteanX::SPARQL::Token->keyword('DEFAULT'));
1285 0     0 0 0 }
1286              
1287 0         0 push(@tokens, AtteanX::SPARQL::Token->keyword('TO'));
1288 0         0
1289 0 0       0 if ($self->has_destination) {
1290 0         0 push(@tokens, AtteanX::SPARQL::Token->keyword('GRAPH'));
1291             push(@tokens, $self->destination->sparql_tokens->elements);
1292 0         0 } else {
1293             push(@tokens, AtteanX::SPARQL::Token->keyword('DEFAULT'));
1294 0 0       0 }
1295 0         0
1296 0         0 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
1297 0         0 }
1298             }
1299 0         0  
1300             =item * L<Attean::Algebra::Modify>
1301              
1302             =cut
1303              
1304             use Moo;
1305             use Scalar::Util qw(blessed);
1306             use AtteanX::SPARQL::Constants;
1307             use AtteanX::SPARQL::Token;
1308 50     50   136036 use List::MoreUtils qw(all any);
  50         123  
  50         269  
1309 50     50   15722 use Types::Standard qw(HashRef ArrayRef ConsumerOf);
  50         102  
  50         2238  
1310 50     50   270 use namespace::clean;
  50         115  
  50         6613  
1311 50     50   319
  50         108  
  50         1103  
1312 50     50   241 with 'Attean::API::Algebra', 'Attean::API::QueryTree';
  50         108  
  50         290  
1313 50     50   31215  
  50         103  
  50         231  
1314             has 'dataset' => (is => 'ro', isa => HashRef, default => sub { +{} });
1315             has 'insert' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TripleOrQuadPattern']], default => sub { [] });
1316             has 'delete' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TripleOrQuadPattern']], default => sub { [] });
1317              
1318              
1319             my $self = shift;
1320             my $i = scalar(@{ $self->insert });
1321             my $d = scalar(@{ $self->delete });
1322             my $w = scalar(@{ $self->children });
1323 0     0 0 0 my $ig = all { $_->is_ground } @{ $self->insert };
1324 0 0       0 my $dg = all { $_->is_ground } @{ $self->delete };
1325 0 0       0 if ($i and not $d) {
1326 0         0 # INSERT
1327             return ($ig and not $w) ? 'ID' : 'I';
1328             } elsif ($d and not $i) {
1329             # DELETE
1330             return ($dg and not $w) ? 'DD' : 'D';
1331 0     0 0 0 } else {
1332 0     0 0 0 # INSERT + DELETE
1333             return 'U'
1334 0     0 0 0 }
1335 0 0       0 }
1336            
1337             around 'blank_nodes' => sub {
1338             my $orig = shift;
1339 0     0 0 0 my $self = shift;
1340             my @blanks = $orig->($self, @_);
1341 0         0 my %seen = map { $_->value => 1 } @blanks;
1342 0 0       0 foreach my $data ($self->insert, $self->delete) {
1343 0 0       0 my @triples = @{ $data };
1344 0         0 my @b = grep { $_->does('Attean::API::Blank') } map { $_->values } @triples;
1345             push(@blanks, grep { not $seen{$_->value}++ } @b);
1346             }
1347 0 0       0 return @blanks;
1348 0         0 };
1349            
1350 0         0 my $self = shift;
1351 0         0 my $level = shift;
1352             my $indent = ' ' x ($level + 1);
1353 0         0 state $S = {
1354             'ID' => 'Insert Data',
1355             'I' => 'Insert',
1356             'DD' => 'Delete Data',
1357             'D' => 'Delete',
1358             'U' => 'Update',
1359             };
1360             my $op = $self->_op_type();
1361             my $s = $S->{ $op };
1362 50     50   147779 my @data;
  50         113  
  50         248  
1363 50     50   14563 my $ic = scalar(@{ $self->insert });
  50         101  
  50         2015  
1364 50     50   268 my $dc = scalar(@{ $self->delete });
  50         108  
  50         6894  
1365 50     50   323 if ($ic) {
  50         100  
  50         1210  
1366 50     50   232 my $name = $dc ? 'Insert Data' : 'Data';
  50         128  
  50         290  
1367 50     50   26622 push(@data, [$name, $self->insert]);
  50         100  
  50         254  
1368             }
1369             if ($dc) {
1370             my $name = $ic ? 'Delete Data' : 'Data';
1371             push(@data, [$name, $self->delete]);
1372             }
1373             foreach my $data (@data) {
1374 0     0 0 0 my ($name, $quads) = @$data;
1375 0     0 0 0 $s .= "\n-${indent} $name";
1376 0     0 0 0 foreach my $q (@$quads) {
1377             $s .= "\n-${indent} " . $q->as_string;
1378             }
1379 0     0 0 0 }
1380             return $s;
1381 0         0 }
1382 0         0  
1383 0 0       0 my $self = shift;
1384 0         0 my $op = $self->_op_type();
1385             my $l = AtteanX::SPARQL::Token->lbrace;
1386 0         0 my $r = AtteanX::SPARQL::Token->rbrace;
1387 0         0 my $dot = AtteanX::SPARQL::Token->dot;
1388 0         0 my $data = AtteanX::SPARQL::Token->keyword('DATA');
1389             my $insert = AtteanX::SPARQL::Token->keyword('INSERT');
1390             my $delete = AtteanX::SPARQL::Token->keyword('DELETE');
1391             my $where = AtteanX::SPARQL::Token->keyword('WHERE');
1392             my $using = AtteanX::SPARQL::Token->keyword('USING');
1393             my $named = AtteanX::SPARQL::Token->keyword('NAMED');
1394            
1395             # TODO: Support 'DELETE WHERE' shortcut syntax
1396             # TODO: Support WITH
1397 50     50   137380
  50         111  
  50         233  
1398 50     50   15320 my @dataset;
  50         105  
  50         2054  
1399 50     50   260 my $dataset = $self->dataset;
  50         93  
  50         6239  
1400 50     50   317 my @default = @{ $dataset->{default} || [] };
  50         97  
  50         1077  
1401 50     50   228 my @named = values %{ $dataset->{named} || {} };
  50         90  
  50         264  
1402 50     50   31566 if (scalar(@default) or scalar(@named)) {
  50         101  
  50         231  
1403             foreach my $g (sort { $a->as_string cmp $b->as_string } @default) {
1404             push(@dataset, $using, $g->sparql_tokens->elements);
1405             }
1406             foreach my $g (sort { $a->as_string cmp $b->as_string } @named) {
1407             push(@dataset, $using, $named, $g->sparql_tokens->elements);
1408             }
1409             }
1410            
1411             my @tokens;
1412             if ($op eq 'ID' or $op eq 'DD') {
1413 1     1 0 4 my $statements = ($op eq 'ID') ? $self->insert : $self->delete;
1414 0     0 0 0 my $kw = ($op eq 'ID') ? $insert : $delete;
1415             push(@tokens, $kw);
1416 3     3 0 6 push(@tokens, $data);
1417 3 50 33     15 push(@tokens, $l);
    50          
1418             foreach my $t (@{ $statements }) {
1419             push(@tokens, $t->sparql_tokens->elements);
1420             push(@tokens, $dot);
1421 0     0 0 0 }
1422             push(@tokens, $r);
1423 0         0 } elsif ($op eq 'I' or $op eq 'D') {
1424 0 0 0     0 my $statements = ($op eq 'I') ? $self->insert : $self->delete;
    0          
1425 0         0 my $kw = ($op eq 'I') ? $insert : $delete;
1426 0 0       0 push(@tokens, $kw);
1427 0         0 push(@tokens, $l);
1428             foreach my $t (@{ $statements }) {
1429             push(@tokens, $t->sparql_tokens->elements);
1430 0 0       0 push(@tokens, $dot);
1431 0         0 }
1432 0         0 push(@tokens, $r);
1433             push(@tokens, @dataset);
1434 0         0 push(@tokens, $where);
1435             push(@tokens, $l);
1436             foreach my $c (@{ $self->children }) {
1437 0         0 push(@tokens, $c->sparql_tokens->elements);
1438             }
1439 0 0       0 push(@tokens, $r);
1440 0         0 } else {
1441 0         0 foreach my $x ([$delete, $self->delete], [$insert, $self->insert]) {
1442             my ($kw, $statements) = @$x;
1443 0         0 push(@tokens, $kw);
1444             push(@tokens, $l);
1445             foreach my $t (@{ $statements }) {
1446 0         0 push(@tokens, $t->sparql_tokens->elements);
1447             push(@tokens, $dot);
1448             }
1449             push(@tokens, $r);
1450             }
1451             push(@tokens, @dataset);
1452             push(@tokens, $where);
1453             push(@tokens, $l);
1454             foreach my $c (@{ $self->children }) {
1455 50     50   144386 push(@tokens, $c->sparql_tokens->elements);
  50         127  
  50         263  
1456 50     50   15053 }
  50         111  
  50         2244  
1457 50     50   306 push(@tokens, $r);
  50         88  
  50         6752  
1458 50     50   351 }
  50         118  
  50         1130  
1459 50     50   234
  50         82  
  50         495  
1460 50     50   48429 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         112  
  50         214  
1461 50     50   31240 }
  50         113  
  50         272  
1462             }
1463              
1464             1;
1465              
1466              
1467             =back
1468              
1469 1     1 0 5 =head1 BUGS
1470 0     0 0 0  
1471             Please report any bugs or feature requests to through the GitHub web interface
1472             at L<https://github.com/kasei/attean/issues>.
1473 8     8   10  
1474 8         11 =head1 SEE ALSO
  8         24  
1475 8         9  
  8         19  
1476 8         9  
  8         16  
1477 8     2   29  
  2         9  
  8         56  
1478 8     6   38 =head1 AUTHOR
  6         22  
  8         31  
1479 8 100 66     60  
    50 33        
1480             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
1481 2 100 66     11  
1482             =head1 COPYRIGHT
1483              
1484 6 100 66     25 Copyright (c) 2014--2022 Gregory Todd Williams.
1485             This program is free software; you can redistribute it and/or modify it under
1486             the same terms as Perl itself.
1487 0         0  
1488             =cut