File Coverage

blib/lib/RDF/LDF.pm
Criterion Covered Total %
statement 277 286 96.8
branch 85 122 69.6
condition 28 52 53.8
subroutine 44 44 100.0
pod 2 6 33.3
total 436 510 85.4


line stmt bran cond sub pod time code
1             package RDF::LDF;
2              
3 5     5   2998247 use strict;
  5         30  
  5         119  
4 5     5   21 use warnings;
  5         7  
  5         113  
5 5     5   29 use feature qw(state);
  5         6  
  5         475  
6 5     5   987 use utf8;
  5         29  
  5         26  
7              
8 5     5   938 use Moo;
  5         18679  
  5         31  
9 5     5   11682 use Data::Compare;
  5         61435  
  5         44  
10 5     5   17873 use RDF::NS;
  5         106315  
  5         146  
11 5     5   788 use RDF::Trine;
  5         2414899  
  5         200  
12 5     5   2602 use RDF::Query;
  5         3632387  
  5         212  
13 5     5   48 use URI::Escape;
  5         10  
  5         272  
14 5     5   35 use LWP::UserAgent;
  5         10  
  5         99  
15 5     5   26 use HTTP::Request::Common;
  5         7  
  5         257  
16 5     5   2275 use Log::Any ();
  5         37542  
  5         133  
17 5     5   2134 use Cache::LRU;
  5         2828  
  5         155  
18 5     5   29 use Clone qw(clone);
  5         10  
  5         214  
19 5     5   24 use JSON;
  5         8  
  5         47  
20 5     5   2611 use URI::Template;
  5         22740  
  5         209  
21 5     5   1951 use RDF::LDF::Error;
  5         14  
  5         15707  
22              
23             our $VERSION = '0.25';
24              
25             has url => (
26             is => 'ro' ,
27             required => 1
28             );
29              
30             has sn => (
31             is => 'ro' ,
32             lazy => 1,
33             builder => sub {
34 3     3   60 RDF::NS->new->REVERSE;
35             }
36             );
37              
38             has lru => (
39             is => 'ro' ,
40             lazy => 1,
41             builder => sub {
42 3     3   50 Cache::LRU->new( size => 100 );
43             }
44             );
45              
46             has log => (
47             is => 'ro',
48             lazy => 1,
49             builder => sub {
50 3     3   50 Log::Any->get_logger(category => ref(shift));
51             }
52             );
53              
54             # Public method
55             sub is_fragment_server {
56 3     3 0 18917 my $self = shift;
57 3 100       33 my $federated = ref($self->url) ? $self->url : [ $self->url ];
58 3         9 for my $part (@$federated) {
59 5 50       18 return 0 unless $self->get_query_pattern($part);
60             }
61 3         26 return 1;
62             }
63              
64             # Public method
65             # Optimized method to find all bindings matching a pattern
66             # See:
67             # Verborgh, Ruben, et al. Querying Datasets on the Web with High Availability. ISWC2014
68             # http://linkeddatafragments.org/publications/iswc2014.pdf
69             sub get_pattern {
70 7     7 1 8953 my ($self,$bgp,$context,%args) = @_;
71              
72 7 100       29 unless (defined $bgp) {
73 1         9 RDF::LDF::Error->throw(text => "can't execute get_pattern for an empty pattern");
74             }
75              
76 6 100 66     92 my (@triples) = ($bgp->isa('RDF::Trine::Statement') or $bgp->isa('RDF::Query::Algebra::Filter'))
77             ? $bgp
78             : $bgp->triples;
79              
80 6 50       35 unless (@triples) {
81 0         0 RDF::LDF::Error->throw(text => "can't execute get_pattern for an empty pattern");
82             }
83              
84 6         27 my @vars = $bgp->referenced_variables;
85              
86 6         535 my @bgps = map { $self->_parse_triple_pattern($_)} @triples;
  8         28  
87              
88             my $sub = sub {
89 12     12   6728 state $it = $self->_find_variable_bindings(\@bgps);
90 12         24 my $b = $it->();
91              
92 12 100       36 return undef unless $b;
93              
94 10         89 my $binding = RDF::Trine::VariableBindings->new({});
95              
96 10         125 for my $key (keys %$b) {
97 26         175 my $val = $b->{$key};
98 26         81 $key =~ s{^\?}{};
99 26         66 $binding->set($key => $val);
100             }
101              
102 10         73 $binding;
103 6         38 };
104              
105 6         61 RDF::Trine::Iterator::Bindings->new($sub,\@vars);
106             }
107              
108             sub _find_variable_bindings {
109 14     14   31 my $self = shift;
110 14         46 my $bgps = shift;
111 14   100     54 my $bindings = shift // {};
112              
113             my $iterator = sub {
114 26     26   34 state $it;
115 26         52 state $results = sub {};
116              
117 26         34 my $ret;
118              
119             # Loop over all variabe bindings with multiple matches
120 26         50 while (!defined($ret = $results->())) {
121 28 100       64 unless (defined $it) {
122             # Find the an binding iterator for the best pattern from $bgpgs
123 14         35 ($it,$bgps) = $self->_find_variable_bindings_($bgps);
124              
125 14 50       39 return undef unless $it;
126             }
127              
128             # Update all the other bgps with the current binding..
129 28         56 my $this_binding = $it->();
130              
131 28 100       88 return undef unless $this_binding;
132              
133 18         99 $bindings = { %$bindings , %$this_binding };
134              
135 18 100       94 return $bindings unless @$bgps;
136              
137             # Apply all the bindings to the rest of the bgps;
138 8         29 my $bgps_prime = $self->_apply_binding($this_binding,$bgps);
139              
140 8         29 $results = $self->_find_variable_bindings($bgps_prime,$bindings);
141             }
142              
143 6         17 $ret;
144 14         76 };
145              
146 14         42 $iterator;
147             }
148              
149             # Given an array ref of patterns return the variable bindings for the
150             # pattern with the least number of triples.
151             #
152             # my ($iterator, $rest) = $self->_find_variable_bindings([ {pattern} , {pattern} , ... ]);
153             #
154             # where:
155             #
156             # $iterator - Iterator for variable bindings for the winnnig pattern, or undef when no
157             # patterns are provided or we get zero results
158             #
159             # $rest - An array ref of patterns not containing the best pattern
160             sub _find_variable_bindings_ {
161 14     14   34 my ($self,$bgps) = @_;
162              
163 14 50 33     34 return (undef, undef) unless _is_array_ref($bgps) && @$bgps > 0;
164              
165 14         43 my ($pattern,$rest) = $self->_find_best_pattern($bgps);
166              
167 14 50       38 return (undef,undef) unless defined $pattern;
168              
169 14         42 my $it = $self->get_statements($pattern);
170              
171             # Build a mapping of variable bindings to Triple nodes. E.g.
172             # {
173             # '?s' => 'subject' ,
174             # '?p' => 'predicate' ,
175             # '?o' => 'object' ,
176             #}
177 14 100       42 my %pattern_var_map = map { $pattern->{$_} =~ /^\?/ ? ($pattern->{$_} , $_) : () } keys %$pattern;
  42         164  
178 14         35 my $num_of_bindings = keys %pattern_var_map;
179              
180             my $sub = sub {
181 28     28   55 my $triple = $it->();
182              
183 28 100       92 return undef unless defined $triple;
184              
185 18         72 my %var_map = %pattern_var_map;
186              
187 18         55 for (keys %var_map) {
188 30         102 my $method = $var_map{$_};
189 30         64 $var_map{$_} = $triple->$method;
190             }
191              
192 18         146 return {%var_map};
193 14         53 };
194              
195 14         50 return ($sub,$rest);
196             }
197              
198             sub _apply_binding {
199 8     8   21 my ($self,$binding,$bgps) = @_;
200              
201 8 50 33     23 return unless _is_array_ref($bgps) && @$bgps > 0;
202              
203 8         100 my $copy = clone $bgps;
204 8         20 my @new = ();
205              
206 8         27 for my $pattern (@$copy) {
207 8         18 for (qw(subject predicate object)) {
208 24         37 my $val = $pattern->{$_};
209 24 100 66     97 if (defined($val) && $binding->{$val}) {
210 8         242 my $str_val = $self->_node_as_string($binding->{$val});
211 8         80 $pattern->{$_} = $str_val
212             }
213             }
214 8         46 push @new, $pattern;
215             }
216              
217 8         21 return \@new;
218             }
219              
220             # Create a pattern which binds to the graph pattern
221             #
222             # Usage:
223             #
224             # my $triples = [
225             # { subject => ... , predicate => ... , object => ... } , #tp1
226             # { subject => ... , predicate => ... , object => ... } , #tp2
227             # ...
228             # { subject => ... , predicate => ... , object => ... } , #tpN
229             # ];
230             #
231             # my ($pattern, $rest) = $self->_find_best_pattern($triples);
232             #
233             # $pattern => Pattern in $triples which least amount of results
234             # $rest => All patterns in $triples except $pattern
235             #
236             sub _find_best_pattern {
237 14     14   33 my ($self,$triples) = @_;
238              
239 14 50       34 return undef unless @$triples > 0;
240              
241             # If we only have one tripple pattern, the use it to create the bind
242 14 100       41 if (@$triples == 1) {
243 12         35 return $triples->[0] , [];
244             }
245              
246 2         4 my $best_pattern = undef;
247 2         4 my $best_count = undef;
248              
249 2         6 for my $pattern (@$triples) {
250 4   50     12 my $count = $self->_total_triples($pattern) // 0;
251              
252 4 50 66     28 if ($count == 0) {
    100          
253 0         0 $best_pattern = undef;
254 0         0 $best_count = 0;
255 0         0 last;
256             }
257             elsif (!defined $best_count || $count < $best_count) {
258 2         4 $best_count = $count;
259 2         5 $best_pattern = $pattern;
260             }
261             }
262              
263 2 50       8 return (undef,$triples) unless defined $best_pattern;
264              
265 2 100       5 my @rest_triples = map { Data::Compare::Compare($_,$best_pattern) ? () : ($_) } @$triples;
  4         139  
266              
267 2         379 return ($best_pattern, \@rest_triples);
268             }
269              
270             # Find the total number of triples available for a pattern
271             #
272             # Usage:
273             #
274             # my $count = $self->_total_triples(
275             # { subject => ... , predicate => ... , object => ...}
276             # );
277             # Where
278             # $count is a number
279             sub _total_triples {
280 4     4   7 my ($self,$pattern) = @_;
281              
282             # Retrieve one...
283 4         19 my $iterator = $self->get_statements($pattern);
284              
285 4 50       12 return 0 unless $iterator;
286              
287 4         9 my ($model,$info) = $iterator->();
288              
289 4         57 $info->{hydra_totalItems};
290             }
291              
292             sub _node_as_string {
293 32     32   139 my $self = shift;
294 32         39 my $node = shift;
295 32 50 33     59 if (_is_blessed($node) && $node->isa('RDF::Trine::Node')) {
296 32 100       140 if ($node->isa('RDF::Trine::Node::Variable')) {
    50          
297 16         40 return $node->as_string; # ?foo
298             } elsif ($node->isa('RDF::Trine::Node::Literal')) {
299 0         0 return $node->as_string; # includes quotes and any language or datatype
300             } else {
301 16         52 return $node->value; # the raw IRI or blank node identifier value, without other syntax
302             }
303             }
304 0         0 return '';
305             }
306              
307             # For an BGP triple create a fragment pattern
308             sub _parse_triple_pattern {
309 8     8   21 my ($self,$triple) = @_;
310 8         22 my $subject = $self->_node_as_string($triple->subject);
311 8         88 my $predicate = $self->_node_as_string($triple->predicate);
312 8         67 my $object = $self->_node_as_string($triple->object);
313 8         75 my $hash = {
314             subject => $subject ,
315             predicate => $predicate,
316             object => $object
317             };
318 8         24 return $hash;
319             }
320              
321             # Dynamic find out which triple patterns need to be used to query the fragment server
322             # Returns a hash:
323             # {
324             # rdf_subject => <name_of_subject_variable> ,
325             # rdf_predicate => <name_of_predicate_variable> ,
326             # rdf_object => <name_of_object_variable>
327             # hydra_template => <endpoint_for_tripple_pattern>
328             # }
329             sub get_query_pattern {
330 42     42 0 89 my ($self,$url) = @_;
331              
332 42         88 my $fragment = $self->get_model_and_info($url);
333              
334 42 50       581 return undef unless defined $fragment;
335              
336 42         77 my $info = $fragment->{info};
337              
338 42         52 my $pattern;
339              
340 42 50       76 return undef unless _is_hash_ref($info);
341              
342 42 50       120 return undef unless $info->{hydra_template};
343              
344 42         290 for (keys %$info) {
345 716 100 100     1143 next unless _is_hash_ref($info->{$_}) && $info->{$_}->{hydra_property};
346 126         2092 my $property = join "_" , $self->sn->qname($info->{$_}->{hydra_property});
347 126         28945 my $variable = $info->{$_}->{hydra_variable};
348              
349 126         351 $pattern->{$property} = $variable;
350             }
351              
352 42 50       145 return undef unless $pattern->{rdf_subject};
353 42 50       80 return undef unless $pattern->{rdf_predicate};
354 42 50       110 return undef unless $pattern->{rdf_object};
355              
356 42         87 $pattern->{hydra_template} = $info->{hydra_template};
357              
358 42         94 $pattern;
359             }
360              
361             #----------------------------------------------------------------------------------
362              
363             # Public method
364             sub get_statements {
365 19     19 1 43 my ($self,@triple) = @_;
366 19         33 my ($subject,$predicate,$object);
367              
368 19 50       76 if (@triple == 3) {
    100          
369 0         0 ($subject,$predicate,$object) = @triple;
370             }
371             elsif (_is_hash_ref($triple[0])) {
372 18         37 $subject = $triple[0]->{subject};
373 18         34 $predicate = $triple[0]->{predicate};
374 18         28 $object = $triple[0]->{object};
375             }
376              
377 19 0 33     45 $subject = $subject->value if (_is_blessed($subject) && $subject->isa('RDF::Trine::Node') and not $subject->is_variable);
      33        
378 19 0 33     40 $predicate = $predicate->value if (_is_blessed($predicate) && $predicate->isa('RDF::Trine::Node') and not $predicate->is_variable);
      33        
379 19 0 33     38 if (_is_blessed($object) && $object->isa('RDF::Trine::Node') and not $object->is_variable) {
      33        
380 0 0       0 $object = ($object->isa('RDF::Trine::Node::Literal')) ? $object->as_string : $object->value;
381             }
382              
383             # Do a federated search over all the URLs provided
384 19 100       104 my $parts = ref($self->url) ? $self->url : [ $self->url ];
385 19         29 my @federated;
386              
387 19         43 for my $part (@$parts) {
388 37         6269 my $pattern = $self->get_query_pattern($part);
389 37 50       81 return undef unless defined $pattern;
390              
391 37         52 my %params;
392 37 100       71 $params{ $pattern->{rdf_subject} } = $subject if _is_string($subject);
393 37 100       78 $params{ $pattern->{rdf_predicate} } = $predicate if _is_string($predicate);
394 37 100       80 $params{ $pattern->{rdf_object} } = $object if _is_string($object);
395              
396 37         189 my $template = URI::Template->new($pattern->{hydra_template});
397 37         4048 push @federated , $template->process(%params)->as_string;
398             }
399              
400             my $sub = sub {
401 34     34   1005 state $model;
402 34         39 state $info;
403 34         46 state $iterator;
404 34         58 state $url = shift(@federated);
405              
406 34         45 my $triple;
407              
408 34   100     41 do {
409 44 100       102 unless (defined $model) {
410             # When no more result pages are available switch
411             # to the next federated url...
412 37 100 100     133 return unless defined($url) || defined($url = pop(@federated));
413              
414 31         87 my $fragment = $self->get_model_and_info($url);
415              
416 31 50       124 return unless defined $fragment->{model};
417              
418 31         61 $model = $fragment->{model};
419 31         56 $info = $fragment->{info};
420              
421 31         66 $url = $info->{hydra_next};
422 31         121 $iterator = $model->get_statements;
423             }
424              
425 38         15574 $triple = $iterator->next;
426              
427 38 100       6125 unless ($iterator->peek) {
428 23         571 $model = undef;
429             }
430             }
431             while (!defined $triple && defined($url = pop(@federated)));
432              
433 28 100       1576 wantarray ? ($triple,$info) : $triple;
434 19         6588 };
435              
436 19         58 $sub;
437             }
438              
439             # Fetch a fragment page and extract the metadata
440             sub get_model_and_info {
441 73     73 0 123 my ($self,$url) = @_;
442              
443 73 100       1376 if (my $cache = $self->lru->get($url)) {
444 49         1081 return $cache;
445             }
446              
447 24         407 my $model = $self->get_fragment($url);
448 24         65 my $info = {};
449              
450 24 50       91 if (defined $model) {
451 24         131 $info = $self->_model_metadata($model,$url, clean => 1);
452             }
453              
454 24         293 my $fragment = { model => $model , info => $info };
455              
456 24         680 $self->lru->set($url => $fragment);
457              
458 24         1023 $fragment;
459             }
460              
461             # Fetch a result page from fragment server
462             sub get_fragment {
463 24     24 0 68 my ($self,$url) = @_;
464              
465 24 50       60 return undef unless $url;
466              
467 24         385 $self->log->info("fetching: $url");
468              
469 24         6032 my $model = RDF::Trine::Model->temporary_model;
470              
471             # JSON support in RDF::Trine isn't JSON-LD
472             # Set the accept header quality parameter at a minimum for this format
473 24         1615 my $ua = clone(RDF::Trine->default_useragent);
474 24         6362 $ua->agent("RDF:::LDF/$RDF::LDF::VERSION " . $ua->_agent);
475 24         5574 $ua->default_header('Accept','text/turtle;q=1.0,application/turtle;q=1.0,application/x-turtle;q=1.0,application/rdf+xml;q=0.9,text/x-nquads;q=0.9,application/json;q=0.1,application/x-rdf+json;q=0.1');
476              
477 24         1071 eval {
478             # Need to explicitly set the useragent to keep the accept headers
479 24         233 RDF::Trine::Parser->parse_url_into_model($url, $model, useragent => $ua);
480             };
481              
482 24 50       3417012 if ($@) {
483 0         0 $self->log->error("failed to parse input");
484             }
485              
486 24         87 return $model;
487             }
488              
489             # Create a hash with fragment metadata from a RDF::Trine::Model
490             # parameters:
491             # $model - RDF::Trine::Model
492             # $this_uri - result page URL
493             # %opts
494             # clean => 1 - remove the metadata from the model
495             sub _model_metadata {
496 24     24   125 my ($self,$model,$this_uri,%opts) = @_;
497              
498 24         57 my $info = {};
499              
500 24         129 $self->_build_metadata($model, {
501             subject => RDF::Trine::Node::Resource->new($this_uri)
502             } , $info);
503              
504 24 50       117 if ($opts{clean}) {
505 24         360 $model->remove_statements(
506             RDF::Trine::Node::Resource->new($this_uri),
507             undef,
508             undef
509             );
510 24         760874 $model->remove_statements(
511             undef,
512             undef,
513             RDF::Trine::Node::Resource->new($this_uri)
514             );
515             }
516              
517 24         19305 for my $predicate (
518             'http://www.w3.org/ns/hydra/core#variable' ,
519             'http://www.w3.org/ns/hydra/core#property' ,
520             'http://www.w3.org/ns/hydra/core#mapping' ,
521             'http://www.w3.org/ns/hydra/core#template' ,
522             'http://www.w3.org/ns/hydra/core#member' ,
523             'http://www.w3.org/ns/hydra/core#variableRepresentation' ,
524             ) {
525 144         795902 $self->_build_metadata($model, {
526             predicate => RDF::Trine::Node::Resource->new($predicate)
527             }, $info);
528              
529 144 50       503 if ($opts{clean}) {
530 144         1437 $model->remove_statements(
531             undef,
532             RDF::Trine::Node::Resource->new($predicate) ,
533             undef);
534             }
535             }
536              
537 24 50       3424 my $source = $info->{dct_source}->[0] if _is_array_ref($info->{dct_source});
538              
539 24 50       88 if ($source) {
540 24         82 $self->_build_metadata($model, {
541             subject => RDF::Trine::Node::Resource->new($source)
542             }, $info);
543              
544 24 50       92 if ($opts{clean}) {
545 24         263 $model->remove_statements(
546             RDF::Trine::Node::Resource->new($source),
547             undef,
548             undef
549             );
550 24         326798 $model->remove_statements(
551             undef,
552             undef,
553             RDF::Trine::Node::Resource->new($source)
554             );
555             }
556             }
557              
558 24         4199 $info;
559             }
560              
561             # Helper method for _parse_model
562             sub _build_metadata {
563 192     192   3745 my ($self, $model, $triple, $info) = @_;
564              
565             my $iterator = $model->get_statements(
566             $triple->{subject},
567             $triple->{predicate},
568             $triple->{object}
569 192         1136 );
570              
571 192         197004 while (my $triple = $iterator->next) {
572 621         63408 my $subject = $triple->subject->as_string;
573 621         7158 my $predicate = $triple->predicate->uri_value;
574 621         4730 my $object = $triple->object->value;
575              
576 621         18471 my $qname = join "_" , $self->sn->qname($predicate);
577              
578 621 100       241043 if ($qname =~ /^(hydra_variable|hydra_property)$/) {
    100          
    100          
579 144         412 my $id= $triple->subject->value;
580              
581 144         2209 $info->{"_$id"}->{$qname} = $object;
582             }
583             elsif ($qname eq 'hydra_mapping') {
584 72         239 my $id= $triple->subject->value;
585              
586 72         755 push @{$info->{"_$id"}->{$qname}} , $object;
  72         400  
587             }
588             elsif ($qname =~ /^(void|hydra)_/) {
589 237         1134 $info->{$qname} = $object;
590             }
591             else {
592 168         250 push @{$info->{$qname}} , $object;
  168         813  
593             }
594             }
595              
596 192         5755 $info;
597             }
598              
599             sub _is_array_ref {
600 46     46   283 return ref($_[0]) eq 'ARRAY';
601             }
602              
603             sub _is_hash_ref {
604 777     777   2185 return ref($_[0]) eq 'HASH';
605             }
606              
607             sub _is_blessed {
608 89     89   381 return ref($_[0]) =~ /\S/;
609             }
610              
611             sub _is_string {
612 111   33 111   808 return defined($_[0]) && !ref($_[0]) && ref(\$_[0]) ne 'GLOB' && length($_[0]) > 0;
613             }
614              
615             1;
616              
617             __END__
618              
619             =head1 NAME
620              
621             RDF::LDF - Linked Data Fragments client
622              
623             =begin markdown
624              
625             # STATUS
626             [![Build Status](https://travis-ci.org/phochste/RDF-LDF.svg)](https://travis-ci.org/phochste/RDF-LDF)
627             [![Coverage Status](https://coveralls.io/repos/phochste/RDF-LDF/badge.svg)](https://coveralls.io/r/phochste/RDF-LDF)
628             [![Kwalitee Score](http://cpants.cpanauthors.org/dist/RDF-LDF.png)](http://cpants.cpanauthors.org/dist/RDF-LDF)
629              
630             =end markdown
631              
632             =head1 SYNOPSIS
633              
634             use RDF::Trine::Store::LDF;
635             use RDF::Trine::Store;
636              
637             # To use a HTTP cache:
638             use LWP::UserAgent::CHICaching;
639             my $cache = CHI->new( driver => 'Memory', global => 1 );
640             my $ua = LWP::UserAgent::CHICaching->new(cache => $cache);
641             RDF::Trine->default_useragent($ua);
642              
643             my $store = RDF::Trine::Store->new_with_config({
644             storetype => 'LDF',
645             url => $url
646             });
647              
648             my $it = $store->get_statements();
649              
650             while (my $st = $it->next) {
651             # $st is a RDF::Trine::Statement
652             print "$st\n";
653             }
654              
655             # Or the low level modules themselves
656              
657             use RDF::LDF;
658              
659             my $client = RDF::LDF->new(url => 'http://fragments.dbpedia.org/2014/en');
660              
661             my $iterator = $client->get_statements($subject, $predicate, $object);
662              
663             while (my $statement = $iterator->()) {
664             # $model is a RDF::Trine::Statement
665             }
666              
667              
668             =head1 DESCRIPTION
669              
670             RDF::LDF implements a basic L<Linked Data Fragment|http://linkeddatafragments.org/> client.
671              
672             This a low level module to implement the Linked Data Fragment protocol. You probably want to
673             use L<RDF::Trine::Store::LDF>.
674              
675             =head1 CONFIGURATION
676              
677             =over
678              
679             =item url
680              
681             URL to retrieve RDF from.
682              
683             Experimental: more than one URL can be provided for federated search over many LDF endpoints.
684              
685             my $store = RDF::Trine::Store->new_with_config({
686             storetype => 'LDF',
687             url => [ $url1, $url2, $url3 ]
688             });
689              
690             # or
691              
692             my $client = RDF::LDF->new(url => [ $url1, $url2, $url3 ]);
693              
694             =back
695              
696             =head1 METHODS
697              
698             =over
699              
700             =item get_statements( $subject, $predicate, $object )
701              
702             Return an iterator for every RDF::Trine::Statement served by the LDF server.
703              
704             =item get_pattern( $bgp );
705              
706             Returns a stream object of all bindings matching the specified graph pattern.
707              
708             =back
709              
710             =head1 CONTRIBUTORS
711              
712             Patrick Hochstenbach, C<< patrick.hochstenbach at ugent.be >>
713              
714             Gregory Todd Williams, C<< greg@evilfunhouse.com >>
715              
716             Jacob Voss, C<< voss@gbv.de >>
717              
718             =head1 COPYRIGHT AND LICENSE
719              
720             This software is copyright (c) 2015 by Patrick Hochstenbach.
721              
722             This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
723              
724             =encoding utf8
725              
726             =cut