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.018
11              
12             =head1 METHODS
13              
14             =over 4
15              
16             =cut
17              
18             package RDF::Trine::Model;
19              
20 68     68   405 use strict;
  68         148  
  68         1565  
21 68     68   313 use warnings;
  68         152  
  68         1509  
22 68     68   300 no warnings 'redefine';
  68         139  
  68         2612  
23              
24             our ($VERSION);
25             BEGIN {
26 68     68   1275 $VERSION = '1.018';
27             }
28              
29 68     68   343 use Scalar::Util qw(blessed refaddr);
  68         151  
  68         2924  
30 68     68   389 use Log::Log4perl;
  68         143  
  68         481  
31              
32 68     68   3839 use RDF::Trine::Error qw(:try);
  68         154  
  68         368  
33 68     68   8194 use RDF::Trine qw(variable);
  68         162  
  68         2614  
34 68     68   391 use RDF::Trine::Node;
  68         151  
  68         1903  
35 68     68   24347 use RDF::Trine::Pattern;
  68         178  
  68         1829  
36 68     68   464 use RDF::Trine::Store;
  68         157  
  68         1152  
37 68     68   24559 use RDF::Trine::Model::Dataset;
  68         203  
  68         290125  
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 3569 my $class = shift;
48 945 100       2620 if (@_) {
49 940         1676 my $store = shift;
50 940 100       4245 $store = RDF::Trine::Store->new( $store ) unless (blessed($store));
51 940         2624 my %args = @_;
52 940         5248 my $self = bless({
53             store => $store,
54             temporary => 0,
55             added => 0,
56             threshold => -1,
57             %args
58             }, $class);
59             } else {
60 5         26 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 13166 my $class = shift;
72 587         3215 my $store = RDF::Trine::Store::Memory->new();
73             # my $store = RDF::Trine::Store::DBI->temporary_store();
74 587         2029 my $self = $class->new( $store );
75 587         1368 $self->{temporary} = 1;
76 587         1105 $self->{threshold} = 25_000;
77 587         1407 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 1473 my $self = shift;
107 773         2188 my $store = $self->_store;
108 773 50 33     6957 if (blessed($store) and $store->can('_begin_bulk_ops')) {
109 773         2630 $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 11123 my $self = shift;
122 7228         15947 my $store = $self->_store;
123 7228 100 66     49696 if (blessed($store) and $store->can('_end_bulk_ops')) {
124 7224         20518 $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 7013 my $self = shift;
138 4106 100       10210 if (scalar(@_)) {
139 1         3 $self->{'logger'} = shift;
140             }
141 4106         11719 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 49307 my ($self, @args) = @_;
152 4085 100       16092 if ($args[0]->isa('RDF::Trine::Statement')) {
153 4073         12576 foreach my $n ($args[0]->nodes) {
154 12264 50 66     80414 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         58 throw RDF::Trine::Error::MethodInvocationError -text => 'Argument is not an RDF::Trine::Statement';
160             }
161 4073 100       12068 if ($self->{temporary}) {
162 3338 100       9792 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       13 if ($store->can('_begin_bulk_ops')) {
167 1         4 $store->_begin_bulk_ops();
168             }
169 1         8 while (my $st = $iter->next) {
170 10         36 $store->add_statement( $st );
171             }
172 1 50       7 if ($store->can('_begin_bulk_ops')) {
173 1         10 $store->_end_bulk_ops();
174             }
175 1         4 $self->{store} = $store;
176 1         32 $self->{temporary} = 0;
177             # warn "*** upgraded to a DBI store";
178             }
179             }
180            
181 4073 100       10801 if (my $log = $self->logger) {
182 3         7 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         10 $log->add($st);
187             }
188            
189 4073         10786 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 381 my $self = shift;
202 49         93 my $index = shift;
203 49         80 my $context = shift;
204            
205 49         160 $self->begin_bulk_ops();
206 49         178 foreach my $s (keys %$index) {
207 78 100       554 my $ts = ( $s =~ /^_:(.*)$/ ) ?
208             RDF::Trine::Node::Blank->new($1) :
209             RDF::Trine::Node::Resource->new($s);
210            
211 78         149 foreach my $p (keys %{ $index->{$s} }) {
  78         272  
212 107         406 my $tp = RDF::Trine::Node::Resource->new($p);
213            
214 107         205 foreach my $O (@{ $index->{$s}->{$p} }) {
  107         286  
215 128         203 my $to;
216            
217             # $O should be a hashref, but we can do a little error-correcting.
218 128 100       377 unless (ref $O) {
219 34 50       192 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         42 $O = { 'value'=>$1, 'type'=>'literal', 'lang'=>$2 };
225             } else {
226 27         89 $O = { 'value'=>$O, 'type'=>'literal' };
227             }
228             }
229            
230 128 100       433 if (lc $O->{'type'} eq 'literal') {
231             $to = RDF::Trine::Node::Literal->new(
232 74         407 $O->{'value'}, $O->{'lang'}, $O->{'datatype'});
233             } else {
234             $to = ( $O->{'value'} =~ /^_:(.*)$/ ) ?
235             RDF::Trine::Node::Blank->new($1) :
236 54 100       308 RDF::Trine::Node::Resource->new($O->{'value'});
237             }
238            
239 128 50 33     742 if ($ts and $tp and $to) {
      33        
240 128         472 my $st = RDF::Trine::Statement->new($ts, $tp, $to);
241 128         377 $self->add_statement($st, $context);
242             }
243             }
244             }
245             }
246 49         142 $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 3 my $self = shift;
300 1         3 my $head = shift;
301 1         10 my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
302 1         11 my @elements;
303             my %seen;
304 1   66     13 while (blessed($head) and not($head->isa('RDF::Trine::Node::Resource') and $head->uri_value eq $rdf->nil->uri_value)) {
      66        
305 291 50       1400 if ($seen{ $head->as_string }++) {
306 0         0 throw RDF::Trine::Error -text => "Loop found during rdf:List traversal";
307             }
308 291         2204 my @n = $self->objects( $head, $rdf->first );
309 291 50       957 if (scalar(@n) != 1) {
310 0         0 throw RDF::Trine::Error -text => "Invalid structure found during rdf:List traversal";
311             }
312 291         727 push(@elements, @n);
313 291         2137 ($head) = $self->objects( $head, $rdf->rest );
314             }
315 1         3 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 69 my $self = shift;
329 5         14 my $head = shift;
330 5         38 my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
331 5         54 my %args = @_;
332 5         13 my %seen;
333            
334 5   66     68 while (blessed($head) and not($head->isa('RDF::Trine::Node::Resource') and $head->uri_value eq $rdf->nil->uri_value)) {
      66        
335 11 50       53 if ($seen{ $head->as_string }++) {
336 0         0 throw RDF::Trine::Error -text => "Loop found during rdf:List traversal";
337             }
338 11         57 my $stream = $self->get_statements($head, undef, undef);
339 11         29 my %statements;
340 11         50 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     226 }->{$st->predicate->uri} || 'other';
346 29 50 66     131 $statement_type = 'other'
347             if $statement_type eq 'rdf:type' && !$st->object->equal($rdf->List);
348 29         63 push @{$statements{$statement_type}}, $st;
  29         156  
349             }
350 11 100       51 if ($args{orphan_check}) {
351 3 100 66     16 return $head if defined $statements{other} && scalar(@{ $statements{other} }) > 0;
  1         33  
352 2 100       13 return $head if $self->count_statements(undef, undef, $head) > 0;
353             }
354 9 100 33     78 unless (defined $statements{'rdf:first'} and defined $statements{'rdf:rest'} and scalar(@{$statements{'rdf:first'} })==1 and scalar(@{ $statements{'rdf:rest'} })==1) {
  9   66     63  
  8   66     38  
355 1         25 throw RDF::Trine::Error -text => "Invalid structure found during rdf:List traversal";
356             }
357             $self->remove_statement($_)
358 8         17 foreach (@{$statements{'rdf:first'}}, @{$statements{'rdf:rest'}}, @{$statements{'rdf:type'}});
  8         24  
  8         18  
  8         53  
359            
360 8         51 $head = $statements{'rdf:rest'}->[0]->object;
361             }
362            
363 2         8 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 61 my $self = shift;
404 26         89 my @args = @_;
405 26 100       109 if (my $log = $self->logger) {
406 1         3 my ($st, $context) = @args;
407 1 50       3 if (defined($context)) {
408 0         0 $st = RDF::Trine::Statement::Quad->new(($st->nodes)[0..2], $context);
409             }
410 1         5 $log->delete($st);
411             }
412 26         102 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 63 my $self = shift;
423 6 50       30 if (my $log = $self->logger) {
424 0         0 $log->delete($_) foreach (@_);
425             }
426 6         28 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 475 my $self = shift;
437 26         93 $self->end_bulk_ops();
438 26         106 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 9 my $self = shift;
452 3         8 my $store = $self->_store;
453 3 50       9 if ($store) {
454 3         10 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 29636 my $self = shift;
485 710         2271 $self->end_bulk_ops();
486              
487 710 100       1995 if (scalar(@_) >= 4) {
488 58         105 my $graph = $_[3];
489 58 100 100     316 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         1442 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 6671 my $self = shift;
512 2526         6446 $self->end_bulk_ops();
513            
514 2526         7162 my @pos = qw(subject predicate object graph);
515 2526         7360 foreach my $i (0 .. $#_) {
516 9467         15318 my $n = $_[$i];
517 9467 100       20521 next unless defined($n); # undef is OK
518 6585 100 66     37250 next if (blessed($n) and $n->isa('RDF::Trine::Node')); # node objects are OK
519 4         11 my $pos = $pos[$i];
520 4         12 local($Data::Dumper::Indent) = 0;
521 4         41 my $ser = Data::Dumper->Dump([$n], [$pos]);
522 4         348 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       7246 if (scalar(@_) >= 4) {
526 2169         3955 my $graph = $_[3];
527 2169 50 100     10684 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         5632 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 11541 my $self = shift;
552 955         1520 my $bgp = shift;
553 955         1490 my $context = shift;
554 955         2148 my @args = @_;
555 955         2524 my %args = @args;
556            
557 955         2682 $self->end_bulk_ops();
558 955 100 66     10038 my (@triples) = ($bgp->isa('RDF::Trine::Statement') or $bgp->isa('RDF::Query::Algebra::Filter'))
559             ? $bgp
560             : $bgp->triples;
561 955 100       2631 unless (@triples) {
562 5         70 throw RDF::Trine::Error::CompilationError -text => 'Cannot call get_pattern() with empty pattern';
563             }
564            
565 950         2021 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     6051 if (blessed($store) and $store->can('get_pattern')) {
571 41         153 return $self->_store->get_pattern( $bgp, $context, @args );
572             } else {
573 909 100       2973 if ($bgp->isa('RDF::Trine::Pattern')) {
574 903         2904 $bgp = $bgp->sort_for_join_variables();
575             }
576 909         2622 my $iter = $self->_get_pattern( $bgp, $context );
577 909 100       2741 if (my $ob = $args{orderby}) {
578 900         2458 my @order = @$ob;
579 900 100       2624 if (scalar(@order) % 2) {
580 1         10 throw RDF::Trine::Error::MethodInvocationError -text => "Invalid arguments to orderby argument in get_pattern";
581             }
582            
583 899         2868 my @results = $iter->get_all();
584 899         2239 my $order_vars = scalar(@order) / 2;
585 899         1518 my %seen;
586 899         1959 foreach my $r (@results) {
587 1687         3887 foreach my $var (keys %$r) {
588 6382         10747 $seen{$var}++;
589             }
590             }
591            
592             @results = sort {
593 899         2303 my $r = 0;
  1450         2123  
594 1450         2895 foreach my $i (0 .. ($order_vars-1)) {
595 2361         3894 my $var = $order[$i*2];
596 2361         3964 my $rev = ($order[$i*2+1] =~ /DESC/i);
597 2361         5995 $r = RDF::Trine::Node::compare( $a->{$var}, $b->{$var} );
598 2361 100       5005 $r *= -1 if ($rev);
599 2361 100       5088 last if ($r);
600             }
601             $r;
602             } @results;
603            
604 899         1475 my @sortedby;
605 899         2312 foreach my $i (0 .. ($order_vars-1)) {
606 2631         4634 my $var = $order[$i*2];
607 2631         4015 my $dir = $order[$i*2+1];
608 2631 100       7718 push(@sortedby, $var, $dir) if ($seen{$var});
609             }
610 899         3685 $iter = RDF::Trine::Iterator::Bindings->new(\@results, undef, sorted_by => \@sortedby);
611             }
612 908         11684 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   1481 my $self = shift;
629 927         1390 my $bgp = shift;
630 927         1438 my $context = shift;
631 927         1738 my @args = @_;
632            
633 927 100 66     7283 my (@triples) = ($bgp->isa('RDF::Trine::Statement') or $bgp->isa('RDF::Query::Algebra::Filter'))
634             ? $bgp
635             : $bgp->triples;
636 927 100       2290 if (1 == scalar(@triples)) {
637 918         1683 my $t = shift(@triples);
638 918         2625 my @nodes = $t->nodes;
639 918         1632 my %vars;
640 918         2114 my @names = qw(subject predicate object context);
641 918         2374 foreach my $n (0 .. $#nodes) {
642 3542 100       10481 if ($nodes[$n]->isa('RDF::Trine::Node::Variable')) {
643 3438         7808 $vars{ $names[ $n ] } = $nodes[$n]->name;
644             }
645             }
646 918 50       2442 if ($context) {
647 0         0 $nodes[3] = $context;
648             }
649 918         2735 my $iter = $self->get_statements( @nodes );
650 918         3127 my @vars = values %vars;
651             my $sub = sub {
652 2641     2641   6947 my $row = $iter->next;
653 2641 100       6524 return unless ($row);
654 1723         4414 my %data = map { $vars{ $_ } => $row->$_() } (keys %vars);
  6426         18317  
655 1723         8091 return RDF::Trine::VariableBindings->new( \%data );
656 918         3416 };
657 918         5389 return RDF::Trine::Iterator::Bindings->new( $sub, \@vars );
658             } else {
659 9         19 my $t = pop(@triples);
660 9         25 my $rhs = $self->_get_pattern( RDF::Trine::Pattern->new( $t ), $context, @args );
661 9         40 my $lhs = $self->_get_pattern( RDF::Trine::Pattern->new( @triples ), $context, @args );
662 9         23 my @inner;
663 9         36 while (my $row = $rhs->next) {
664 21         77 push(@inner, $row);
665             }
666 9         25 my @results;
667 9         26 while (my $row = $lhs->next) {
668 15         33 RESULT: foreach my $irow (@inner) {
669 34         50 my %keysa;
670 34         75 my @keysa = keys %$irow;
671 34         86 @keysa{ @keysa } = (1) x scalar(@keysa);
672 34         68 my @shared = grep { exists $keysa{ $_ } } (keys %$row);
  34         85  
673 34         61 foreach my $key (@shared) {
674 25         43 my $val_a = $irow->{ $key };
675 25         36 my $val_b = $row->{ $key };
676 25 50 33     101 next unless (defined($val_a) and defined($val_b));
677 25         71 my $equal = $val_a->equal( $val_b );
678 25 100       64 unless ($equal) {
679 12         44 next RESULT;
680             }
681             }
682            
683 22         44 my $jrow = { (map { $_ => $irow->{$_} } grep { defined($irow->{$_}) } keys %$irow), (map { $_ => $row->{$_} } grep { defined($row->{$_}) } keys %$row) };
  35         80  
  35         71  
  22         67  
  22         44  
684 22         69 push(@results, RDF::Trine::VariableBindings->new($jrow));
685             }
686             }
687 9         40 my $result = RDF::Trine::Iterator::Bindings->new( \@results, [ $bgp->referenced_variables ] );
688 9         49 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 14 my $self = shift;
703 2         7 my $store = $self->_store;
704 2         5 $self->end_bulk_ops();
705 2         8 my $iter = $store->get_contexts( @_ );
706 2 50       6 if (wantarray) {
707 0         0 return $iter->get_all;
708             } else {
709 2         6 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 2438 my $self = shift;
722 788         2178 $self->end_bulk_ops();
723 788         1981 my $st = RDF::Trine::Statement::Quad->new( map { variable($_) } qw(s p o g) );
  3152         7721  
724 788         3747 my $pat = RDF::Trine::Pattern->new( $st );
725 788         3111 my $stream = $self->get_pattern( $pat, undef, orderby => [ qw(s ASC p ASC o ASC) ] );
726 788         3354 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 7 my $self = shift;
777 2         9 $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 35 my $self = shift;
828 14         34 my $pred = shift;
829 14         30 my $obj = shift;
830 14         30 my $graph = shift;
831 14         59 $self->end_bulk_ops();
832 14         70 my $iter = $self->get_statements( undef, $pred, $obj, $graph );
833 14         36 my %nodes;
834 14         70 while (my $st = $iter->next) {
835 22         74 my $subj = $st->subject;
836 22         96 $nodes{ $subj->as_string } = $subj;
837             }
838 14 100       50 if (wantarray) {
839 10         250 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 29 my $self = shift;
855 12         24 my $subj = shift;
856 12         23 my $obj = shift;
857 12         27 my $graph = shift;
858 12         50 $self->end_bulk_ops();
859 12         52 my $iter = $self->get_statements( $subj, undef, $obj, $graph );
860 12         29 my %nodes;
861 12         50 while (my $st = $iter->next) {
862 20         78 my $pred = $st->predicate;
863 20         83 $nodes{ $pred->as_string } = $pred;
864             }
865 12 100       39 if (wantarray) {
866 8         236 return values(%nodes);
867             } else {
868 4         19 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 5797 my $self = shift;
887 1319         2501 my $subj = shift;
888 1319         2229 my $pred = shift;
889 1319 50       5235 my ($graph, %options) = (@_ % 2 == 0) ? (undef, @_) : @_;
890 1319         2621 my $type = $options{type};
891 1319 100 100     6782 $type = 'literal' if ($options{language} or $options{datatype});
892 1319 100 100     3944 if ($options{datatype} and not blessed($options{datatype})) {
893 1         6 $options{datatype} = RDF::Trine::Node::Resource->new($options{datatype});
894             }
895            
896 1319 100       3129 if (defined $type) {
897 6 50       30 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         4270 $self->end_bulk_ops();
904 1319         4486 my $iter = $self->get_statements( $subj, $pred, undef, $graph );
905 1319         2514 my %nodes;
906 1319         4336 while (my $st = $iter->next) {
907 1351         6013 my $obj = $st->object;
908 1351 100       3513 if (defined $type) {
909 30 100       101 next unless $obj->$type;
910 14 100       49 if ($options{language}) {
    100          
911 3         8 my $lang = $obj->literal_value_language;
912 3 100 66     18 next unless ($lang and $lang eq $options{language});
913             } elsif ($options{datatype}) {
914 6         15 my $dt = $obj->literal_datatype;
915 6 100 66     28 next unless ($dt and $dt eq $options{datatype}->uri_value);
916             }
917             }
918 1329         5231 $nodes{ $obj->as_string } = $obj;
919             }
920 1319 100       3481 if (wantarray) {
921 1315         10707 return values(%nodes);
922             } else {
923 4         22 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 187 my $self = shift;
938 94         151 my $node = shift;
939 94         211 my @preds = @_;
940 94         255 $self->end_bulk_ops();
941 94         176 my @objects;
942 94         185 foreach my $p (@preds) {
943 137         344 my $iter = $self->get_statements( $node, $p );
944 137         398 while (my $s = $iter->next) {
945 137 50       333 if (not(wantarray)) {
946 0         0 return $s->object;
947             } else {
948 137         377 push( @objects, $s->object );
949             }
950             }
951             }
952 94         316 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 18 my $self = shift;
965 8         15 my $node = shift;
966 8         26 $self->end_bulk_ops();
967 8         18 my %seen;
968 8         21 my @nodes = $node;
969 8         14 my @statements;
970             my $sub = sub {
971 35 100 100 35   136 return if (not(@statements) and not(@nodes));
972 30         52 while (1) {
973 33 100       71 if (not(@statements)) {
974 20         121 my $l = Log::Log4perl->get_logger("rdf.trine.model");
975 20 100       1493 return unless (scalar(@nodes));
976 17         31 my $n = shift(@nodes);
977             # warn "CBD handling node " . $n->sse . "\n";
978 17 100       55 next if ($seen{ $n->sse });
979             try {
980 16         506 my $st = RDF::Trine::Statement->new( $n, map { variable($_) } qw(p o) );
  32         84  
981 16         73 my $pat = RDF::Trine::Pattern->new( $st );
982 16         61 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         58 my @s = grep { not($seen{$_->{'o'}->sse}) } $sts->get_all;
  26         71  
986             # warn "+ " . $_->as_string . "\n" for (@s);
987 16         47 push(@statements, map { RDF::Trine::Statement->new($n, @{ $_ }{qw(p o)}) } @s);
  26         44  
  26         81  
988             } catch RDF::Trine::Error::UnimplementedError with {
989 0         0 $l->debug('[model] Ignored UnimplementedError in bounded_description: ' . $_[0]->{'-text'});
990 16         129 };
991             try {
992 16         443 my $st = RDF::Trine::Statement->new( (map { variable($_) } qw(s p)), $n );
  32         90  
993 16         55 my $pat = RDF::Trine::Pattern->new( $st );
994 16         58 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     57 my @s = grep { not($seen{$_->{'s'}->sse}) and not($_->{'s'}->equal($n)) } $sts->get_all;
  13         44  
998             # warn "- " . $_->as_string . "\n" for (@s);
999 16         71 push(@statements, map { RDF::Trine::Statement->new(@{ $_ }{qw(s p)}, $n) } @s);
  1         3  
  1         6  
1000             } catch RDF::Trine::Error::UnimplementedError with {
1001 0         0 $l->debug('[model] Ignored UnimplementedError in bounded_description: ' . $_[0]->{'-text'});
1002 16         419 };
1003 16         322 $seen{ $n->sse }++
1004             }
1005 29 100       82 last if (scalar(@statements));
1006             }
1007 27 50       73 return unless (scalar(@statements));
1008 27         44 my $st = shift(@statements);
1009 27 100 66     73 if ($st->object->isa('RDF::Trine::Node::Blank') and not($seen{ $st->object->sse })) {
1010             # warn "+ CBD pushing " . $st->object->sse . "\n";
1011 9         31 push(@nodes, $st->object);
1012             }
1013 27 50 66     72 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         61 return $st;
1018 8         49 };
1019 8         63 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   25124 my $self = shift;
1056 16331         45102 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