Attean
view release on metacpan or search on metacpan
lib/Attean/Plan.pm view on Meta::CPAN
}
}
foreach my $rows (@buckets) {
foreach my $r (@$rows) {
if (my $j = $l->join($r)) {
$seen++;
if ($left) {
# TODO: filter with expression
push(@results, $j);
} else {
push(@results, $j);
}
}
}
}
if ($left and not($seen)) {
push(@results, $l);
}
}
return Attean::ListIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
values => \@results
);
}
}
}
=item * L<Attean::Plan::Construct>
=cut
package Attean::Plan::Construct 0.038 {
use Moo;
use List::Util qw(all);
use Types::Standard qw(Str ArrayRef ConsumerOf InstanceOf);
use namespace::clean;
has 'triples' => (is => 'ro', 'isa' => ArrayRef[ConsumerOf['Attean::API::TripleOrQuadPattern']], required => 1);
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::UnaryQueryTree';
sub plan_as_string {
my $self = shift;
my $triples = $self->triples;
return sprintf('Construct { %s }', join(' . ', map { $_->as_string } @$triples));
}
sub BUILDARGS {
# TODO: this code is repeated in several plan classes; figure out a way to share it.
my $class = shift;
my %args = @_;
my %vars = map { $_ => 1 } map { @{ $_->in_scope_variables } } @{ $args{ children } };
my @vars = keys %vars;
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = \@vars;
return $class->SUPER::BUILDARGS(%args);
}
sub impl {
my $self = shift;
my $model = shift;
my @children = map { $_->impl($model) } @{ $self->children };
return $self->_impl($model, @children);
}
sub substitute_impl {
my $self = shift;
my $model = shift;
my $b = shift;
unless (all { $_->does('Attean::API::BindingSubstitutionPlan') } @{ $self->children }) {
die "Plan children do not all consume BindingSubstitutionPlan role:\n" . $self->as_string;
}
warn "TODO: fix substitute_impl to substitute construct triples";
my @children = map { $_->substitute_impl($model, $b) } @{ $self->children };
return $self->_impl($model, @children);
}
# replace blank nodes in all the triple patterns with fresh ones
sub refresh_triples {
my $self = shift;
my @t;
my %mapping;
foreach my $t (@_) {
foreach my $term ($t->values) {
if ($term->does('Attean::API::Blank')) {
$mapping{$term->as_string} = Attean::Blank->new();
}
}
}
my $mapper = Attean::TermMap->rewrite_map(\%mapping);
foreach my $t (@_) {
push(@t, $t->apply_map($mapper));
}
return @t;
}
sub _impl {
my $self = shift;
my $model = shift;
my $child = shift;
my @triples = @{ $self->triples };
return sub {
my $iter = $child->();
my @buffer;
my %seen;
return Attean::CodeIterator->new(
item_type => 'Attean::API::Triple',
generator => sub {
if (scalar(@buffer)) {
return shift(@buffer);
}
while (my $row = $iter->next) {
foreach my $tp ($self->refresh_triples(@triples)) {
lib/Attean/Plan.pm view on Meta::CPAN
$iter = $current->();
} else {
undef $iter;
}
}
}
},
);
} else {
return Attean::ListIterator->new( item_type => 'Attean::API::Result', variables => [], values => [], );
}
};
}
}
=item * L<Attean::Plan::Extend>
Evaluates a sub-plan, and extends each result by evaluating a set of
expressions, binding the produced values to new variables.
=cut
package Attean::Plan::Extend 0.038 {
use Moo;
use Encode;
use UUID::Tiny ':std';
use URI::Escape;
use Data::Dumper;
use I18N::LangTags;
use POSIX qw(ceil floor);
use Digest::SHA;
use Digest::MD5 qw(md5_hex);
use Scalar::Util qw(blessed looks_like_number);
use List::Util qw(uniq all);
use Types::Standard qw(ConsumerOf ArrayRef InstanceOf HashRef);
use namespace::clean;
with 'MooX::Log::Any';
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::UnaryQueryTree';
has 'expressions' => (is => 'ro', isa => HashRef[ConsumerOf['Attean::API::Expression']], required => 1);
has 'active_graphs' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::IRI']], required => 1);
sub plan_as_string {
my $self = shift;
my @strings = map { sprintf('?%s â %s', $_, $self->expressions->{$_}->as_string) } keys %{ $self->expressions };
return sprintf('Extend { %s }', join(', ', @strings));
}
sub tree_attributes { return qw(variable expression) };
sub BUILDARGS {
my $class = shift;
my %args = @_;
my $exprs = $args{ expressions };
my @vars = map { @{ $_->in_scope_variables } } @{ $args{ children } };
my @evars = (@vars, keys %$exprs);
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = [@evars];
return $class->SUPER::BUILDARGS(%args);
}
sub evaluate_expression {
my $self = shift;
my $model = shift;
my $expr = shift;
my $r = shift;
Carp::confess unless ($expr->can('operator'));
my $op = $expr->operator;
state $true = Attean::Literal->true;
state $false = Attean::Literal->false;
state $type_roles = { qw(URI IRI IRI IRI BLANK Blank LITERAL Literal NUMERIC NumericLiteral TRIPLE Triple) };
state $type_classes = { qw(URI Attean::IRI IRI Attean::IRI STR Attean::Literal) };
if ($expr->isa('Attean::CastExpression')) {
my $datatype = $expr->datatype->value;
my ($child) = @{ $expr->children };
my $term = $self->evaluate_expression($model, $child, $r);
if ($datatype =~ m<^http://www.w3.org/2001/XMLSchema#string$>) {
my $value = $term->value;
if ($term->does('Attean::API::IRI')) {
return Attean::Literal->new(value => $term->value);
} elsif ($term->datatype->value eq 'http://www.w3.org/2001/XMLSchema#boolean') {
my $v = ($value eq 'true' or $value eq '1') ? 'true' : 'false';
return Attean::Literal->new(value => $v);
} elsif ($term->does('Attean::API::NumericLiteral')) {
my $v = $term->numeric_value();
if ($v == int($v)) {
return Attean::Literal->new(value => int($v));
}
}
return Attean::Literal->new(value => $value);
}
die "TypeError $op" unless (blessed($term) and $term->does('Attean::API::Literal'));
if ($datatype =~ m<^http://www.w3.org/2001/XMLSchema#(integer|float|double|decimal)>) {
my $value = $term->value;
my $num;
if ($datatype eq 'http://www.w3.org/2001/XMLSchema#integer') {
if ($term->datatype->value eq 'http://www.w3.org/2001/XMLSchema#boolean') {
$value = ($value eq 'true' or $value eq '1') ? '1' : '0';
} elsif ($term->does('Attean::API::NumericLiteral')) {
my $v = $term->numeric_value();
$v =~ s/[.].*$//;
$value = int($v);
} elsif ($value =~ /^[-+]\d+$/) {
my ($v) = "$value";
$v =~ s/[.].*$//;
$value = int($v);
}
$num = $value;
} elsif ($datatype eq 'http://www.w3.org/2001/XMLSchema#decimal') {
if ($term->datatype->value eq 'http://www.w3.org/2001/XMLSchema#boolean') {
$value = ($value eq 'true') ? '1' : '0';
} elsif ($term->does('Attean::API::NumericLiteral')) {
$value = $term->numeric_value;
} elsif (looks_like_number($value)) {
lib/Attean/Plan.pm view on Meta::CPAN
=cut
package Attean::Plan::Slice 0.038 {
use Moo;
use Types::Standard qw(Int);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
has 'limit' => (is => 'ro', isa => Int, default => -1);
has 'offset' => (is => 'ro', isa => Int, default => 0);
sub plan_as_string {
my $self = shift;
my @str;
push(@str, "Limit=" . $self->limit) if ($self->limit >= 0);
push(@str, "Offset=" . $self->offset) if ($self->offset > 0);
return sprintf('Slice { %s }', join(' ', @str));
}
sub impl {
my $self = shift;
my $model = shift;
my ($impl) = map { $_->impl($model) } @{ $self->children };
my $offset = $self->offset;
my $limit = $self->limit;
return sub {
my $iter = $impl->();
$iter = $iter->offset($offset) if ($offset > 0);
$iter = $iter->limit($limit) if ($limit >= 0);
return $iter;
};
}
}
=item * L<Attean::Plan::Project>
Evaluates a sub-plan and returns projected results by only keeping a fixed-set
of variable bindings in each result.
=cut
package Attean::Plan::Project 0.038 {
use Moo;
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::UnaryQueryTree';
use Types::Standard qw(ArrayRef ConsumerOf);
has 'variables' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Variable']], required => 1);
sub BUILDARGS {
my $class = shift;
my %args = @_;
my @vars = map { $_->value } @{ $args{variables} };
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = \@vars;
return $class->SUPER::BUILDARGS(%args);
}
# sub BUILD {
# my $self = shift;
# my @vars = map { $_->value } @{ $self->variables };
# unless (scalar(@vars)) {
# Carp::confess "No vars in project?";
# }
# }
sub plan_as_string {
my $self = shift;
return sprintf('Project { %s }', join(' ', map { '?' . $_->value } @{ $self->variables }));
}
sub tree_attributes { return qw(variables) };
sub substitute_impl {
my $self = shift;
my $model = shift;
my $bind = shift;
my ($impl) = map { $_->substitute_impl($model, $bind) } @{ $self->children };
my @vars = map { $_->value } @{ $self->variables };
my $iter_variables = $self->in_scope_variables;
# TODO: substitute variables in the projection where appropriate
return sub {
my $iter = $impl->();
return $iter->map(sub {
my $r = shift;
my $b = { map { my $t = $r->value($_); $t ? ($_ => $t) : () } @vars };
return Attean::Result->new( bindings => $b );
}, $iter->item_type, variables => $iter_variables);
};
}
sub impl {
my $self = shift;
my $model = shift;
my ($impl) = map { $_->impl($model) } @{ $self->children };
my @vars = map { $_->value } @{ $self->variables };
my $iter_variables = $self->in_scope_variables;
return sub {
my $iter = $impl->();
return $iter->map(sub {
my $r = shift;
my $b = { map { my $t = $r->value($_); $t ? ($_ => $t) : () } @vars };
return Attean::Result->new( bindings => $b );
}, $iter->item_type, variables => $iter_variables);
};
}
}
=item * L<Attean::Plan::OrderBy>
Evaluates a sub-plan and returns the results after fully materializing and
sorting is applied.
=cut
lib/Attean/Plan.pm view on Meta::CPAN
sub tree_attributes { return qw(endpoint) };
sub impl {
my $self = shift;
my $model = shift;
my $endpoint = $self->endpoint->value;
my $sparql = $self->sparql;
my $silent = $self->silent;
my %args = (
endpoint => $endpoint,
silent => $silent,
request_signer => $self->request_signer,
);
$args{user_agent} = $self->user_agent if ($self->user_agent);
my $client = $self->client || Attean::SPARQLClient->new(%args);
return sub {
return $client->query($sparql);
};
}
}
=item * L<Attean::Plan::Table>
Returns a constant set of results.
=cut
package Attean::Plan::Table 0.038 {
use Moo;
use Types::Standard qw(ArrayRef ConsumerOf);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
has variables => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Variable']]);
has rows => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Result']]);
sub tree_attributes { return qw(variables rows) };
sub plan_as_string {
my $self = shift;
my $level = shift;
my $indent = ' ' x ($level + 1);
my $vars = join(', ', map { "?$_" } @{ $self->in_scope_variables });
my $s = "Table (" . $vars . ")";
foreach my $row (@{ $self->rows }) {
$s .= "\n-${indent} " . $row->as_string;
}
return $s;
}
sub BUILDARGS {
my $class = shift;
my %args = @_;
my @vars = map { $_->value } @{ $args{variables} };
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = \@vars;
return $class->SUPER::BUILDARGS(%args);
}
sub impl {
my $self = shift;
my $model = shift;
my $rows = $self->rows;
my $iter_variables = $self->in_scope_variables;
return sub {
return Attean::ListIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
values => $rows
);
};
}
}
=item * L<Attean::Plan::Iterator>
Returns a constant set of results.
Be aware that if the iterator being wrapped is not repeatable (consuming the
L<Attean::API::RepeatableIterator> role), then this plan may only be evaluated
once.
A size estimate may be given if it is available. If the iterator is an
L<Attean::ListIterator>, the size of that iterator will be used.
=cut
package Attean::Plan::Iterator 0.038 {
use Moo;
use Types::Standard qw(ArrayRef ConsumerOf Int);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
has iterator => (is => 'ro', isa => ConsumerOf['Attean::API::ResultIterator']);
has size_estimate => (is => 'lazy', isa => Int, predicate => 1);
sub _build_size_estimate {
my $self = shift;
my $iter = $self->iterator;
if ($iter->isa('Attean::ListIterator')) {
return $iter->size;
}
}
sub tree_attributes { return qw(iterator) };
sub plan_as_string {
my $self = shift;
my $level = shift;
my $indent = ' ' x ($level + 1);
my $string = 'Iterator (';
$string .= join(', ', map { "?$_" } @{ $self->in_scope_variables });
if ($self->has_size_estimate) {
$string .= ' with ' . $self->size_estimate . ' elements';
}
$string .= ')';
return $string;
}
sub BUILDARGS {
my $class = shift;
my %args = @_;
my $vars = $args{iterator}->variables;
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = $vars;
return $class->SUPER::BUILDARGS(%args);
}
sub impl {
my $self = shift;
my $model = shift;
my $iter = $self->iterator;
return sub {
if ($iter->does('Attean::API::RepeatableIterator')) {
$iter->reset;
}
return $iter;
};
}
}
=item * L<Attean::Plan::ALPPath>
=cut
package Attean::Plan::ALPPath 0.038 {
use Moo;
use Attean::TreeRewriter;
use Types::Standard qw(ArrayRef ConsumerOf);
use namespace::clean;
has 'subject' => (is => 'ro', required => 1);
has 'object' => (is => 'ro', required => 1);
has 'graph' => (is => 'ro', required => 1);
has 'step_begin' => (is => 'ro', required => 1);
has 'step_end' => (is => 'ro', required => 1);
has 'skip' => (is => 'ro', required => 1, default => 0);
# has 'children' => (is => 'ro', isa => ConsumerOf['Attean::API::BindingSubstitutionPlan'], required => 1);
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::NullaryQueryTree';
sub tree_attributes { return qw(subject object graph) };
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
sub plan_as_string {
my $self = shift;
my @strings;
push(@strings, sprintf('%s â %s', map { $_->ntriples_string } ($self->subject, $self->step_begin)));
push(@strings, sprintf('%s â %s', map { $_->ntriples_string } ($self->object, $self->step_end)));
return sprintf('ALPPath %s', join(', ', @strings));
}
sub BUILDARGS {
my $class = shift;
my %args = @_;
my @vars = map { $_->value } grep { $_->does('Attean::API::Variable') } (@args{qw(subject object)});
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = \@vars;
return $class->SUPER::BUILDARGS(%args);
}
sub alp {
my $model = shift;
my $graph = shift;
my $skip = shift;
my $x = shift;
my $path = shift;
my $v = shift;
my $start = shift;
my $end = shift;
my $bind = shift;
if (exists $v->{$x->as_string}) {
return;
}
my $binding = Attean::Result->new( bindings => { $start => $x } )->join($bind);
unless ($binding) {
return;
}
if ($skip) {
$skip--;
} else {
$v->{$x->as_string} = $x;
}
my $impl = $path->substitute_impl($model, $binding);
my $iter = $impl->();
while (my $row = $iter->next()) {
my $n = $row->value($end);
alp($model, $graph, $skip, $n, $path, $v, $start, $end, $bind);
}
}
sub substitute_impl {
my $self = shift;
my $model = shift;
my $bind = shift;
my $path = $self->children->[0];
my $subject = $self->subject;
my $object = $self->object;
my $graph = $self->graph;
my $start = $self->step_begin->value;
my $end = $self->step_end->value;
my $skip = $self->skip;
my $iter_variables = $self->in_scope_variables;
for ($subject, $object) {
if ($_->does('Attean::API::Variable')) {
my $name = $_->value;
if (my $node = $bind->value($name)) {
$_ = $node;
}
}
}
my $s_var = $subject->does('Attean::API::Variable');
my $o_var = $object->does('Attean::API::Variable');
if ($s_var and $o_var) {
return sub {
lib/Attean/Plan.pm view on Meta::CPAN
values => \@rows,
);
};
} elsif ($o_var) {
return sub {
my %seen;
alp($model, $graph, $skip, $subject, $path, \%seen, $start, $end, $bind);
my @rows = map { Attean::Result->new( bindings => { $object->value => $_ } ) } (values %seen);
return Attean::ListIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
values => \@rows,
);
};
} elsif ($s_var) {
die "ALP for FB should never occur in a plan (should be inversed during planning)";
} else {
return sub {
my %seen;
alp($model, $graph, $skip, $subject, $path, \%seen, $start, $end, $bind);
if (exists $seen{ $object->as_string }) {
return Attean::ListIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
values => [Attean::Result->new()]
);
} else {
return Attean::ListIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
values => []
);
}
};
}
}
}
package Attean::Plan::ZeroOrOnePath 0.038 {
use Moo;
use Attean::TreeRewriter;
use Types::Standard qw(ArrayRef ConsumerOf);
use namespace::clean;
has 'subject' => (is => 'ro', required => 1);
has 'object' => (is => 'ro', required => 1);
has 'graph' => (is => 'ro', required => 1);
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::NullaryQueryTree';
sub BUILDARGS {
my $class = shift;
my %args = @_;
my @vars = map { $_->value } grep { $_->does('Attean::API::Variable') } (@args{qw(subject object)});
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = \@vars;
return $class->SUPER::BUILDARGS(%args);
}
sub tree_attributes { return qw(subject object) };
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
sub plan_as_string { return 'ZeroOrOnePath' }
sub substitute_impl {
my $self = shift;
my $model = shift;
my $bind = shift;
my ($impl) = map { $_->substitute_impl($model, $bind) } @{ $self->children };
my $iter_variables = $self->in_scope_variables;
my $subject = $self->subject;
my $object = $self->object;
my $graph = $self->graph;
for ($subject, $object) {
if ($_->does('Attean::API::Variable')) {
my $name = $_->value;
if (my $node = $bind->value($name)) {
$_ = $node;
}
}
}
my $s_var = $subject->does('Attean::API::Variable');
my $o_var = $object->does('Attean::API::Variable');
return sub {
my @extra;
if ($s_var and $o_var) {
my $nodes = $model->graph_nodes($graph);
while (my $n = $nodes->next) {
push(@extra, Attean::Result->new( bindings => { map { $_->value => $n } ($subject, $object) } ));
}
} elsif ($s_var) {
push(@extra, Attean::Result->new( bindings => { $subject->value => $object } ));
} elsif ($o_var) {
push(@extra, Attean::Result->new( bindings => { $object->value => $subject } ));
} else {
if (0 == $subject->compare($object)) {
push(@extra, Attean::Result->new( bindings => {} ));
}
}
my $iter = $impl->();
my %seen;
return Attean::CodeIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
generator => sub {
while (scalar(@extra)) {
my $r = shift(@extra);
unless ($seen{$r->as_string}++) {
return $r;
}
}
while (my $r = $iter->next()) {
return unless ($r);
if ($seen{$r->as_string}++) {
next;
lib/Attean/Plan.pm view on Meta::CPAN
my ($impl) = map { $_->impl($model) } @{ $self->children };
return sub {
my $iter = $impl->();
my $result = $iter->next;
# if ($result) {
# warn "EXISTS: " . $result->as_string;
# }
my $term = $result ? Attean::Literal->true : Attean::Literal->false;
return Attean::ListIterator->new(values => [$term], item_type => 'Attean::API::Term');
}
}
}
=item * L<Attean::Plan::Aggregate>
=cut
package Attean::Plan::Aggregate 0.038 {
use Moo;
use Encode;
use UUID::Tiny ':std';
use URI::Escape;
use I18N::LangTags;
use POSIX qw(ceil floor);
use Digest::SHA;
use Digest::MD5 qw(md5_hex);
use Scalar::Util qw(blessed);
use List::Util qw(uniq);
use Types::Standard qw(ConsumerOf InstanceOf HashRef ArrayRef);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
has 'aggregates' => (is => 'ro', isa => HashRef[ConsumerOf['Attean::API::Expression']], required => 1);
has 'groups' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Expression']], required => 1);
has 'active_graphs' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::IRI']], required => 1);
sub plan_as_string {
my $self = shift;
my @astrings = map { sprintf('?%s â %s', $_, $self->aggregates->{$_}->as_string) } keys %{ $self->aggregates };
my @gstrings = map { sprintf('%s', $_->as_string) } @{ $self->groups };
return sprintf('Aggregate { %s } Groups { %s }', join(', ', @astrings), join(', ', @gstrings));
}
sub tree_attributes { return qw(aggregates groups) };
sub BUILD {
# Ensure that the CT extensions are registered
AtteanX::Functions::CompositeLists->register();
AtteanX::Functions::CompositeMaps->register();
}
sub BUILDARGS {
my $class = shift;
my %args = @_;
my $aggs = $args{ aggregates };
my @vars = map { $_->value } grep { $_->does('Attean::API::Variable') } map { $_->value } @{ $args{groups} // [] };
my @evars = (@vars, keys %$aggs);
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = [@evars];
return $class->SUPER::BUILDARGS(%args);
}
sub evaluate_aggregate {
my $self = shift;
my $model = shift;
my $expr = shift;
my $rows = shift;
my $op = $expr->operator;
my ($e) = @{ $expr->children };
# my @children = map { Attean::Plan::Extend->evaluate_expression($model, $_, $r) } @{ $expr->children };
# warn "$op â " . join(' ', map { $_->as_string } @children);
if ($op eq 'COUNT') {
my $count = 0;
foreach my $r (@$rows) {
if ($e) {
my $term = Attean::Plan::Extend->evaluate_expression($model, $e, $r);
if ($term) {
$count++;
}
} else {
# This is the special-case branch for COUNT(*)
$count++;
}
}
return Attean::Literal->new(value => $count, datatype => 'http://www.w3.org/2001/XMLSchema#integer');
} elsif ($op eq 'SUM') {
my @cmp;
my @terms;
foreach my $r (@$rows) {
my $term = Attean::Plan::Extend->evaluate_expression($model, $e, $r);
if ($term->does('Attean::API::NumericLiteral')) {
push(@terms, $term);
}
}
my $lhs = shift(@terms);
while (my $rhs = shift(@terms)) {
my $type = $lhs->binary_promotion_type($rhs, '+');
my ($lv, $rv) = map { $_->numeric_value } ($lhs, $rhs);
$lhs = Attean::Literal->new(value => ($lv + $rv), datatype => $type);
}
return $lhs;
} elsif ($op eq 'AVG') {
my @cmp;
my $count = 0;
my $all_ints = 1;
my @terms;
if (scalar(@$rows) == 0) {
return Attean::Literal->integer(0);
}
foreach my $r (@$rows) {
my $term = Attean::Plan::Extend->evaluate_expression($model, $e, $r);
die unless ($term->does('Attean::API::NumericLiteral'));
push(@terms, $term);
$count++;
}
my $lhs = shift(@terms);
while (my $rhs = shift(@terms)) {
my $type = $lhs->binary_promotion_type($rhs, '+');
my ($lv, $rv) = map { $_->numeric_value } ($lhs, $rhs);
$lhs = Attean::Literal->new(value => ($lv + $rv), datatype => $type);
lib/Attean/Plan.pm view on Meta::CPAN
return $iter;
}
}
if ($silent) {
return Attean::ListIterator->new(values => [], item_type => 'Attean::API::Triple');
} else {
die "Failed to load url: " . $resp->status_line;
}
};
}
}
=item * L<Attean::Plan::Unfold>
=cut
package Attean::Plan::Unfold 0.032 {
use Moo;
use Encode;
use UUID::Tiny ':std';
use URI::Escape;
use Data::Dumper;
use I18N::LangTags;
use POSIX qw(ceil floor);
use Digest::SHA;
use Digest::MD5 qw(md5_hex);
use Scalar::Util qw(blessed looks_like_number);
use List::Util qw(uniq all);
use Types::Standard qw(ConsumerOf ArrayRef InstanceOf HashRef);
use namespace::clean;
with 'MooX::Log::Any';
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::UnaryQueryTree';
has 'variables' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Variable']], required => 1);
has 'expression' => (is => 'ro', isa => ConsumerOf['Attean::API::Expression'], required => 1);
has 'active_graphs' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::IRI']], required => 1);
sub plan_as_string {
my $self = shift;
my @vars = map { $_->as_string } @{ $self->variables };
my $vars = '(' . join(', ', @vars) . ')';
return sprintf('Unfold { %s â %s }', $vars, $self->expression->as_string);
}
sub tree_attributes { return qw(variable expression) };
sub BUILDARGS {
my $class = shift;
my %args = @_;
my $exprs = $args{ expressions };
my @vars = map { @{ $_->in_scope_variables } } @{ $args{ children } };
my @evars = (@vars, keys %$exprs);
if (exists $args{in_scope_variables}) {
Carp::confess "in_scope_variables is computed automatically, and must not be specified in the $class constructor";
}
$args{in_scope_variables} = [@evars];
return $class->SUPER::BUILDARGS(%args);
}
sub substitute_impl {
my $self = shift;
my $model = shift;
my $bind = shift;
my $expr = $self->expression;
my $vars = $self->variables;
my ($impl) = map { $_->substitute_impl($model, $bind) } @{ $self->children };
# TODO: substitute variables in the expression
return $self->_impl($model, $impl, $expr, @$vars);
}
sub impl {
my $self = shift;
my $model = shift;
my $expr = $self->expression;
my $vars = $self->variables;
my ($impl) = map { $_->impl($model) } @{ $self->children };
return $self->_impl($model, $impl, $expr, @$vars);
}
sub _impl {
my $self = shift;
my $model = shift;
my $impl = shift;
Carp::confess unless (defined($impl));
my $expr = shift;
my @vars = @_;
my $iter_variables = $self->in_scope_variables;
my $var = $vars[0];
my $index_var = $vars[1];
return sub {
my $iter = $impl->();
my @buffer;
return Attean::CodeIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
generator => sub {
if (scalar(@buffer)) {
return shift(@buffer);
}
ROW: while (my $r = $iter->next) {
my %base = map { $_ => $r->value($_) } $r->variables;
my $cdt = eval { Attean::Plan::Extend->evaluate_expression($model, $expr, $r) };
warn "UNFOLD expression evaluation error: $@" if ($@);
unless ($cdt) {
return $r;
}
if ($cdt->does('Attean::API::Literal') and $cdt->datatype->value eq $AtteanX::Functions::CompositeLists::LIST_TYPE_IRI) {
my @values = AtteanX::Functions::CompositeLists::lex_to_list($cdt);
foreach my $index (0 .. $#values) {
my %row = %base;
my $term = $values[$index];
if (blessed($term)) {
if ($row{ $var } and $term->as_string ne $row{ $var }->as_string) {
next ROW;
}
( run in 0.934 second using v1.01-cache-2.11-cpan-5a3173703d6 )