File Coverage

blib/lib/AtteanX/Store/SPARQL.pm
Criterion Covered Total %
statement 54 74 72.9
branch 2 6 33.3
condition 2 6 33.3
subroutine 15 17 88.2
pod 2 3 66.6
total 75 106 70.7


line stmt bran cond sub pod time code
1             package AtteanX::Store::SPARQL;
2              
3 1     1   6343 use v5.14;
  1         2  
4 1     1   4 use strict;
  1         0  
  1         17  
5 1     1   3 use warnings;
  1         1  
  1         36  
6              
7              
8             our $AUTHORITY = 'cpan:KJETILK';
9             our $VERSION = '0.008';
10              
11 1     1   3 use Moo;
  1         1  
  1         5  
12 1     1   204 use Type::Tiny::Role;
  1         1  
  1         22  
13 1     1   3 use Types::URI -all;
  1         1  
  1         11  
14 1     1   1505 use Types::Standard qw(InstanceOf);
  1         1  
  1         6  
15 1     1   413 use Attean;
  1         1  
  1         5  
16 1     1   17 use Attean::RDF;
  1         1  
  1         76  
17 1     1   327 use AtteanX::Plan::SPARQLBGP;
  1         2  
  1         21  
18 1     1   4 use LWP::UserAgent;
  1         1  
  1         18  
19              
20 1     1   3 use Data::Dumper;
  1         1  
  1         34  
21 1     1   4 use Carp;
  1         1  
  1         473  
22              
23             with 'Attean::API::TripleStore';
24             with 'Attean::API::CostPlanner';
25             with 'MooX::Log::Any';
26              
27             has 'endpoint_url' => (is => 'ro', isa => Uri, coerce => 1);
28             has 'ua' => (is => 'rw', isa => InstanceOf['LWP::UserAgent'], builder => '_build_ua');
29              
30             sub _build_ua {
31 1     1   8662 my $self = shift;
32 1         6 my $ua = LWP::UserAgent->new;
33 1         1896 $ua->default_headers->push_header( 'Accept' => 'application/sparql-results+json' ); #Attean->acceptable_parsers(handles => q[Attean::API::Result]));
34 1         43 return $ua;
35             }
36              
37             sub get_triples {
38             my $self = shift;
39             my $pattern = Attean::TriplePattern->new(@_);
40             my $ua = $self->ua->clone;
41             $ua->default_headers->header( 'Accept' => Attean->acceptable_parsers);
42             return $self->get_sparql("CONSTRUCT WHERE {\n\t".$pattern->tuples_string."\n}");
43             }
44              
45             sub count_triples {
46 0     0 1 0 my $self = shift;
47 0         0 my $pattern = Attean::TriplePattern->new(@_);
48 0         0 my $iter = $self->get_sparql("SELECT (count(*) AS ?count) WHERE {\n\t".$pattern->tuples_string."\n}");
49 0         0 return $iter->next->value('count')->value;
50             }
51              
52             sub get_sparql {
53 0     0 1 0 my $self = shift;
54 0         0 my $sparql = shift;
55 0   0     0 my $ua = shift || $self->ua;
56 0         0 my $url = $self->endpoint_url->clone;
57 0         0 my %query = $url->query_form;
58 0         0 $query{'query'} = $sparql;
59 0         0 $url->query_form(%query);
60 0         0 $self->log->debug('Sending GET request for URL: ' . $url);
61 0         0 my $response = $ua->get( $url );
62 0 0       0 if ($response->is_success) {
63 0         0 my $parsertype = Attean->get_parser( media_type => $response->content_type);
64 0 0       0 croak 'Could not parse response from '. $self->endpoint_url->as_string . ' which returned ' . $response->content_type unless defined($parsertype);
65 0         0 my $p = $parsertype->new;
66 0         0 return $p->parse_iter_from_bytes($response->content);
67             } else {
68 0         0 $self->log->trace('Got an error, dumping the response: ' . Dumper($response));
69 0         0 croak 'Error making remote SPARQL call to endpoint ' . $self->endpoint_url->as_string . ' (' .$response->status_line. ')';
70             }
71             }
72              
73             sub plans_for_algebra {
74 2     2 0 17450 my $self = shift;
75 2         3 my $algebra = shift;
76 2         2 my $model = shift;
77 2         2 my $active_graphs = shift;
78 2         3 my $default_graphs = shift;
79              
80 2 100 66     14 if ($algebra->isa('Attean::Algebra::BGP') && scalar $algebra->elements > 1) {
81 1         7 my @quads;
82 1         2 foreach my $triple (@{$algebra->triples}) {
  1         2  
83 3         487 push(@quads, Attean::Plan::Quad->new(subject => $triple->subject,
84             predicate => $triple->predicate,
85             object => $triple->object,
86             graph => $active_graphs,
87             distinct => 0));
88             }
89 1         211 return AtteanX::Plan::SPARQLBGP->new(children => \@quads,
90             distinct => 0); # TODO: Fix
91             }
92 1         11 return;
93             }
94              
95             sub cost_for_plan {
96             my $self = shift;
97             my $plan = shift;
98             return; #TODO for now
99             }
100              
101             1;
102              
103             __END__
104              
105             =pod
106              
107             =encoding utf-8
108              
109             =head1 NAME
110              
111             AtteanX::Store::SPARQL - Attean SPARQL store
112              
113             =head1 SYNOPSIS
114              
115             my $store = Attean->get_store('SPARQL')->new(endpoint_url => $url);
116              
117             =head1 DESCRIPTION
118              
119             This implements a simple immutable triple store, which simply allows
120             programmers to use L<Attean> facilities to query remote SPARQL
121             endpoints.
122              
123             This distribution also brings a corresponding
124             L<AtteanX::Model::SPARQL>, which allows query plans to be made, and a
125             L<AtteanX::Plan::SPARQLBGP> plan class, which contains a
126             rudimentary cost estimate that attempts to avoid sending Cartesian
127             joins to remote endpoints if possible.
128              
129             =head2 Attributes and methods
130              
131             =over
132              
133             =item C<< endpoint_url >>
134              
135             The URL of a remote SPARQL endpoint. Will be coerced into a L<URI>
136             object, so it may be set as a string or whatever. Required attribute.
137              
138             =item C<< ua >>
139              
140             An L<LWP::UserAgent> object to use for remote queries. Will be set to
141             a reasonable default if not supplied.
142              
143             =item C<< get_triples >>
144              
145             Method to query the remote endpoint, as required by L<Attean::API::TripleStore>.
146              
147             =item C<< count_triples >>
148              
149             Reimplemented using an aggregate query for greater efficiency.
150              
151             =item C<< get_sparql($sparql, [ $ua ]) >>
152              
153             Will submit the given C<$sparql> query to the above C<endpoint_url>
154             attribute. Optionally, you may pass an L<LWP::UserAgent>, if not it
155             will use the user agent set using the C<ua> method. Will return an
156             iterator with the results if the request is successful.
157              
158              
159              
160             =back
161              
162              
163              
164             =head1 BUGS
165              
166             Please report any bugs to
167             L<https://github.com/kjetilk/p5-atteanx-store-sparql/issues>.
168              
169             =head1 ACKNOWLEDGEMENTS
170              
171             This module is heavily influenced by L<RDF::Trine::Store::SPARQL>.
172              
173             =head1 AUTHOR
174              
175             Kjetil Kjernsmo E<lt>kjetilk@cpan.orgE<gt>.
176              
177             =head1 COPYRIGHT AND LICENCE
178              
179             This software is copyright (c) 2015, 2016 by Kjetil Kjernsmo and Gregory
180             Todd Williams.
181              
182             This is free software; you can redistribute it and/or modify it under
183             the same terms as the Perl 5 programming language system itself.
184              
185              
186             =head1 DISCLAIMER OF WARRANTIES
187              
188             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
189             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
190             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
191