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