Attean
view release on metacpan or search on metacpan
lib/Attean/Plan.pm view on Meta::CPAN
unless (defined($value)) {
$has_unbound_left_join_var++;
}
push(@values, $value);
}
my @buckets;
if (my $b = $hash{''}) {
push(@buckets, $b);
}
if ($has_unbound_left_join_var) {
my $pattern = join(',', map { ref($_) ? quotemeta($_->as_string) : '.*' } @values);
foreach my $key (keys %hash) {
if ($key =~ /^${pattern}$/) {
push(@buckets, $hash{$key});
}
}
} else {
my $key = join(',', map { ref($_) ? $_->as_string : '' } @values);
if (my $rows = $hash{$key}) {
push(@buckets, $rows);
}
}
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)) {
my $tp = $tp->apply_bindings($row);
my $t = eval { $tp->as_triple };
if ($t) {
push(@buffer, $t);
}
}
if (scalar(@buffer)) {
my $t = shift(@buffer);
return $t;
}
}
}
)->grep(sub {
return not $seen{$_->as_string}++;
});
}
}
}
=item * L<Attean::Plan::Describe>
=cut
package Attean::Plan::Describe 0.038 {
use Moo;
use Attean::RDF;
use List::Util qw(all);
use Types::Standard qw(Str ArrayRef ConsumerOf InstanceOf);
use namespace::clean;
has 'graph' => (is => 'ro');
has 'terms' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::TermOrVariable']]);
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::UnaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
sub plan_as_string {
my $self = shift;
my $terms = $self->terms;
return sprintf('Describe { %s }', join(' . ', map { $_->as_string } @$terms));
}
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 describe terms";
my @children = map { $_->substitute_impl($model, $b) } @{ $self->children };
return $self->_impl($model, @children);
}
sub _impl {
my $self = shift;
my $model = shift;
my $child = shift;
my $graph = $self->graph;
my @terms = @{ $self->terms };
# TODO: Split @terms into ground terms and variables.
# Only call get_quads once for ground terms.
# For variable terms, call get_quads for each variable-result combination.
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 $term (@terms) {
my $value = $term->apply_binding($row);
if ($value->does('Attean::API::Term')) {
my $iter = $model->get_quads( $value, variable('predicate'), variable('object'), $graph );
push(@buffer, $iter->elements);
}
if (scalar(@buffer)) {
return shift(@buffer);
}
lib/Attean/Plan.pm view on Meta::CPAN
has 'variable' => (is => 'ro', isa => Str, required => 1);
sub plan_as_string {
my $self = shift;
return sprintf('EBVFilter { ?%s }', $self->variable);
}
sub tree_attributes { return qw(expression) };
sub substitute_impl {
my $self = shift;
my $model = shift;
my $bind = shift;
my ($impl) = map { $_->substitute_impl($model, $bind) } @{ $self->children };
my $var = $self->variable;
return sub {
my $iter = $impl->();
return $iter->grep(sub {
my $r = shift;
my $term = $r->value($var);
return 0 unless (blessed($term) and $term->does('Attean::API::Term'));
return $term->ebv;
});
};
}
sub impl {
my $self = shift;
my $model = shift;
my ($impl) = map { $_->impl($model) } @{ $self->children };
my $var = $self->variable;
return sub {
my $iter = $impl->();
return $iter->grep(sub {
my $r = shift;
my $term = $r->value($var);
return 0 unless (blessed($term) and $term->does('Attean::API::Term'));
return $term->ebv;
});
};
}
}
=item * L<Attean::Plan::Merge>
Evaluates a set of sub-plans, returning the merged union of results, preserving
ordering.
=cut
package Attean::Plan::Merge 0.038 {
use Moo;
use Scalar::Util qw(blessed);
use Types::Standard qw(Str ArrayRef ConsumerOf);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::BinaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
has 'variables' => (is => 'ro', isa => ArrayRef[Str], required => 1);
sub plan_as_string { return 'Merge' }
sub impl {
my $self = shift;
my $model = shift;
my @children = map { $_->impl($model) } @{ $self->children };
return sub {
die "Unimplemented";
};
}
}
=item * L<Attean::Plan::Union>
Evaluates a set of sub-plans, returning the union of results.
=cut
package Attean::Plan::Union 0.038 {
use Moo;
use Scalar::Util qw(blessed);
use namespace::clean;
with 'Attean::API::BindingSubstitutionPlan', 'Attean::API::BinaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
sub plan_as_string { return 'Union' }
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;
}
my @children = map { $_->substitute_impl($model, $b) } @{ $self->children };
return $self->_impl($model, @children);
}
sub _impl {
my $self = shift;
my $model = shift;
my @children = @_;
my $iter_variables = $self->in_scope_variables;
return sub {
if (my $current = shift(@children)) {
my $iter = $current->();
return Attean::CodeIterator->new(
item_type => 'Attean::API::Result',
variables => $iter_variables,
generator => sub {
while (blessed($iter)) {
my $row = $iter->next();
if ($row) {
return $row;
} else {
$current = shift(@children);
if ($current) {
$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;
lib/Attean/Plan.pm view on Meta::CPAN
$last = $s;
return $ok;
});
};
}
}
=item * L<Attean::Plan::Slice>
Evaluates a sub-plan, and returns the results after optionally skipping some
number of results ("offset") and limiting the total number of returned results
("limit").
=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
package Attean::Plan::OrderBy 0.038 {
use Moo;
use Types::Standard qw(HashRef ArrayRef InstanceOf Bool Str);
use Scalar::Util qw(blessed);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
has 'variables' => (is => 'ro', isa => ArrayRef[Str], required => 1);
has 'ascending' => (is => 'ro', isa => HashRef[Bool], required => 1);
sub plan_as_string {
my $self = shift;
my @vars = @{ $self->variables };
my $ascending = $self->ascending;
my @strings = map { sprintf('%s(?%s)', ($ascending->{$_} ? 'ASC' : 'DESC'), $_) } @vars;
return sprintf('Order { %s }', join(', ', @strings));
}
sub sort_rows {
my $self = shift;
my $vars = shift;
my $ascending = shift;
my $rows = shift;
local($Attean::API::Binding::ALLOW_IRI_COMPARISON) = 1;
my @sorted = map { $_->[0] } sort {
my ($ar, $avalues) = @$a;
my ($br, $bvalues) = @$b;
my $c = 0;
foreach my $i (0 .. $#{ $vars }) {
my $ascending = $ascending->{ $vars->[$i] };
my ($av, $bv) = map { $_->[$i] } ($avalues, $bvalues);
# Mirrors code in Attean::SimpleQueryEvaluator->evaluate
if (blessed($av) and $av->does('Attean::API::Binding') and (not(defined($bv)) or not($bv->does('Attean::API::Binding')))) {
$c = 1;
} elsif (blessed($bv) and $bv->does('Attean::API::Binding') and (not(defined($av)) or not($av->does('Attean::API::Binding')))) {
$c = -1;
} else {
$c = eval { $av ? $av->compare($bv) : 1 };
if ($@) {
$c = 1;
}
}
$c *= -1 unless ($ascending);
last unless ($c == 0);
}
$c
} map {
my $r = $_;
[$r, [map { $r->value($_) } @$vars]]
} @$rows;
return @sorted;
}
sub impl {
my $self = shift;
my $model = shift;
my $vars = $self->variables;
my $ascending = $self->ascending;
my ($impl) = map { $_->impl($model) } @{ $self->children };
my $iter_variables = $self->in_scope_variables;
return sub {
my $iter = $impl->();
my @rows = $iter->elements;
my @sorted = $self->sort_rows($vars, $ascending, \@rows);
return Attean::ListIterator->new(
values => \@sorted,
variables => $iter_variables,
lib/Attean/Plan.pm view on Meta::CPAN
package Attean::Plan::Service 0.038 {
use Moo;
use Types::Standard qw(ConsumerOf Bool Str InstanceOf);
use Encode qw(encode);
use Scalar::Util qw(blessed);
use URI::Escape;
use Attean::SPARQLClient;
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
has 'endpoint' => (is => 'ro', isa => ConsumerOf['Attean::API::TermOrVariable'], required => 1);
has 'silent' => (is => 'ro', isa => Bool, default => 0);
has 'sparql' => (is => 'ro', isa => Str, required => 1);
has 'user_agent' => (is => 'rw', isa => InstanceOf['LWP::UserAgent']);
has 'request_signer' => (is => 'rw');
has 'client' => (is => 'rw', required => 0);
sub plan_as_string {
my $self = shift;
my $sparql = $self->sparql;
$sparql =~ s/\s+/ /g;
return sprintf('Service <%s> %s', $self->endpoint->as_string, $sparql);
}
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;
lib/Attean/Plan.pm view on Meta::CPAN
}
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;
}
return $r;
}
}
);
};
}
}
=item * L<Attean::Plan::Exists>
Returns an iterator containing a single boolean term indicating whether any
results were produced by evaluating the sub-plan.
=cut
package Attean::Plan::Exists 0.038 {
use Moo;
use Types::Standard qw(ArrayRef ConsumerOf);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
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 { return 'Exists' }
sub impl {
my $self = shift;
my $model = shift;
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);
}
}
lib/Attean/Plan.pm view on Meta::CPAN
my %b = %{ $row->bindings };
$b{ $rank } = Attean::Literal->integer($ord++);
my $r = Attean::Result->new( bindings => \%b );
push(@results, $r);
}
} else {
foreach my $var (keys %aggs) {
my $expr = $aggs{$var};
my $value = eval { $self->evaluate_aggregate($model, $expr, $rows) };
if ($value) {
$row{$var} = $value;
}
}
my $result = Attean::Result->new( bindings => \%row );
push(@results, $result);
}
}
return Attean::ListIterator->new(
values => \@results,
variables => $iter_variables,
item_type => 'Attean::API::Result'
);
};
}
}
package Attean::Plan::Sequence 0.038 {
use Moo;
use Scalar::Util qw(blessed);
use Types::Standard qw(ConsumerOf ArrayRef);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::QueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
sub plan_as_string { return 'Sequence'; }
sub impl {
my $self = shift;
my $model = shift;
my @children = map { $_->impl($model) } @{ $self->children };
return sub {
foreach my $child (@children) {
my $iter = $child->();
$iter->elements;
}
return Attean::ListIterator->new(values => [Attean::Literal->true], item_type => 'Attean::API::Term');
};
}
}
package Attean::Plan::Clear 0.038 {
use Moo;
use Scalar::Util qw(blessed);
use Types::Standard qw(ConsumerOf ArrayRef);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::NullaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
has 'graphs' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Term']]);
sub plan_as_string {
my $self = shift;
my $level = shift;
my $indent = ' ' x (1+$level);
my $s = sprintf("Clear { %d graphs }", scalar(@{ $self->graphs }));
foreach my $g (@{ $self->graphs }) {
my $name = $g->as_sparql;
chomp($name);
$s .= "\n-${indent} $name";
}
return $s;
}
sub impl {
my $self = shift;
my $model = shift;
my $graphs = $self->graphs;
return sub {
foreach my $g (@$graphs) {
$model->clear_graph($g);
}
return Attean::ListIterator->new(values => [Attean::Literal->true], item_type => 'Attean::API::Term');
};
}
}
package Attean::Plan::Drop 0.038 {
use Moo;
use Scalar::Util qw(blessed);
use Types::Standard qw(ConsumerOf ArrayRef);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::NullaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
has 'graphs' => (is => 'ro', isa => ArrayRef[ConsumerOf['Attean::API::Term']]);
sub plan_as_string {
my $self = shift;
my $level = shift;
my $indent = ' ' x (1+$level);
my $s = sprintf("Drop { %d graphs }", scalar(@{ $self->graphs }));
foreach my $g (@{ $self->graphs }) {
$s .= "\n-${indent} " . $g->as_sparql;
}
return $s;
}
sub impl {
my $self = shift;
my $model = shift;
my $graphs = $self->graphs;
return sub {
foreach my $g (@$graphs) {
$model->drop_graph($g);
}
return Attean::ListIterator->new(values => [Attean::Literal->true], item_type => 'Attean::API::Term');
};
}
}
package Attean::Plan::TripleTemplateToModelQuadMethod 0.038 {
use Moo;
use Scalar::Util qw(blessed);
use Types::Standard qw(ConsumerOf Str ArrayRef HashRef);
use namespace::clean;
with 'Attean::API::Plan', 'Attean::API::UnaryQueryTree';
with 'Attean::API::UnionScopeVariablesPlan';
has 'order' => (is => 'ro', isa => ArrayRef[Str], required => 1);
has 'patterns' => (is => 'ro', isa => HashRef[ArrayRef[ConsumerOf['Attean::API::TripleOrQuadPattern']]], required => 1);
has 'graph' => (is => 'ro', isa => ConsumerOf['Attean::API::Term']);
sub plan_as_string {
my $self = shift;
my $level = shift;
my $indent = ' ' x (1+$level);
my $s = sprintf("Template-to-Model { Default graph: %s }", $self->graph->as_string);
foreach my $method (@{ $self->order }) {
my $pattern = $self->patterns->{ $method };
$s .= "\n-${indent} Method: ${method}";
foreach my $p (@$pattern) {
$s .= "\n-${indent} " . $p->as_string;
}
}
return $s;
}
sub impl {
my $self = shift;
my $model = shift;
my $child = $self->children->[0]->impl($model);
my $graph = $self->graph;
my @order = @{ $self->order };
my $method = shift(@order);
my $pattern = $self->patterns->{ $method };
return sub {
my $iter = $child->();
my @results;
while (my $t = $iter->next) {
if (scalar(@order)) {
push(@results, $t);
}
foreach my $p (@$pattern) {
my $q = $p->apply_bindings($t);
my $quad = $q->does('Attean::API::QuadPattern') ? $q : $q->as_quad_pattern($graph);
if ($quad->is_ground) {
# warn "# $method: " . $quad->as_string . "\n";
$model->$method($quad->as_quad);
} else {
# warn "not ground: " . $quad->as_string;
}
}
}
foreach my $method (@order) {
my $pattern = $self->patterns->{ $method };
foreach my $t (@results) {
foreach my $p (@$pattern) {
my $q = $p->apply_bindings($t);
my $quad = $q->does('Attean::API::QuadPattern') ? $q : $q->as_quad_pattern($graph);
if ($quad->is_ground) {
# warn "# $method: " . $quad->as_string . "\n";
$model->$method($quad->as_quad);
} else {
# warn "not ground: " . $quad->as_string;
}
}
}
}
lib/Attean/Plan.pm view on Meta::CPAN
has 'url' => (is => 'ro', isa => Str);
sub plan_as_string {
my $self = shift;
return sprintf("Load { %s }", $self->url);
}
sub impl {
my $self = shift;
my $url = $self->url;
my $ua = LWP::UserAgent->new();
my $silent = $self->silent;
my $accept = Attean->acceptable_parsers( handles => 'Attean::API::Triple' );
$ua->default_headers->push_header( 'Accept' => $accept );
return sub {
my $resp = $ua->get( $url );
if ($resp->is_success) {
my $ct = $resp->header('Content-Type');
if (my $pclass = Attean->get_parser( media_type => $ct )) {
my $p = $pclass->new();
my $str = $resp->decoded_content;
my $bytes = encode('UTF-8', $str, Encode::FB_CROAK);
my $iter = $p->parse_iter_from_bytes( $bytes );
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,
( run in 0.432 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )