File Coverage

blib/lib/RDF/Trine/Model.pm
Criterion Covered Total %
statement 399 516 77.3
branch 134 180 74.4
condition 69 101 68.3
subroutine 40 49 81.6
pod 32 32 100.0
total 674 878 76.7


line stmt bran cond sub pod time code
1             # RDF::Trine::Model
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Model - Model class
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Model version 1.017
11              
12             =head1 METHODS
13              
14             =over 4
15              
16             =cut
17              
18             package RDF::Trine::Model;
19              
20 68     68   414 use strict;
  68         153  
  68         1647  
21 68     68   369 use warnings;
  68         159  
  68         1527  
22 68     68   304 no warnings 'redefine';
  68         146  
  68         2640  
23              
24             our ($VERSION);
25             BEGIN {
26 68     68   1313 $VERSION = '1.017';
27             }
28              
29 68     68   355 use Scalar::Util qw(blessed refaddr);
  68         160  
  68         2896  
30 68     68   378 use Log::Log4perl;
  68         157  
  68         473  
31              
32 68     68   3811 use RDF::Trine::Error qw(:try);
  68         147  
  68         405  
33 68     68   7930 use RDF::Trine qw(variable);
  68         223  
  68         2558  
34 68     68   376 use RDF::Trine::Node;
  68         174  
  68         2023  
35 68     68   24827 use RDF::Trine::Pattern;
  68         190  
  68         1914  
36 68     68   468 use RDF::Trine::Store;
  68         154  
  68         1203  
37 68     68   24606 use RDF::Trine::Model::Dataset;
  68         201  
  68         291233  
38              
39             =item C<< new ( $store ) >>
40              
41             Returns a new model over the supplied L<rdf store|RDF::Trine::Store> or a new temporary model.
42             If you provide an unblessed value, it will be used to create a new rdf store.
43              
44             =cut
45              
46             sub new {
47 945     945 1 4492 my $class = shift;
48 945 100       3301 if (@_) {
49 940         2225 my $store = shift;
50 940 100       5046 $store = RDF::Trine::Store->new( $store ) unless (blessed($store));
51 940         3187 my %args = @_;
52 940         6724 my $self = bless({
53             store => $store,
54             temporary => 0,
55             added => 0,
56             threshold => -1,
57             %args
58             }, $class);
59             } else {
60 5         25 return $class->temporary_model;
61             }
62             }
63              
64             =item C<< temporary_model >>
65            
66             Returns a new temporary (non-persistent) model.
67            
68             =cut
69            
70             sub temporary_model {
71 587     587 1 13999 my $class = shift;
72 587         3822 my $store = RDF::Trine::Store::Memory->new();
73             # my $store = RDF::Trine::Store::DBI->temporary_store();
74 587         2553 my $self = $class->new( $store );
75 587         1623 $self->{temporary} = 1;
76 587         1367 $self->{threshold} = 25_000;
77 587         1708 return $self;
78             }
79              
80             =item C<< dataset_model ( default => \@dgraphs, named => \@ngraphs ) >>
81              
82             Returns a new model object with the default graph mapped to the union of the
83             graphs named in C<< @dgraphs >>, and with available named graphs named in
84             C<< @ngraphs >>.
85              
86             =cut
87              
88             sub dataset_model {
89 0     0 1 0 my $self = shift;
90 0         0 my $ds = RDF::Trine::Model::Dataset->new( $self );
91 0         0 $ds->push_dataset( @_ );
92 0         0 return $ds;
93             }
94              
95             =item C<< begin_bulk_ops >>
96              
97             Provides a hint to the backend that many update operations are about to occur.
98             The backend may use this hint to, for example, aggregate many operations into a
99             single operation, or delay index maintenence. After the update operations have
100             been executed, C<< end_bulk_ops >> should be called to ensure the updates are
101             committed to the backend.
102              
103             =cut
104              
105             sub begin_bulk_ops {
106 773     773 1 1837 my $self = shift;
107 773         2504 my $store = $self->_store;
108 773 50 33     8270 if (blessed($store) and $store->can('_begin_bulk_ops')) {
109 773         3467 $store->_begin_bulk_ops();
110             }
111             }
112              
113             =item C<< end_bulk_ops >>
114              
115             Provides a hint to the backend that a set of bulk operations have been completed
116             and may be committed to the backend.
117              
118             =cut
119              
120             sub end_bulk_ops {
121 7228     7228 1 12001 my $self = shift;
122 7228         16975 my $store = $self->_store;
123 7228 100 66     52106 if (blessed($store) and $store->can('_end_bulk_ops')) {
124 7224         22350 $store->_end_bulk_ops();
125             }
126             }
127              
128             =item C<< logger ( [ $logger ] ) >>
129              
130             Returns the logging object responsible for recording data inserts and deletes.
131              
132             If C<< $logger >> is passed as an argument, sets the logger to this object.
133              
134             =cut
135              
136             sub logger {
137 4106     4106 1 7080 my $self = shift;
138 4106 100       11253 if (scalar(@_)) {
139 1         2 $self->{'logger'} = shift;
140             }
141 4106         12428 return $self->{'logger'};
142             }
143              
144             =item C<< add_statement ( $statement [, $context] ) >>
145              
146             Adds the specified C<< $statement >> to the rdf store.
147              
148             =cut
149            
150             sub add_statement {
151 4085     4085 1 53485 my ($self, @args) = @_;
152 4085 100       15964 if ($args[0]->isa('RDF::Trine::Statement')) {
153 4073         13144 foreach my $n ($args[0]->nodes) {
154 12264 50 66     82931 unless (blessed($n) and ($n->isa('RDF::Trine::Node::Resource') or $n->isa('RDF::Trine::Node::Literal') or $n->isa('RDF::Trine::Node::Blank') or $n->isa('RDF::Trine::Node::Nil'))) {
      66        
155 0         0 throw RDF::Trine::Error::MethodInvocationError -text => 'Cannot add a pattern (non-ground statement) to a model';
156             }
157             }
158             } else {
159 12         57 throw RDF::Trine::Error::MethodInvocationError -text => 'Argument is not an RDF::Trine::Statement';
160             }
161 4073 100       12685 if ($self->{temporary}) {
162 3338 100       10944 if ($self->{added}++ >= $self->{threshold}) {
163             # warn "*** should upgrade to a DBI store here";
164 1         9 my $store = RDF::Trine::Store::DBI->temporary_store;
165 1         7 my $iter = $self->get_statements(undef, undef, undef, undef);
166 1 50       12 if ($store->can('_begin_bulk_ops')) {
167 1         5 $store->_begin_bulk_ops();
168             }
169 1         7 while (my $st = $iter->next) {
170 10         32 $store->add_statement( $st );
171             }
172 1 50       7 if ($store->can('_begin_bulk_ops')) {
173 1         12 $store->_end_bulk_ops();
174             }
175 1         4 $self->{store} = $store;
176 1         76 $self->{temporary} = 0;
177             # warn "*** upgraded to a DBI store";
178             }
179             }
180            
181 4073 100       9878 if (my $log = $self->logger) {
182 3         8 my ($st, $context) = @args;
183 3 50       8 if (defined($context)) {
184 0         0 $st = RDF::Trine::Statement::Quad->new(($st->nodes)[0..2], $context);
185             }
186 3         14 $log->add($st);
187             }
188            
189 4073         12250 return $self->_store->add_statement( @args );
190             }
191              
192             =item C<< add_hashref ( $hashref [, $context] ) >>
193              
194             Add triples represented in an RDF/JSON-like manner to the model.
195              
196             See C<< as_hashref >> for full documentation of the hashref format.
197              
198             =cut
199              
200             sub add_hashref {
201 49     49 1 407 my $self = shift;
202 49         97 my $index = shift;
203 49         122 my $context = shift;
204            
205 49         199 $self->begin_bulk_ops();
206 49         214 foreach my $s (keys %$index) {
207 78 100       665 my $ts = ( $s =~ /^_:(.*)$/ ) ?
208             RDF::Trine::Node::Blank->new($1) :
209             RDF::Trine::Node::Resource->new($s);
210            
211 78         163 foreach my $p (keys %{ $index->{$s} }) {
  78         308  
212 107         426 my $tp = RDF::Trine::Node::Resource->new($p);
213            
214 107         198 foreach my $O (@{ $index->{$s}->{$p} }) {
  107         320  
215 128         223 my $to;
216            
217             # $O should be a hashref, but we can do a little error-correcting.
218 128 100       367 unless (ref $O) {
219 34 50       215 if ($O =~ /^_:/) {
    50          
    100          
220 0         0 $O = { 'value'=>$O, 'type'=>'bnode' };
221             } elsif ($O =~ /^[a-z0-9._\+-]{1,12}:\S+$/i) {
222 0         0 $O = { 'value'=>$O, 'type'=>'uri' };
223             } elsif ($O =~ /^(.*)\@([a-z]{2})$/) {
224 7         46 $O = { 'value'=>$1, 'type'=>'literal', 'lang'=>$2 };
225             } else {
226 27         101 $O = { 'value'=>$O, 'type'=>'literal' };
227             }
228             }
229            
230 128 100       453 if (lc $O->{'type'} eq 'literal') {
231             $to = RDF::Trine::Node::Literal->new(
232 74         492 $O->{'value'}, $O->{'lang'}, $O->{'datatype'});
233             } else {
234             $to = ( $O->{'value'} =~ /^_:(.*)$/ ) ?
235             RDF::Trine::Node::Blank->new($1) :
236 54 100       329 RDF::Trine::Node::Resource->new($O->{'value'});
237             }
238            
239 128 50 33     775 if ($ts and $tp and $to) {
      33        
240 128         558 my $st = RDF::Trine::Statement->new($ts, $tp, $to);
241 128         453 $self->add_statement($st, $context);
242             }
243             }
244             }
245             }
246 49         167 $self->end_bulk_ops();
247             }
248              
249             =item C<< add_iterator ( $iter ) >>
250              
251             Add triples from the statement iterator to the model.
252              
253             =cut
254              
255             sub add_iterator {
256 0     0 1 0 my $self = shift;
257 0         0 my $iter = shift;
258 0 0 0     0 unless (blessed($iter) and ($iter->is_graph)) {
259 0         0 throw RDF::Trine::Error::MethodInvocationError -text => 'Cannot add a '. ref($iter) . ' iterator to a model, only graphs.';
260             }
261 0         0 $self->begin_bulk_ops();
262 0         0 while (my $st = $iter->next) {
263 0         0 $self->add_statement( $st );
264             }
265 0         0 $self->end_bulk_ops();
266             }
267              
268             =item C<< add_list ( @elements ) >>
269              
270             Adds an rdf:List to the model with the given elements. Returns the node object
271             that is the head of the list.
272              
273             =cut
274              
275             sub add_list {
276 0     0 1 0 my $self = shift;
277 0         0 my @elements = @_;
278 0         0 my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
279 0 0       0 if (scalar(@elements) == 0) {
280 0         0 return $rdf->nil;
281             } else {
282 0         0 my $head = RDF::Query::Node::Blank->new();
283 0         0 my $node = shift(@elements);
284 0         0 my $rest = $self->add_list( @elements );
285 0         0 $self->add_statement( RDF::Trine::Statement->new($head, $rdf->first, $node) );
286 0         0 $self->add_statement( RDF::Trine::Statement->new($head, $rdf->rest, $rest) );
287 0         0 return $head;
288             }
289             }
290              
291             =item C<< get_list ( $head ) >>
292              
293             Returns a list of nodes that are elements of the rdf:List represented by the
294             supplied head node.
295              
296             =cut
297              
298             sub get_list {
299 1     1 1 5 my $self = shift;
300 1         4 my $head = shift;
301 1         13 my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
302 1         10 my @elements;
303             my %seen;
304 1   66     15 while (blessed($head) and not($head->isa('RDF::Trine::Node::Resource') and $head->uri_value eq $rdf->nil->uri_value)) {
      66        
305 291 50       949 if ($seen{ $head->as_string }++) {
306 0         0 throw RDF::Trine::Error -text => "Loop found during rdf:List traversal";
307             }
308 291         2230 my @n = $self->objects( $head, $rdf->first );
309 291 50       1004 if (scalar(@n) != 1) {
310 0         0 throw RDF::Trine::Error -text => "Invalid structure found during rdf:List traversal";
311             }
312 291         717 push(@elements, @n);
313 291         2198 ($head) = $self->objects( $head, $rdf->rest );
314             }
315 1         8 return @elements;
316             }
317              
318             =item C<< remove_list ( $head [, orphan_check => 1] ) >>
319              
320             Removes the nodes of type rdf:List that make up the list. Optionally checks each node
321             before removal to make sure that it is not used in any other statements. Returns false
322             if the list was removed completely; returns the first remaining node if the removal
323             was abandoned because of an orphan check.
324              
325             =cut
326              
327             sub remove_list {
328 5     5 1 57 my $self = shift;
329 5         10 my $head = shift;
330 5         21 my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
331 5         43 my %args = @_;
332 5         9 my %seen;
333            
334 5   66     53 while (blessed($head) and not($head->isa('RDF::Trine::Node::Resource') and $head->uri_value eq $rdf->nil->uri_value)) {
      66        
335 11 50       35 if ($seen{ $head->as_string }++) {
336 0         0 throw RDF::Trine::Error -text => "Loop found during rdf:List traversal";
337             }
338 11         32 my $stream = $self->get_statements($head, undef, undef);
339 11         21 my %statements;
340 11         28 while (my $st = $stream->next) {
341             my $statement_type = {
342             $rdf->first->uri => 'rdf:first',
343             $rdf->rest->uri => 'rdf:rest',
344             $rdf->type->uri => 'rdf:type',
345 29   100     142 }->{$st->predicate->uri} || 'other';
346 29 50 66     112 $statement_type = 'other'
347             if $statement_type eq 'rdf:type' && !$st->object->equal($rdf->List);
348 29         53 push @{$statements{$statement_type}}, $st;
  29         123  
349             }
350 11 100       28 if ($args{orphan_check}) {
351 3 100 66     13 return $head if defined $statements{other} && scalar(@{ $statements{other} }) > 0;
  1         18  
352 2 100       11 return $head if $self->count_statements(undef, undef, $head) > 0;
353             }
354 9 100 33     52 unless (defined $statements{'rdf:first'} and defined $statements{'rdf:rest'} and scalar(@{$statements{'rdf:first'} })==1 and scalar(@{ $statements{'rdf:rest'} })==1) {
  9   66     37  
  8   66     29  
355 1         20 throw RDF::Trine::Error -text => "Invalid structure found during rdf:List traversal";
356             }
357             $self->remove_statement($_)
358 8         13 foreach (@{$statements{'rdf:first'}}, @{$statements{'rdf:rest'}}, @{$statements{'rdf:type'}});
  8         15  
  8         14  
  8         35  
359            
360 8         25 $head = $statements{'rdf:rest'}->[0]->object;
361             }
362            
363 2         7 return;
364             }
365              
366             =item C<< get_sequence ( $seq ) >>
367              
368             Returns a list of nodes that are elements of the rdf:Seq sequence.
369              
370             =cut
371              
372             sub get_sequence {
373 0     0 1 0 my $self = shift;
374 0         0 my $head = shift;
375 0         0 my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
376 0         0 my @elements;
377 0         0 my $i = 1;
378 0         0 while (1) {
379 0         0 my $method = '_' . $i;
380 0         0 my (@elem) = $self->objects( $head, $rdf->$method() );
381 0 0       0 unless (scalar(@elem)) {
382 0         0 last;
383             }
384 0 0       0 if (scalar(@elem) > 1) {
385 0         0 my $count = scalar(@elem);
386 0         0 throw RDF::Trine::Error -text => "Invalid structure found during rdf:Seq access: $count elements found for element $i";
387             }
388 0         0 my $elem = $elem[0];
389 0 0       0 last unless (blessed($elem));
390 0         0 push(@elements, $elem);
391 0         0 $i++;
392             }
393 0         0 return @elements;
394             }
395              
396             =item C<< remove_statement ( $statement [, $context]) >>
397              
398             Removes the specified C<< $statement >> from the rdf store.
399              
400             =cut
401              
402             sub remove_statement {
403 26     26 1 50 my $self = shift;
404 26         69 my @args = @_;
405 26 100       71 if (my $log = $self->logger) {
406 1         2 my ($st, $context) = @args;
407 1 50       4 if (defined($context)) {
408 0         0 $st = RDF::Trine::Statement::Quad->new(($st->nodes)[0..2], $context);
409             }
410 1         6 $log->delete($st);
411             }
412 26         74 return $self->_store->remove_statement( @args );
413             }
414              
415             =item C<< remove_statements ( $subject, $predicate, $object [, $context] ) >>
416              
417             Removes all statements matching the supplied C<< $statement >> pattern from the rdf store.
418              
419             =cut
420              
421             sub remove_statements {
422 6     6 1 54 my $self = shift;
423 6 50       29 if (my $log = $self->logger) {
424 0         0 $log->delete($_) foreach (@_);
425             }
426 6         30 return $self->_store->remove_statements( @_ );
427             }
428              
429             =item C<< size >>
430              
431             Returns the number of statements in the model.
432              
433             =cut
434              
435             sub size {
436 26     26 1 576 my $self = shift;
437 26         84 $self->end_bulk_ops();
438 26         111 return $self->count_statements(undef, undef, undef, undef);
439             }
440              
441             =item C<< etag >>
442              
443             If the model is based on a store that has the capability and knowledge to
444             support caching, this method returns a persistent token that will remain
445             consistent as long as the store's data doesn't change. This token is acceptable
446             for use as an HTTP ETag.
447              
448             =cut
449              
450             sub etag {
451 3     3 1 7 my $self = shift;
452 3         7 my $store = $self->_store;
453 3 50       8 if ($store) {
454 3         11 return $store->etag;
455             }
456 0         0 return;
457             }
458              
459             =item C<< supports ( [ $feature ] ) >>
460              
461             If C<< $feature >> is specified, returns true if the feature is supported by the
462             underlying store, false otherwise. If C<< $feature >> is not specified, returns
463             a list of supported features.
464              
465             =cut
466              
467             sub supports {
468 0     0 1 0 my $self = shift;
469 0         0 my $store = $self->_store;
470 0 0       0 if ($store) {
471 0         0 return $store->supports( @_ );
472             }
473 0         0 return;
474             }
475              
476             =item C<< count_statements ( $subject, $predicate, $object ) >>
477              
478             Returns a count of all the statements matching the specified subject,
479             predicate and objects. Any of the arguments may be undef to match any value.
480              
481             =cut
482              
483             sub count_statements {
484 710     710 1 29344 my $self = shift;
485 710         2092 $self->end_bulk_ops();
486              
487 710 100       2049 if (scalar(@_) >= 4) {
488 58         110 my $graph = $_[3];
489 58 100 100     339 if (blessed($graph) and $graph->isa('RDF::Trine::Node::Resource') and $graph->uri_value eq 'tag:gwilliams@cpan.org,2010-01-01:RT:ALL') {
      100        
490 1         4 $_[3] = undef;
491             }
492             }
493 710         1516 return $self->_store->count_statements( @_ );
494             }
495              
496             =item C<< get_statements ($subject, $predicate, $object [, $context] ) >>
497              
498             Returns an L<iterator|RDF::Trine::Iterator> of all statements matching the specified
499             subject, predicate and objects from the rdf store. Any of the arguments may be undef
500             to match any value.
501              
502             If three or fewer arguments are given, the statements returned will be matched
503             based on triple semantics (the graph union of triples from all the named
504             graphs). If four arguments are given (even if C<< $context >> is undef),
505             statements will be matched based on quad semantics (the union of all quads in
506             the underlying store).
507              
508             =cut
509              
510             sub get_statements {
511 2526     2526 1 7118 my $self = shift;
512 2526         6990 $self->end_bulk_ops();
513            
514 2526         8114 my @pos = qw(subject predicate object graph);
515 2526         7820 foreach my $i (0 .. $#_) {
516 9467         16222 my $n = $_[$i];
517 9467 100       21562 next unless defined($n); # undef is OK
518 6585 100 66     38768 next if (blessed($n) and $n->isa('RDF::Trine::Node')); # node objects are OK
519 4         14 my $pos = $pos[$i];
520 4         12 local($Data::Dumper::Indent) = 0;
521 4         34 my $ser = Data::Dumper->Dump([$n], [$pos]);
522 4         298 throw RDF::Trine::Error::MethodInvocationError -text => "get_statements called with a value that isn't undef or a node object: $ser";
523             }
524            
525 2522 100       7752 if (scalar(@_) >= 4) {
526 2169         3920 my $graph = $_[3];
527 2169 50 100     11695 if (blessed($graph) and $graph->isa('RDF::Trine::Node::Resource') and $graph->uri_value eq 'tag:gwilliams@cpan.org,2010-01-01:RT:ALL') {
      66        
528 0         0 $_[3] = undef;
529             }
530             }
531 2522         6388 return $self->_store->get_statements( @_ );
532             }
533              
534             =item C<< get_pattern ( $bgp [, $context] [, %args ] ) >>
535              
536             Returns a stream object of all bindings matching the specified graph pattern.
537              
538             If C<< $context >> is given, restricts BGP matching to only quads with the
539             C<< $context >> value.
540              
541             C<< %args >> may contain an 'orderby' key-value pair to request a specific
542             ordering based on variable name. The value for the 'orderby' key should be an
543             ARRAY reference containing variable name and direction ('ASC' or 'DESC') tuples.
544             A valid C<< %args >> hash, therefore, might look like
545             C<< orderby => [qw(name ASC)] >> (corresponding to a SPARQL-like request to
546             'ORDER BY ASC(?name)').
547              
548             =cut
549              
550             sub get_pattern {
551 955     955 1 11928 my $self = shift;
552 955         1965 my $bgp = shift;
553 955         1674 my $context = shift;
554 955         2266 my @args = @_;
555 955         2780 my %args = @args;
556            
557 955         3028 $self->end_bulk_ops();
558 955 100 66     11232 my (@triples) = ($bgp->isa('RDF::Trine::Statement') or $bgp->isa('RDF::Query::Algebra::Filter'))
559             ? $bgp
560             : $bgp->triples;
561 955 100       3201 unless (@triples) {
562 5         73 throw RDF::Trine::Error::CompilationError -text => 'Cannot call get_pattern() with empty pattern';
563             }
564            
565 950         2362 my $store = $self->_store;
566             # while almost all models will delegate get_pattern() to the underlying
567             # store object, in some cases this isn't possible (union models don't have
568             # a single store, so have to fall back to the model-specific get_pattern()
569             # implementation).
570 950 100 100     6793 if (blessed($store) and $store->can('get_pattern')) {
571 41         125 return $self->_store->get_pattern( $bgp, $context, @args );
572             } else {
573 909 100       3768 if ($bgp->isa('RDF::Trine::Pattern')) {
574 903         3432 $bgp = $bgp->sort_for_join_variables();
575             }
576 909         3345 my $iter = $self->_get_pattern( $bgp, $context );
577 909 100       3112 if (my $ob = $args{orderby}) {
578 900         2592 my @order = @$ob;
579 900 100       3025 if (scalar(@order) % 2) {
580 1         11 throw RDF::Trine::Error::MethodInvocationError -text => "Invalid arguments to orderby argument in get_pattern";
581             }
582            
583 899         3538 my @results = $iter->get_all();
584 899         2684 my $order_vars = scalar(@order) / 2;
585 899         1621 my %seen;
586 899         2118 foreach my $r (@results) {
587 1687         4140 foreach my $var (keys %$r) {
588 6382         11377 $seen{$var}++;
589             }
590             }
591            
592             @results = sort {
593 899         2855 my $r = 0;
  1445         2375  
594 1445         3475 foreach my $i (0 .. ($order_vars-1)) {
595 2357         4636 my $var = $order[$i*2];
596 2357         4638 my $rev = ($order[$i*2+1] =~ /DESC/i);
597 2357         7431 $r = RDF::Trine::Node::compare( $a->{$var}, $b->{$var} );
598 2357 100       5852 $r *= -1 if ($rev);
599 2357 100       5883 last if ($r);
600             }
601             $r;
602             } @results;
603            
604 899         1622 my @sortedby;
605 899         2900 foreach my $i (0 .. ($order_vars-1)) {
606 2631         5164 my $var = $order[$i*2];
607 2631         4493 my $dir = $order[$i*2+1];
608 2631 100       8423 push(@sortedby, $var, $dir) if ($seen{$var});
609             }
610 899         4300 $iter = RDF::Trine::Iterator::Bindings->new(\@results, undef, sorted_by => \@sortedby);
611             }
612 908         12750 return $iter;
613             }
614             }
615              
616             =item C<< get_sparql ( $sparql ) >>
617              
618             Returns a stream object of all bindings matching the specified graph pattern.
619              
620             =cut
621              
622             sub get_sparql {
623 0     0 1 0 my $self = shift;
624 0         0 return $self->_store->get_sparql( @_ );
625             }
626              
627             sub _get_pattern {
628 927     927   1735 my $self = shift;
629 927         1780 my $bgp = shift;
630 927         1655 my $context = shift;
631 927         1905 my @args = @_;
632            
633 927 100 66     7995 my (@triples) = ($bgp->isa('RDF::Trine::Statement') or $bgp->isa('RDF::Query::Algebra::Filter'))
634             ? $bgp
635             : $bgp->triples;
636 927 100       2721 if (1 == scalar(@triples)) {
637 918         1980 my $t = shift(@triples);
638 918         3129 my @nodes = $t->nodes;
639 918         1841 my %vars;
640 918         2445 my @names = qw(subject predicate object context);
641 918         2881 foreach my $n (0 .. $#nodes) {
642 3542 100       11570 if ($nodes[$n]->isa('RDF::Trine::Node::Variable')) {
643 3438         8490 $vars{ $names[ $n ] } = $nodes[$n]->name;
644             }
645             }
646 918 50       2598 if ($context) {
647 0         0 $nodes[3] = $context;
648             }
649 918         3029 my $iter = $self->get_statements( @nodes );
650 918         3530 my @vars = values %vars;
651             my $sub = sub {
652 2641     2641   7341 my $row = $iter->next;
653 2641 100       7121 return unless ($row);
654 1723         5214 my %data = map { $vars{ $_ } => $row->$_() } (keys %vars);
  6426         19152  
655 1723         9391 return RDF::Trine::VariableBindings->new( \%data );
656 918         4024 };
657 918         6660 return RDF::Trine::Iterator::Bindings->new( $sub, \@vars );
658             } else {
659 9         32 my $t = pop(@triples);
660 9         37 my $rhs = $self->_get_pattern( RDF::Trine::Pattern->new( $t ), $context, @args );
661 9         42 my $lhs = $self->_get_pattern( RDF::Trine::Pattern->new( @triples ), $context, @args );
662 9         24 my @inner;
663 9         48 while (my $row = $rhs->next) {
664 21         86 push(@inner, $row);
665             }
666 9         23 my @results;
667 9         36 while (my $row = $lhs->next) {
668 15         40 RESULT: foreach my $irow (@inner) {
669 34         55 my %keysa;
670 34         90 my @keysa = keys %$irow;
671 34         107 @keysa{ @keysa } = (1) x scalar(@keysa);
672 34         72 my @shared = grep { exists $keysa{ $_ } } (keys %$row);
  34         98  
673 34         68 foreach my $key (@shared) {
674 25         48 my $val_a = $irow->{ $key };
675 25         44 my $val_b = $row->{ $key };
676 25 50 33     108 next unless (defined($val_a) and defined($val_b));
677 25         87 my $equal = $val_a->equal( $val_b );
678 25 100       70 unless ($equal) {
679 12         53 next RESULT;
680             }
681             }
682            
683 22         50 my $jrow = { (map { $_ => $irow->{$_} } grep { defined($irow->{$_}) } keys %$irow), (map { $_ => $row->{$_} } grep { defined($row->{$_}) } keys %$row) };
  35         86  
  35         83  
  22         94  
  22         51  
684 22         92 push(@results, RDF::Trine::VariableBindings->new($jrow));
685             }
686             }
687 9         46 my $result = RDF::Trine::Iterator::Bindings->new( \@results, [ $bgp->referenced_variables ] );
688 9         45 return $result;
689             }
690             }
691              
692             =item C<< get_graphs >>
693              
694             =item C<< get_contexts >>
695              
696             Returns an L<iterator|RDF::Trine::Iterator> containing the nodes representing
697             the named graphs in the model.
698              
699             =cut
700              
701             sub get_contexts {
702 2     2 1 15 my $self = shift;
703 2         7 my $store = $self->_store;
704 2         8 $self->end_bulk_ops();
705 2         11 my $iter = $store->get_contexts( @_ );
706 2 50       9 if (wantarray) {
707 0         0 return $iter->get_all;
708             } else {
709 2         5 return $iter;
710             }
711             }
712             *get_graphs = \&get_contexts;
713              
714             =item C<< as_stream >>
715              
716             Returns an L<iterator|RDF::Trine::Iterator> containing every statement in the model.
717              
718             =cut
719              
720             sub as_stream {
721 788     788 1 3359 my $self = shift;
722 788         2529 $self->end_bulk_ops();
723 788         2381 my $st = RDF::Trine::Statement::Quad->new( map { variable($_) } qw(s p o g) );
  3152         8453  
724 788         4649 my $pat = RDF::Trine::Pattern->new( $st );
725 788         4079 my $stream = $self->get_pattern( $pat, undef, orderby => [ qw(s ASC p ASC o ASC) ] );
726 788         4130 return $stream->as_statements( qw(s p o g) );
727             }
728              
729             =item C<< as_hashref >>
730              
731             Returns a hashref representing the model in an RDF/JSON-like manner.
732              
733             A graph like this (in Turtle):
734              
735             @prefix ex: <http://example.com/> .
736            
737             ex:subject1
738             ex:predicate1
739             "Foo"@en ,
740             "Bar"^^ex:datatype1 .
741            
742             _:bnode1
743             ex:predicate2
744             ex:object2 ;
745             ex:predicate3 ;
746             _:bnode3 .
747              
748             Is represented like this as a hashref:
749              
750             {
751             "http://example.com/subject1" => {
752             "http://example.com/predicate1" => [
753             { 'type'=>'literal', 'value'=>"Foo", 'lang'=>"en" },
754             { 'type'=>'literal', 'value'=>"Bar", 'datatype'=>"http://example.com/datatype1" },
755             ],
756             },
757             "_:bnode1" => {
758             "http://example.com/predicate2" => [
759             { 'type'=>'uri', 'value'=>"http://example.com/object2" },
760             ],
761             "http://example.com/predicate2" => [
762             { 'type'=>'bnode', 'value'=>"_:bnode3" },
763             ],
764             },
765             }
766              
767             Note that the type of subjects (resource or blank node) is indicated
768             entirely by the convention of starting blank nodes with "_:".
769              
770             This hashref structure is compatible with RDF/JSON and with the ARC2
771             library for PHP.
772              
773             =cut
774              
775             sub as_hashref {
776 2     2 1 5 my $self = shift;
777 2         7 $self->end_bulk_ops();
778 2         10 return $self->as_stream->as_hashref;
779             }
780              
781             =item C<< as_graphviz >>
782              
783             Returns a L<GraphViz> object of the RDF graph of this model, ignoring graph
784             names/contexts.
785              
786             This method will attempt to load the L<GraphViz> module at runtime and will fail
787             if the module is unavailable.
788              
789             =cut
790              
791             sub as_graphviz {
792 0     0 1 0 my $self = shift;
793 0         0 require GraphViz;
794 0         0 my $g = GraphViz->new();
795 0         0 my %seen;
796 0         0 my $iter = $self->as_stream;
797 0         0 while (my $t = $iter->next) {
798 0         0 my @nodes;
799 0         0 foreach my $pos (qw(subject object)) {
800 0         0 my $n = $t->$pos();
801 0 0       0 my $label = ($n->isa('RDF::Trine::Node::Literal')) ? $n->literal_value : $n->as_string;
802 0         0 push(@nodes, $label);
803 0 0       0 unless ($seen{ $label }++) {
804 0         0 $g->add_node( $label );
805             }
806             }
807 0         0 $g->add_edge( @nodes, label => $t->predicate->as_string );
808             }
809 0         0 return $g;
810             }
811              
812             =back
813              
814             =head2 Node-Centric Graph API
815              
816             =over 4
817              
818             =item C<< subjects ( $predicate, $object ) >>
819              
820             Returns a list of the nodes that appear as the subject of statements with the
821             specified C<< $predicate >> and C<< $object >>. Either of the two arguments may
822             be undef to signify a wildcard.
823              
824             =cut
825              
826             sub subjects {
827 14     14 1 33 my $self = shift;
828 14         32 my $pred = shift;
829 14         31 my $obj = shift;
830 14         33 my $graph = shift;
831 14         62 $self->end_bulk_ops();
832 14         74 my $iter = $self->get_statements( undef, $pred, $obj, $graph );
833 14         36 my %nodes;
834 14         63 while (my $st = $iter->next) {
835 22         76 my $subj = $st->subject;
836 22         92 $nodes{ $subj->as_string } = $subj;
837             }
838 14 100       50 if (wantarray) {
839 10         217 return values(%nodes);
840             } else {
841 4         26 return RDF::Trine::Iterator->new( [values(%nodes)] );
842             }
843             }
844              
845             =item C<< predicates ( $subject, $object ) >>
846              
847             Returns a list of the nodes that appear as the predicate of statements with the
848             specified C<< $subject >> and C<< $object >>. Either of the two arguments may
849             be undef to signify a wildcard.
850              
851             =cut
852              
853             sub predicates {
854 12     12 1 28 my $self = shift;
855 12         23 my $subj = shift;
856 12         24 my $obj = shift;
857 12         23 my $graph = shift;
858 12         38 $self->end_bulk_ops();
859 12         58 my $iter = $self->get_statements( $subj, undef, $obj, $graph );
860 12         28 my %nodes;
861 12         52 while (my $st = $iter->next) {
862 20         71 my $pred = $st->predicate;
863 20         72 $nodes{ $pred->as_string } = $pred;
864             }
865 12 100       34 if (wantarray) {
866 8         201 return values(%nodes);
867             } else {
868 4         22 return RDF::Trine::Iterator->new( [values(%nodes)] );
869             }
870             }
871              
872             =item C<< objects ( $subject, $predicate [, $graph ] [, %options ] ) >>
873              
874             Returns a list of the nodes that appear as the object of statements with the
875             specified C<< $subject >> and C<< $predicate >>. Either of the two arguments
876             may be undef to signify a wildcard. You can further filter objects using the
877             C<< %options >> argument. Keys in C<< %options >> indicate the restriction type
878             and may be 'type', 'language', or 'datatype'. The value of the 'type' key may be
879             one of 'node', 'nil', 'blank', 'resource', 'literal', or 'variable'. The use of
880             either 'language' or 'datatype' restrict objects to literal nodes with a
881             specific language or datatype value, respectively.
882              
883             =cut
884              
885             sub objects {
886 1319     1319 1 5795 my $self = shift;
887 1319         2245 my $subj = shift;
888 1319         2514 my $pred = shift;
889 1319 50       5130 my ($graph, %options) = (@_ % 2 == 0) ? (undef, @_) : @_;
890 1319         2989 my $type = $options{type};
891 1319 100 100     6895 $type = 'literal' if ($options{language} or $options{datatype});
892 1319 100 100     3899 if ($options{datatype} and not blessed($options{datatype})) {
893 1         8 $options{datatype} = RDF::Trine::Node::Resource->new($options{datatype});
894             }
895            
896 1319 100       3172 if (defined $type) {
897 6 50       32 if ($type =~ /^(node|nil|blank|resource|literal|variable)$/) {
898 6         18 $type = "is_$type";
899             } else {
900 0         0 throw RDF::Trine::Error::CompilationError -text => "unknown type"
901             }
902             }
903 1319         4058 $self->end_bulk_ops();
904 1319         4354 my $iter = $self->get_statements( $subj, $pred, undef, $graph );
905 1319         3053 my %nodes;
906 1319         4477 while (my $st = $iter->next) {
907 1351         5212 my $obj = $st->object;
908 1351 100       3651 if (defined $type) {
909 30 100       128 next unless $obj->$type;
910 14 100       54 if ($options{language}) {
    100          
911 3         10 my $lang = $obj->literal_value_language;
912 3 100 66     23 next unless ($lang and $lang eq $options{language});
913             } elsif ($options{datatype}) {
914 6         16 my $dt = $obj->literal_datatype;
915 6 100 66     26 next unless ($dt and $dt eq $options{datatype}->uri_value);
916             }
917             }
918 1329         5357 $nodes{ $obj->as_string } = $obj;
919             }
920 1319 100       3613 if (wantarray) {
921 1315         11319 return values(%nodes);
922             } else {
923 4         26 return RDF::Trine::Iterator->new( [values(%nodes)] );
924             }
925             }
926              
927             =item C<< objects_for_predicate_list ( $subject, @predicates ) >>
928              
929             Given the RDF::Trine::Node objects C<< $subject >> and C<< @predicates >>,
930             finds all matching triples in the model with the specified subject and any
931             of the given predicates, and returns a list of object values (in the partial
932             order given by the ordering of C<< @predicates >>).
933              
934             =cut
935              
936             sub objects_for_predicate_list {
937 94     94 1 174 my $self = shift;
938 94         156 my $node = shift;
939 94         229 my @preds = @_;
940 94         271 $self->end_bulk_ops();
941 94         201 my @objects;
942 94         215 foreach my $p (@preds) {
943 137         392 my $iter = $self->get_statements( $node, $p );
944 137         406 while (my $s = $iter->next) {
945 137 50       340 if (not(wantarray)) {
946 0         0 return $s->object;
947             } else {
948 137         370 push( @objects, $s->object );
949             }
950             }
951             }
952 94         369 return @objects;
953             }
954              
955             =item C<< bounded_description ( $node ) >>
956              
957             Returns an RDF::Trine::Iterator::Graph object over the bounded description
958             triples for C<< $node >> (all triples resulting from a graph traversal starting
959             with C<< node >> and stopping at non-blank nodes).
960              
961             =cut
962              
963             sub bounded_description {
964 8     8 1 20 my $self = shift;
965 8         16 my $node = shift;
966 8         25 $self->end_bulk_ops();
967 8         22 my %seen;
968 8         22 my @nodes = $node;
969 8         15 my @statements;
970             my $sub = sub {
971 35 100 100 35   168 return if (not(@statements) and not(@nodes));
972 30         54 while (1) {
973 33 100       81 if (not(@statements)) {
974 20         121 my $l = Log::Log4perl->get_logger("rdf.trine.model");
975 20 100       1527 return unless (scalar(@nodes));
976 17         36 my $n = shift(@nodes);
977             # warn "CBD handling node " . $n->sse . "\n";
978 17 100       56 next if ($seen{ $n->sse });
979             try {
980 16         576 my $st = RDF::Trine::Statement->new( $n, map { variable($_) } qw(p o) );
  32         96  
981 16         85 my $pat = RDF::Trine::Pattern->new( $st );
982 16         66 my $sts = $self->get_pattern( $pat, undef, orderby => [ qw(p ASC o ASC) ] );
983             # my $sts = $stream->as_statements( qw(s p o) );
984             # my $sts = $self->get_statements( $n );
985 16         62 my @s = grep { not($seen{$_->{'o'}->sse}) } $sts->get_all;
  26         76  
986             # warn "+ " . $_->as_string . "\n" for (@s);
987 16         45 push(@statements, map { RDF::Trine::Statement->new($n, @{ $_ }{qw(p o)}) } @s);
  26         45  
  26         84  
988             } catch RDF::Trine::Error::UnimplementedError with {
989 0         0 $l->debug('[model] Ignored UnimplementedError in bounded_description: ' . $_[0]->{'-text'});
990 16         135 };
991             try {
992 16         483 my $st = RDF::Trine::Statement->new( (map { variable($_) } qw(s p)), $n );
  32         91  
993 16         56 my $pat = RDF::Trine::Pattern->new( $st );
994 16         70 my $sts = $self->get_pattern( $pat, undef, orderby => [ qw(s ASC p ASC) ] );
995             # my $sts = $stream->as_statements( qw(s p o) );
996             # my $sts = $self->get_statements( undef, undef, $n );
997 16   100     62 my @s = grep { not($seen{$_->{'s'}->sse}) and not($_->{'s'}->equal($n)) } $sts->get_all;
  13         43  
998             # warn "- " . $_->as_string . "\n" for (@s);
999 16         69 push(@statements, map { RDF::Trine::Statement->new(@{ $_ }{qw(s p)}, $n) } @s);
  1         2  
  1         5  
1000             } catch RDF::Trine::Error::UnimplementedError with {
1001 0         0 $l->debug('[model] Ignored UnimplementedError in bounded_description: ' . $_[0]->{'-text'});
1002 16         381 };
1003 16         334 $seen{ $n->sse }++
1004             }
1005 29 100       85 last if (scalar(@statements));
1006             }
1007 27 50       66 return unless (scalar(@statements));
1008 27         50 my $st = shift(@statements);
1009 27 100 66     82 if ($st->object->isa('RDF::Trine::Node::Blank') and not($seen{ $st->object->sse })) {
1010             # warn "+ CBD pushing " . $st->object->sse . "\n";
1011 9         27 push(@nodes, $st->object);
1012             }
1013 27 50 66     76 if ($st->subject->isa('RDF::Trine::Node::Blank') and not($seen{ $st->subject->sse })) {
1014             # warn "- CBD pushing " . $st->subject->sse . "\n";
1015 0         0 push(@nodes, $st->subject);
1016             }
1017 27         68 return $st;
1018 8         51 };
1019 8         60 return RDF::Trine::Iterator::Graph->new( $sub );
1020             }
1021              
1022             =item C<< as_string >>
1023              
1024             =cut
1025              
1026             sub as_string {
1027 0     0 1 0 my $self = shift;
1028 0         0 $self->end_bulk_ops();
1029 0         0 my $iter = $self->get_statements( undef, undef, undef, undef );
1030 0         0 my @rows;
1031 0         0 my @names = qw[subject predicate object context];
1032 0         0 while (my $row = $iter->next) {
1033 0         0 push(@rows, [map {$row->$_()->as_string} @names]);
  0         0  
1034             }
1035 0         0 my @rule = qw(- +);
1036 0         0 my @headers = (\q"| ");
1037 0         0 push(@headers, map { $_ => \q" | " } @names);
  0         0  
1038 0         0 pop @headers;
1039 0         0 push @headers => (\q" |");
1040 0         0 my $table = Text::Table->new(@names);
1041 0         0 $table->rule(@rule);
1042 0         0 $table->body_rule(@rule);
1043 0         0 $table->load(@rows);
1044 0         0 my $size = scalar(@rows);
1045             return join('',
1046             $table->rule(@rule),
1047             $table->title,
1048             $table->rule(@rule),
1049 0         0 map({ $table->body($_) } 0 .. @rows),
  0         0  
1050             $table->rule(@rule)
1051             ) . "$size statements\n";
1052             }
1053              
1054             sub _store {
1055 16331     16331   28491 my $self = shift;
1056 16331         47304 return $self->{store};
1057             }
1058              
1059             sub _debug {
1060 0     0     my $self = shift;
1061 0           my $warn = shift;
1062 0           my $stream = $self->get_statements( undef, undef, undef, undef );
1063 0           my $l = Log::Log4perl->get_logger("rdf.trine.model");
1064 0           $l->debug( 'model statements:' );
1065 0 0         if ($warn) {
1066 0           warn "Model $self:\n";
1067             }
1068 0           my $count = 0;
1069 0           while (my $s = $stream->next) {
1070 0           $count++;
1071 0 0         if ($warn) {
1072 0           warn $s->as_string . "\n";
1073             }
1074 0           $l->debug('[model]' . $s->as_string);
1075             }
1076 0 0         if ($warn) {
1077 0           warn "$count statements\n";
1078             }
1079             }
1080              
1081             1;
1082              
1083             __END__
1084              
1085             =back
1086              
1087             =head1 BUGS
1088              
1089             Please report any bugs or feature requests to through the GitHub web interface
1090             at L<https://github.com/kasei/perlrdf/issues>.
1091              
1092             =head1 AUTHOR
1093              
1094             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
1095              
1096             =head1 COPYRIGHT
1097              
1098             Copyright (c) 2006-2012 Gregory Todd Williams. This
1099             program is free software; you can redistribute it and/or modify it under
1100             the same terms as Perl itself.
1101              
1102             =cut