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   528 use v5.14;
  50         174  
2 50     50   262 use warnings;
  50         100  
  50         1249  
3 50     50   304 use utf8;
  50         112  
  50         413  
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.032
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   1551 use Attean::API::Query;
  50         122  
  50         2227  
28              
29             use AtteanX::SPARQL::Constants;
30 50     50   345 use AtteanX::SPARQL::Token;
  50         123  
  50         8537  
31 50     50   356 use Types::Standard qw(Bool ArrayRef HashRef ConsumerOf);
  50         125  
  50         1652  
32 50     50   288 use Moo;
  50         119  
  50         413  
33 50     50   37306 use namespace::clean;
  50         137  
  50         282  
34 50     50   16855  
  50         136  
  50         383  
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 13
43 9 50       34 my %dataset = %{ $self->dataset };
44             my @default = @{ $dataset{ default } || [] };
45 9         12 my @named = @{ $dataset{ named } || [] };
  9         30  
46 9 50       13 my $has_dataset = (scalar(@default) + scalar(@named));
  9         35  
47 9 50       17
  9         29  
48 9         18 my $s = $name;
49             if ($has_dataset) {
50 9         12 my @parts;
51 9 50       32 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         36  
62             my $self = shift;
63             my $child = $self->child;
64             my $l = AtteanX::SPARQL::Token->lbrace;
65 8     8 0 190 my $r = AtteanX::SPARQL::Token->rbrace;
66 8         45 my $from = AtteanX::SPARQL::Token->keyword('FROM');
67 8         49 my $named = AtteanX::SPARQL::Token->keyword('NAMED');
68 8         415  
69 8         348 my %dataset = %{ $self->dataset };
70 8         329 my @default = @{ $dataset{ default } || [] };
71             my @named = @{ $dataset{ named } || [] };
72 8         319 my $has_dataset = (scalar(@default) + scalar(@named));
  8         45  
73 8 100       18 if ($child->does('Attean::API::SPARQLQuerySerializable')) {
  8         53  
74 8 100       18 if ($self->subquery) {
  8         46  
75 8         22 my @tokens;
76 8 100       46 push(@tokens, $l);
77 7 50       145 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         14 }
85 7 100       20 return $child->query_tokens(%args);
86 4         15 }
87             } else {
88 7         44 my $sel = AtteanX::SPARQL::Token->keyword('SELECT');
89             my $star = AtteanX::SPARQL::Token->star;
90             my $where = AtteanX::SPARQL::Token->keyword('WHERE');
91 1         31
92 1         52 my @tokens;
93 1         44 if ($self->subquery) {
94             push(@tokens, $l);
95 1         40 }
96 1 50       8 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         3 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         4 push(@tokens, $r);
112 1         2 if ($self->subquery) {
113 1         6 push(@tokens, $r);
114 1         4 }
115 1 50       6 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
116 0         0 }
117             }
118 1         17 }
119              
120             use AtteanX::SPARQL::Constants;
121             use AtteanX::SPARQL::Token;
122             use Types::Standard qw(Bool);
123             use Moo;
124 50     50   169891 use namespace::clean;
  50         118  
  50         6735  
125 50     50   348  
  50         113  
  50         1026  
126 50     50   234 with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
  50         107  
  50         276  
127 50     50   21971  
  50         112  
  50         237  
128 50     50   16759  
  50         134  
  50         244  
129             my $self = shift;
130             my $child = $self->child;
131             return $child->sparql_tokens;
132 3     3 0 12 }
133             }
134              
135 6     6 0 19 =item * L<Attean::Algebra::Sequence>
136 6         28  
137 6         27 =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   131493 my $self = shift;
  50         125  
  50         6556  
147 50     50   303 return scalar(@{ $self->children });
  50         117  
  50         772  
148 50     50   284 }
  50         103  
  50         219  
149 50     50   14869
  50         118  
  50         238  
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 4 }
159             pop(@tokens); # remove last SEMICOLON token
160             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
161 1     1 0 3 }
162 1         8 }
163              
164 1         50 =item * L<Attean::Algebra::Join>
165 1         3  
  1         6  
166 2         7 =cut
167 2         11  
168             use AtteanX::SPARQL::Constants;
169 1         2 use AtteanX::SPARQL::Token;
170 1         17 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   136600  
  50         132  
  50         6525  
180 50     50   317 my @tokens;
  50         124  
  50         770  
181 50     50   237 push(@tokens, $l);
  50         100  
  50         228  
182 50     50   15475 foreach my $t (@{ $self->children }) {
  50         114  
  50         261  
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 31  
190 5         21 =item * L<Attean::Algebra::LeftJoin>
191 5         260  
192             =cut
193 5         204  
194 5         11 use AtteanX::SPARQL::Constants;
195 5         7 use AtteanX::SPARQL::Token;
  5         47  
196 8         31 use Moo;
197             use Types::Standard qw(ConsumerOf);
198 5         16 use namespace::clean;
199 5         76  
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   133854 my $l = AtteanX::SPARQL::Token->lbrace;
  50         131  
  50         6701  
209 50     50   321 my $r = AtteanX::SPARQL::Token->rbrace;
  50         105  
  50         774  
210 50     50   221 my ($lhs, $rhs) = @{ $self->children };
  50         151  
  50         223  
211 50     50   16035
  50         119  
  50         297  
212 50     50   22719 my @tokens;
  50         134  
  50         233  
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 18 $is_true = 1;
224 4         24 }
225 4         264 }
226 4         171
227 4         154 unless ($is_true) {
  4         18  
228             my $f = AtteanX::SPARQL::Token->keyword('FILTER');
229 4         12 my $lparen = AtteanX::SPARQL::Token->lparen;
230 4         9 my $rparen = AtteanX::SPARQL::Token->rparen;
231 4         15 push(@tokens, $f);
232 4         17 push(@tokens, $lparen);
233 4         16 push(@tokens, $expr->sparql_tokens->elements);
234             push(@tokens, $rparen);
235 4         24 }
236 4         11
237 4 100       30 push(@tokens, $r);
238 2         6 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
239 2 50       8 }
240 2         18
241             }
242              
243             =item * L<Attean::Algebra::Filter>
244 4 100       12  
245 2         9 =cut
246 2         99  
247 2         82 use AtteanX::SPARQL::Constants;
248 2         85 use AtteanX::SPARQL::Token;
249 2         3 use Moo;
250 2         9 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         10  
255 4         61 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   145362 my @tokens;
  50         126  
  50         7181  
266 50     50   346 push(@tokens, $child->sparql_tokens->elements);
  50         105  
  50         863  
267 50     50   239 push(@tokens, $f);
  50         108  
  50         273  
268 50     50   15871 push(@tokens, $l);
  50         119  
  50         330  
269 50     50   22029 push(@tokens, $expr->sparql_tokens->elements);
  50         133  
  50         238  
270             push(@tokens, $r);
271             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
272             }
273             }
274              
275 2     2 0 7 =item * L<Attean::Algebra::Union>
276 2         23  
277             =cut
278 0     0 0 0  
279             use AtteanX::SPARQL::Constants;
280 4     4 0 49 use AtteanX::SPARQL::Token;
281 4         19 use Moo;
282 4         220 use Types::Standard qw(ConsumerOf);
283 4         163 use namespace::clean;
284 4         187  
  4         14  
285 4         8 with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::BinaryQueryTree';
286 4         7  
287 4         10 my $self = shift;
288 4         13 my $union = AtteanX::SPARQL::Token->keyword('UNION');
289 4         7 my $l = AtteanX::SPARQL::Token->lbrace;
290 4         24 my $r = AtteanX::SPARQL::Token->rbrace;
291 4         13 my ($lhs, $rhs) = @{ $self->children };
292 4         60
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   137890 }
  50         140  
  50         6584  
302 50     50   330  
  50         102  
  50         776  
303 50     50   218 =item * L<Attean::Algebra::Graph>
  50         107  
  50         430  
304 50     50   26974  
  50         149  
  50         288  
305 50     50   22200 =cut
  50         104  
  50         251  
306              
307             use AtteanX::SPARQL::Constants;
308             use AtteanX::SPARQL::Token;
309 1     1 0 3 use Moo;
310             use Types::Standard qw(ConsumerOf);
311 2     2 0 11 use namespace::clean;
312 2         13  
313 2         125 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
314 2         87  
315 2         81 has 'graph' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariable'], required => 1);
  2         11  
316              
317 2         4 my $self = shift;
318 2         5 my $graph = $self->graph;
319 2         9 my ($child) = @{ $self->children };
320 2         7 my @vars = $child->in_scope_variables;
321 2         9 if ($graph->does('Attean::API::Variable')) {
322 2         6 return Set::Scalar->new(@vars, $graph->value)->elements;
323 2         29 } 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   137283 my $l = AtteanX::SPARQL::Token->lbrace;
  50         139  
  50         6574  
333 50     50   303 my $r = AtteanX::SPARQL::Token->rbrace;
  50         114  
  50         823  
334 50     50   211 my ($child) = @{ $self->children };
  50         308  
  50         8748  
335 50     50   15718
  50         105  
  50         280  
336 50     50   21323 my @tokens;
  50         314  
  50         232  
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 12 }
344 6         18 }
345 6         8  
  6         19  
346 6         19 =item * L<Attean::Algebra::Extend>
347 6 100       273  
348 2         55 =cut
349              
350 4         89 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 9 return Set::Scalar->new(@vars, $self->variable->value)->elements;
360 2         12 }
361 2         105 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
362 2         84  
363 2         77 has 'variable' => (is => 'ro', isa => ConsumerOf['Attean::API::Variable'], required => 1);
  2         9  
364             has 'expression' => (is => 'ro', isa => ConsumerOf['Attean::API::Expression'], required => 1);
365 2         4  
366 2         5 my $self = shift;
367 2         12 return sprintf('Extend { %s ← %s }', $self->variable->as_string, $self->expression->as_string);
368 2         6 }
369 2         10 my $self = shift;
370 2         8 my $bind = AtteanX::SPARQL::Token->keyword('BIND');
371 2         32 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   143456 push(@tokens, $bind);
  50         118  
  50         6498  
381 50     50   302 push(@tokens, $l);
  50         97  
  50         858  
382 50     50   491 push(@tokens, $expr->sparql_tokens->elements);
  50         90  
  50         226  
383 50     50   15903 push(@tokens, $as);
  50         106  
  50         556  
384 50     50   21919 push(@tokens, $var->sparql_tokens->elements);
  50         105  
  50         239  
385             push(@tokens, $r);
386             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
387 12     12 0 23 }
388 12         21 }
  12         33  
389 12         36  
390 12         461 =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 3 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 1121 my ($child) = @{ $self->children };
404 2         8 return $child->in_scope_variables;
405 2         98 }
406 2         81  
407 2         80 my $self = shift;
408 2         75 my $minus = AtteanX::SPARQL::Token->keyword('MINUS');
  2         10  
409 2         5 my $l = AtteanX::SPARQL::Token->lbrace;
410 2         8 my $r = AtteanX::SPARQL::Token->rbrace;
411             my ($lhs, $rhs) = @{ $self->children };
412 2         3
413 2         5 my @tokens;
414 2         8 push(@tokens, $l);
415 2         3 push(@tokens, $lhs->sparql_subtokens->elements);
416 2         9 push(@tokens, $r, $minus, $l);
417 2         6 push(@tokens, $rhs->sparql_subtokens->elements);
418 2         10 push(@tokens, $r);
419 2         11 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
420 2         33 }
421            
422             }
423              
424             =item * L<Attean::Algebra::Distinct>
425              
426             =cut
427              
428             use Moo;
429 50     50   145197 use namespace::clean;
  50         105  
  50         6653  
430 50     50   301  
  50         359  
  50         884  
431 50     50   216 with 'Attean::API::SPARQLQuerySerializable';
  50         94  
  50         227  
432 50     50   16143 with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
  50         117  
  50         274  
433 50     50   22239  
  50         110  
  50         230  
434             }
435              
436             =item * L<Attean::Algebra::Reduced>
437              
438 2     2 0 5 =cut
439 2         3  
  2         8  
440 2         5 use Moo;
441             use namespace::clean;
442              
443 1     1 0 3 with 'Attean::API::SPARQLQuerySerializable';
444             with 'Attean::API::UnionScopeVariables', 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
445 2     2 0 11  
446 2         12 }
447 2         121  
448 2         99 =item * L<Attean::Algebra::Slice>
449 2         82  
  2         9  
450             =cut
451 2         4  
452 2         4 use Moo;
453 2         11 use Types::Standard qw(Int);
454 2         8 use namespace::clean;
455 2         8  
456 2         6 with 'Attean::API::SPARQLQuerySerializable';
457 2         33 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   139859 }
  50         109  
  50         258  
468 50     50   15361  
  50         101  
  50         231  
469             =item * L<Attean::Algebra::Project>
470              
471             =cut
472              
473 1     1 0 5 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   23476  
  50         101  
  50         224  
482 50     50   14139 my $self = shift;
  50         135  
  50         236  
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 4 }
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   23770 =cut
  50         130  
  50         231  
496 50     50   14978  
  50         136  
  50         335  
497 50     50   23275 use Moo;
  50         114  
  50         230  
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   32022 my $l = AtteanX::SPARQL::Token->lparen;
  50         112  
  50         252  
519 50     50   25761 my $r = AtteanX::SPARQL::Token->rparen;
  50         106  
  50         250  
520 50     50   15401
  50         159  
  50         237  
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 4 }
529 2         3 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  2         5  
530 2         7 }
531 2         283 }
  4         12  
  2         96  
532 2         113  
533             =item * L<Attean::Algebra::OrderBy>
534              
535 5     5 0 8 =cut
536 5         9  
  8         46  
  5         15  
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   36861 has 'comparators' => (is => 'ro', isa => ArrayRef[InstanceOf['Attean::Algebra::Comparator']], required => 1);
  50         149  
  50         257  
547 50     50   15106
  50         105  
  50         8097  
548 50     50   398 my $self = shift;
  50         97  
  50         1291  
549 50     50   267 return sprintf('Order { %s }', join(', ', map { $_->as_string } @{ $self->comparators }));
  50         115  
  50         289  
550 50     50   26605 }
  50         103  
  50         211  
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         7 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 2074 has 'triples' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TriplePattern']], default => sub { [] });
568 2         9
569 2         109 my $self = shift;
570 2         83 my $set = Set::Scalar->new();
571 2         81 foreach my $t (@{ $self->triples }) {
572             my @vars = $t->referenced_variables();
573 2         76 $set->insert(@vars);
574 2 50       9 }
575 0         0 return $set->elements;
576             }
577 2         6
578 2         11 my $self = shift;
579 2         6 my @tokens;
580             my $dot = AtteanX::SPARQL::Token->dot;
581 2         31 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   142164 }
  50         103  
  50         250  
591 50     50   14835
  50         104  
  50         7055  
592 50     50   319 my $self = shift;
  50         91  
  50         1189  
593 50     50   246 return @{ $self->triples };
  50         86  
  50         258  
594 50     50   27007 }
  50         127  
  50         237  
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 3 expression => Attean::ValueExpression->new( value => variable($mapping->{$var}{id}) ),
604 1         2 );
  1         4  
  1         5  
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   136388 }
  50         105  
  50         239  
614 50     50   14806  
  50         109  
  50         410  
615 50     50   62612 =item * L<Attean::Algebra::Service>
  50         482019  
  50         2080  
616 50     50   377  
  50         108  
  50         6762  
617 50     50   309 =cut
  50         103  
  50         1110  
618 50     50   241  
  50         120  
  50         365  
619 50     50   28734 use AtteanX::SPARQL::Constants;
  50         112  
  50         350  
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 132  
627 77         474 has 'endpoint' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariable'], required => 1);
628 77         5617 has 'silent' => (is => 'ro', isa => Bool, default => 0);
  77         302  
629 91         1324
630 91         357 my $self = shift;
631             my $endpoint = $self->endpoint->as_sparql;
632 77         3584 chomp($endpoint);
633             return sprintf('Service %s', $endpoint);
634             }
635              
636 81     81 0 149  
637 81         121 my $self = shift;
638 81         315 my $service = AtteanX::SPARQL::Token->keyword('SERVICE');
639 81         3781 my $l = AtteanX::SPARQL::Token->lbrace;
  81         346  
640 91         313 my $r = AtteanX::SPARQL::Token->rbrace;
641 91         285 my ($child) = @{ $self->children };
642            
643 81         1256 my @tokens;
644             push(@tokens, $service);
645             if ($self->silent) {
646             push(@tokens, AtteanX::SPARQL::Token->keyword('SILENT'));
647 19     19 0 33 }
648 19         33 push(@tokens, $self->endpoint->sparql_tokens->elements);
  24         74  
  19         54  
649             push(@tokens, $l);
650             push(@tokens, $child->sparql_subtokens->elements);
651             push(@tokens, $r);
652 4     4 0 6 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
653 4         7 }
  4         15  
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 1397 has 'object' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariableOrTriplePattern'], required => 1);
671 4         17  
672 4         68 my $self = shift;
673 4         14 my @vars = map { $_->value } grep { $_->does('Attean::API::Variable') } ($self->subject, $self->object);
674             return Set::Scalar->new(@vars)->elements;
675 5     5 0 10 }
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   177406 my @tokens;
  50         116  
  50         6688  
684 50     50   342 foreach my $t ($self->subject, $self->path, $self->object) {
  50         101  
  50         890  
685 50     50   226 push(@tokens, $t->sparql_tokens->elements);
  50         88  
  50         336  
686 50     50   16537 }
  50         118  
  50         290  
687 50     50   26926 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         118  
  50         243  
688             }
689             }
690              
691             =item * L<Attean::Algebra::Group>
692              
693             =cut
694              
695 1     1 0 3 use utf8;
696 1         23 use Moo;
697 1         22 use Attean::API::Query;
698 1         10 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 13 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
705 3         17  
706 3         159 has 'groupby' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Expression']]);
707 3         134 has 'aggregates' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::AggregateExpression']]);
708 3         117
  3         14  
709             my $self = shift;
710 3         7 foreach my $a (@{ $self->aggregates }) {
711 3         7 my $op = $a->operator;
712 3 50       13 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         21 }
716 3         10 }
717 3         17 }
718 3         8 }
719 3         47
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   144995 if ($e->isa('Attean::ValueExpression')) {
  50         123  
  50         249  
729 50     50   15107 my $value = $e->value;
  50         113  
  50         7184  
730 50     50   314 if ($value->does('Attean::API::Variable')) {
  50         110  
  50         1110  
731 50     50   240 $vars{ $value->value }++;
  50         96  
  50         270  
732 50     50   26853 }
  50         106  
  50         288  
733             }
734             }
735            
736             return keys %vars;
737             }
738            
739             my $self = shift;
740             my @aggs;
741 2     2 0 5 my $aggs = $self->aggregates // [];
742 2         9 my $groups = $self->groupby // [];
  2         47  
  4         38  
743 2         6 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         57  
  3         9  
751             return sprintf('Group { %s } aggregate { %s }', join(', ', map { $_->as_string() } @$groups), join(', ', @aggs));
752             }
753              
754 13     13 0 28 }
755 13         22  
756 13         66 =item * L<Attean::Algebra::NegatedPropertySet>
757 39         117  
758             =cut
759 13         201  
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   146364 has 'predicates' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::IRI']], required => 1);
  50         115  
  50         377  
769 50     50   1113
  50         129  
  50         238  
770 50     50   15064 my $self = shift;
  50         110  
  50         1086  
771 50     50   231 return sprintf("!(%s)", join('|', map { $_->ntriples_string } @{ $self->predicates }));
  50         96  
  50         10978  
772 50     50   336 }
  50         101  
  50         1214  
773 50     50   240 my $self = shift;
  50         95  
  50         282  
774 50     50   27415 return "!(" . join('|', map { $_->as_sparql } @{$self->predicates}) . ")";
  50         112  
  50         235  
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 240 my @tokens;
784 9         16 push(@tokens, $bang, $l);
  9         46  
785 9         25 foreach my $t (@{ $self->predicates }) {
786 9 100       53 push(@tokens, $t->sparql_tokens->elements);
787 3 50       7 push(@tokens, $or);
  3         25  
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 5 =item * L<Attean::Algebra::PredicatePath>
796 4   50     12  
797 4   50     12 =cut
798 4         6  
799 4         7 use Moo;
800 4         16 use Types::Standard qw(ConsumerOf);
801             use namespace::clean;
802 4         8  
803 4 50       15 with 'Attean::API::PropertyPath';
804 4         8  
805 4 50       11 has 'predicate' => (is => 'ro', isa => ConsumerOf['Attean::API::IRI'], required => 1);
806 4         68 my $self = shift;
807             return $self->predicate->ntriples_string;
808             }
809             my $self = shift;
810             return 'Property Path ' . $self->as_string;
811 4         13 }
812             my $self = shift;
813             return $self->predicate->as_sparql;
814             }
815 6     6 0 9  
816 6         8 my $self = shift;
817 6   50     19 return $self->predicate->sparql_tokens;
818 6   50     19 }
819 6         12
820 6         27 }
821 6         13  
822 6 50       22 =item * L<Attean::Algebra::InversePath>
823 6         8  
  6         20  
  6         16  
824 6         29 =cut
825 6         18  
826             use Moo;
827 6         13 use AtteanX::SPARQL::Constants;
  6         16  
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   154284  
  50         140  
  50         252  
839 50     50   14996 my $self = shift;
  50         113  
  50         7393  
840 50     50   337 my $hat = AtteanX::SPARQL::Token->path_hat;
  50         115  
  50         1195  
841 50     50   246 my $l = AtteanX::SPARQL::Token->lparen;
  50         99  
  50         272  
842 50     50   27246 my $r = AtteanX::SPARQL::Token->rparen;
  50         116  
  50         234  
843              
844             my @tokens;
845             foreach my $t (@{ $self->children }) {
846             push(@tokens, $t->sparql_tokens->elements);
847             }
848            
849 1     1 0 420 if (scalar(@tokens) > 1) {
850 1         2 unshift(@tokens, $hat, $l);
  2         42  
  1         4  
851             push(@tokens, $r);
852 1     1 0 3 } else {
853 1     1 0 5 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 137 =item * L<Attean::Algebra::SequencePath>
861 2         12  
862 2         108 =cut
863 2         83  
864 2         79 use Moo;
865             use AtteanX::SPARQL::Constants;
866 2         74 use AtteanX::SPARQL::Token;
867 2         5 use namespace::clean;
868 2         3  
  2         9  
869 4         13 with 'Attean::API::NaryPropertyPath';
870 4         12  
871             my $self = shift;
872 2         4 my @paths = @{ $self->children };
873 2         4 return '(' . join('/', map { $_->as_sparql } @paths) . ')';
874 2         31 }
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   147271 }
  50         115  
  50         266  
884 50     50   16167 pop(@tokens); # remove last SLASH token
  50         117  
  50         277  
885 50     50   21674 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         122  
  50         244  
886             }
887             }
888              
889             =item * L<Attean::Algebra::AlternativePath>
890              
891 18     18 0 422 =cut
892 18         320  
893             use Moo;
894             use AtteanX::SPARQL::Constants;
895 4     4 0 6 use AtteanX::SPARQL::Token;
896 4         8 use namespace::clean;
897              
898 4     4 0 14 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 153 my $self = shift;
906 36         145 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   32512 }
  50         148  
  50         259  
917 50     50   14811  
  50         139  
  50         8094  
918 50     50   364 =item * L<Attean::Algebra::ZeroOrMorePath>
  50         123  
  50         1273  
919 50     50   266  
  50         117  
  50         309  
920 50     50   20633 =cut
  50         129  
  50         227  
921              
922             use Moo;
923             use AtteanX::SPARQL::Constants;
924 4     4 0 12 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 2526 return $self->path->as_sparql . '*';
933 10         37 }
934 10         533
935 10         396 my $self = shift;
936             my $star = AtteanX::SPARQL::Token->star;
937 10         381 my $l = AtteanX::SPARQL::Token->lparen;
938 10         14 my $r = AtteanX::SPARQL::Token->rparen;
  10         32  
939 10         37  
940             my @tokens;
941             foreach my $t (@{ $self->children }) {
942 10 100       27 push(@tokens, $t->sparql_tokens->elements);
943 8         21 }
944 8         12
945             if (scalar(@tokens) > 1) {
946 2         5 unshift(@tokens, $l);
947             push(@tokens, $r);
948             }
949 10         149 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   140258  
  50         125  
  50         266  
959 50     50   14622 use Moo;
  50         140  
  50         6917  
960 50     50   333 use AtteanX::SPARQL::Constants;
  50         102  
  50         902  
961 50     50   223 use AtteanX::SPARQL::Token;
  50         104  
  50         213  
962             use Types::Standard qw(ConsumerOf);
963             use namespace::clean;
964              
965 5     5 0 16 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 2191 my $plus = AtteanX::SPARQL::Token->op_plus;
974 6         30 my $l = AtteanX::SPARQL::Token->lparen;
975             my $r = AtteanX::SPARQL::Token->rparen;
976 6         299  
977 6         10 my @tokens;
  6         22  
978 10         31 foreach my $t (@{ $self->children }) {
979 10         32 push(@tokens, $t->sparql_tokens->elements);
980             }
981 6         13
982 6         89 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   136693  
  50         130  
  50         272  
992 50     50   15131 =item * L<Attean::Algebra::ZeroOrOnePath>
  50         120  
  50         6707  
993 50     50   311  
  50         100  
  50         778  
994 50     50   218 =cut
  50         98  
  50         219  
995              
996             use Moo;
997             use AtteanX::SPARQL::Constants;
998 2     2 0 7 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 2041 return $self->path->as_sparql . '?';
1007 12         40 }
1008              
1009 12         513 my $self = shift;
1010 12         19 my $q = AtteanX::SPARQL::Token->question;
  12         34  
1011 22         56 my $l = AtteanX::SPARQL::Token->lparen;
1012 22         65 my $r = AtteanX::SPARQL::Token->rparen;
1013              
1014 12         34 my @tokens;
1015 12         178 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   137557
  50         118  
  50         251  
1025 50     50   14578 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         127  
  50         6608  
1026 50     50   307 }
  50         98  
  50         1013  
1027 50     50   236 }
  50         91  
  50         275  
1028 50     50   22288  
  50         103  
  50         254  
1029             =item * L<Attean::Algebra::Table>
1030              
1031             =cut
1032 3     3 0 13  
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 2132  
1041 2         11 has variables => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Variable']]);
1042 2         103 has rows => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Result']]);
1043 2         90  
1044             my $self = shift;
1045 2         78 return map { $_->value } @{ $self->variables };
1046 2         3 }
  2         9  
1047 2         5  
1048             my $self = shift;
1049             my $values = AtteanX::SPARQL::Token->keyword('VALUES');
1050 2 50       9 my $lparen = AtteanX::SPARQL::Token->lparen;
1051 2         3 my $rparen = AtteanX::SPARQL::Token->rparen;
1052 2         5 my $lbrace = AtteanX::SPARQL::Token->lbrace;
1053             my $rbrace = AtteanX::SPARQL::Token->rbrace;
1054 2         5  
1055             my @tokens;
1056 2         30 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   139993 push(@tokens, $lparen);
  50         106  
  50         301  
1066 50     50   14839 foreach my $val ($row->values) {
  50         110  
  50         6733  
1067 50     50   317 # TODO: verify correct serialization of UNDEF
  50         127  
  50         1000  
1068 50     50   216 push(@tokens, $val->sparql_tokens->elements);
  50         100  
  50         253  
1069 50     50   22785 }
  50         115  
  50         248  
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 1775  
1082 2         11 use Moo;
1083 2         98 use AtteanX::SPARQL::Constants;
1084 2         81 use AtteanX::SPARQL::Token;
1085             use namespace::clean;
1086 2         76
1087 2         4 with 'Attean::API::SPARQLQuerySerializable';
  2         8  
1088 2         6 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
1089            
1090              
1091 2 50       12 }
1092 2         5  
1093 2         4 =item * L<Attean::Algebra::Construct>
1094              
1095 2         4 =cut
1096              
1097 2         31 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   139618 has 'triples' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TriplePattern']]);
  50         141  
  50         274  
1107 50     50   15454  
  50         123  
  50         6689  
1108 50     50   289 my $self = shift;
  50         104  
  50         1134  
1109 50     50   259 my $triples = $self->triples;
  50         109  
  50         267  
1110 50     50   22523 return sprintf('Construct { %s }', join(' . ', map { $_->as_string } @$triples));
  50         108  
  50         249  
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 1846 use namespace::clean;
1123 2         11
1124 2         97 with 'Attean::API::SPARQLQuerySerializable';
1125 2         85 with 'Attean::API::Algebra', 'Attean::API::UnaryQueryTree';
1126              
1127 2         75 has 'terms' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TermOrVariable']]);
1128 2         3  
  2         9  
1129 2         7 }
1130              
1131             =item * L<Attean::Algebra::Load>
1132 2 50       8  
1133 2         5 =cut
1134 2         3  
1135             use Moo;
1136 2         4 use AtteanX::SPARQL::Constants;
1137             use AtteanX::SPARQL::Token;
1138 2         61 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   140864 my $self = shift;
  50         118  
  50         253  
1148 50     50   14680 return 'Load ' . $self->url->as_string;
  50         121  
  50         6656  
1149 50     50   331 }
  50         100  
  50         1411  
1150 50     50   227  
  50         104  
  50         309  
1151 50     50   26782 my $self = shift;
  50         120  
  50         233  
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         7  
  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 4 push(@tokens, $self->graph->sparql_tokens->elements);
1164             }
1165             return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
1166 2     2 0 10 }
1167 2         14 }
1168 2         124  
1169 2         84 =item * L<Attean::Algebra::Clear>
1170 2         81  
1171 2         109 =cut
1172              
1173 2         95 use Moo;
1174 2         5 use Scalar::Util qw(blessed);
1175 2         2 use AtteanX::SPARQL::Constants;
1176 2         4 use AtteanX::SPARQL::Token;
  2         18  
1177 2         11 use Types::Standard qw(Enum Bool ConsumerOf);
1178             use namespace::clean;
1179 2         6
1180             with 'Attean::API::Algebra', 'Attean::API::NullaryQueryTree';
1181 2         5  
1182 2         4 has 'drop' => (is => 'ro', isa => Bool, default => 0);
  2         7  
1183 4         8 has 'silent' => (is => 'ro', isa => Bool, default => 0);
1184 4         13 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         31  
1187             my $self = shift;
1188 4         10 if ($self->target eq 'GRAPH') {
1189             unless (blessed($self->graph)) {
1190 2         9 die "Attean::Algebra::Clear operations with a GRAPH target must include a graph IRI";
1191             }
1192 2         31 }
1193             }
1194              
1195             my $self = shift;
1196             return $self->drop ? 'Drop' : 'Clear';
1197             }
1198              
1199             my $self = shift;
1200              
1201 50     50   146252 my @tokens;
  50         108  
  50         249  
1202 50     50   14807 push(@tokens, AtteanX::SPARQL::Token->keyword($self->drop ? 'DROP' : 'CLEAR'));
  50         116  
  50         6907  
1203 50     50   310 if ($self->silent) {
  50         113  
  50         940  
1204 50     50   240 push(@tokens, AtteanX::SPARQL::Token->keyword('SILENT'));
  50         100  
  50         219  
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   130569 =cut
  50         138  
  50         243  
1220 50     50   14667  
  50         122  
  50         6473  
1221 50     50   344 use Moo;
  50         114  
  50         1148  
1222 50     50   289 use Scalar::Util qw(blessed);
  50         100  
  50         279  
1223 50     50   27650 use AtteanX::SPARQL::Constants;
  50         114  
  50         234  
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         7 my $self = shift;
1235 3         7  
  3         8  
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   138695 }
  50         115  
  50         260  
1245 50     50   15807 }
  50         109  
  50         6717  
1246 50     50   294  
  50         97  
  50         996  
1247 50     50   228 =item * L<Attean::Algebra::Add>
  50         104  
  50         252  
1248 50     50   26687  
  50         114  
  50         247  
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   133972 has 'destination' => (is => 'ro', isa => ConsumerOf['Attean::API::Term'], predicate => 'has_destination');
  50         128  
  50         254  
1266 50     50   14594  
  50         116  
  50         6727  
1267 50     50   317 my $self = shift;
  50         99  
  50         1080  
1268 50     50   281 return ($self->drop_source and $self->drop_destination) ? 'Move' : ($self->drop_destination) ? 'Copy' : 'Add';
  50         109  
  50         276  
1269 50     50   27118 }
  50         112  
  50         223  
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   142096 use List::MoreUtils qw(all any);
  50         120  
  50         265  
1309 50     50   15979 use Types::Standard qw(HashRef ArrayRef ConsumerOf);
  50         111  
  50         2273  
1310 50     50   287 use namespace::clean;
  50         90  
  50         6642  
1311 50     50   332
  50         101  
  50         1196  
1312 50     50   244 with 'Attean::API::Algebra', 'Attean::API::QueryTree';
  50         110  
  50         329  
1313 50     50   32298  
  50         119  
  50         256  
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   152883 my @data;
  50         112  
  50         286  
1363 50     50   15028 my $ic = scalar(@{ $self->insert });
  50         106  
  50         2072  
1364 50     50   290 my $dc = scalar(@{ $self->delete });
  50         110  
  50         7110  
1365 50     50   364 if ($ic) {
  50         112  
  50         1149  
1366 50     50   251 my $name = $dc ? 'Insert Data' : 'Data';
  50         93  
  50         283  
1367 50     50   26973 push(@data, [$name, $self->insert]);
  50         120  
  50         239  
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   141724
  50         112  
  50         284  
1398 50     50   15717 my @dataset;
  50         121  
  50         2066  
1399 50     50   296 my $dataset = $self->dataset;
  50         121  
  50         6600  
1400 50     50   386 my @default = @{ $dataset->{default} || [] };
  50         136  
  50         1181  
1401 50     50   248 my @named = values %{ $dataset->{named} || {} };
  50         116  
  50         285  
1402 50     50   32075 if (scalar(@default) or scalar(@named)) {
  50         117  
  50         247  
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 5 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     18 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   150902 push(@tokens, $c->sparql_tokens->elements);
  50         142  
  50         265  
1456 50     50   15151 }
  50         114  
  50         2076  
1457 50     50   317 push(@tokens, $r);
  50         115  
  50         7103  
1458 50     50   335 }
  50         97  
  50         1247  
1459 50     50   286
  50         95  
  50         482  
1460 50     50   50111 return Attean::ListIterator->new( values => \@tokens, item_type => 'AtteanX::SPARQL::Token' );
  50         114  
  50         262  
1461 50     50   32132 }
  50         123  
  50         242  
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   16  
1474 8         13 =head1 SEE ALSO
  8         27  
1475 8         14  
  8         24  
1476 8         17  
  8         24  
1477 8     2   40  
  2         14  
  8         74  
1478 8     6   48 =head1 AUTHOR
  6         35  
  8         38  
1479 8 100 66     81  
    50 33        
1480             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
1481 2 100 66     18  
1482             =head1 COPYRIGHT
1483              
1484 6 100 66     33 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