File Coverage

blib/lib/ODO/Query/VariablePatternMap.pm
Criterion Covered Total %
statement 138 169 81.6
branch 43 76 56.5
condition 8 15 53.3
subroutine 20 22 90.9
pod 10 10 100.0
total 219 292 75.0


line stmt bran cond sub pod time code
1             #
2             # Copyright (c) 2005-2006 IBM Corporation.
3             #
4             # All rights reserved. This program and the accompanying materials
5             # are made available under the terms of the Eclipse Public License v1.0
6             # which accompanies this distribution, and is available at
7             # http://www.eclipse.org/legal/epl-v10.html
8             #
9             # File: $Source: /var/lib/cvs/ODO/lib/ODO/Query/VariablePatternMap.pm,v $
10             # Created by: Stephen Evanchik( evanchik@us.ibm.com )
11             # Created on: 01/13/2005
12             # Revision: $Id: VariablePatternMap.pm,v 1.2 2009-11-25 17:53:53 ubuntu Exp $
13             #
14             # Contributors:
15             # IBM Corporation - initial API and implementation
16             #
17             package ODO::Query::VariablePatternMap;
18              
19 5     5   1419 use strict;
  5         14  
  5         154  
20 5     5   25 use warnings;
  5         11  
  5         104  
21              
22 5     5   30 use ODO::Query::Simple;
  5         8  
  5         177  
23 5     5   2288 use ODO::Query::Simple::Mapper;
  5         12  
  5         207  
24              
25 5     5   582 use ODO::Graph::Simple;
  5         12  
  5         179  
26 5     5   91 use vars qw /$VERSION/;
  5         12  
  5         313  
27             $VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /: (\d+)\.(\d+)/;
28 5     5   25 use base qw/ODO/;
  5         10  
  5         10431  
29              
30             __PACKAGE__->mk_accessors(qw/patterns known_var_map/);
31              
32             =head1 NAME
33              
34             ODO::Graph::Query::VariablePatternMap
35              
36             =head1 SYNOPSIS
37              
38             =head1 DESCRIPTION
39              
40             =head1 METHODS
41              
42             =over
43              
44             =item add( $stmt, $item )
45              
46             =cut
47              
48             sub add {
49 6     6 1 55 my $self = shift;
50            
51 6         10 my $stmt = shift;
52 6 50 66     40 unless( UNIVERSAL::isa($stmt, 'ODO::Statement')
53             || $self->__is_pattern_key($stmt)) {
54 0         0 return undef;
55             }
56            
57 6         10 my $item = shift;
58            
59 6         14 my $key = $stmt;
60              
61             # The ternary operator wasn't working here.....
62 6 100       18 $key = $self->__make_pattern_key($stmt)
63             unless($self->__is_pattern_key($stmt) == 1);
64            
65 6         26 my $patternList = $self->patterns()->{ $key };
66            
67 6 100       62 $self->patterns()->{ $key } = $patternList = []
68             unless(UNIVERSAL::isa($patternList, 'ARRAY'));
69            
70 6         26 push @{ $patternList }, $item;
  6         15  
71            
72             # Record the fact that we have results for any variables
73             # in the triple match
74 6         31 my @comps = split('-', $key);
75            
76 6         15 foreach my $c (@comps) {
77            
78             next
79 18 50       187 if($c eq '*');
80            
81 18 100       54 $self->known_var_map()->{ $c } =
82             (exists($self->known_var_map()->{ $c })) ? $self->known_var_map()->{ $c } + 1 : 1;
83             }
84             }
85              
86              
87             =item add_list( $stmt, $list )
88              
89             =cut
90              
91             sub add_list {
92 2     2 1 5 my ($self, $stmt, $list) = @_;
93            
94 2         4 my $count = 0;
95 2         4 foreach my $item (@{ $list }) {
  2         5  
96 2         7 $self->add($stmt, $item);
97 2         74 $count++;
98             }
99             }
100              
101              
102             =item get_result_graph( [ $graph ])
103            
104             Gathers all of the triples for the patterns in the query
105             and results the concrete results in the form of an L.
106            
107             If an L is specified as a parameter, the method will
108             fill it with the results instead of creating a new graph object.
109              
110             =cut
111              
112             sub get_result_graph {
113 0     0 1 0 my ($self, $graph) = @_;
114            
115 0 0       0 $graph = ODO::Graph::Simple->Memory()
116             unless($graph);
117            
118 0         0 foreach my $p (keys(%{ $self->patterns() })) {
  0         0  
119 0         0 $graph->add($self->get_results_for_pattern($p));
120             }
121            
122 0         0 return $graph;
123             }
124              
125              
126             =item get_results_for_pattern( $stmt )
127              
128             Returns the array reference that potentially contains results
129             that can fill this triple pattern.
130            
131             =cut
132              
133             sub get_results_for_pattern {
134 1     1 1 3 my ($self, $stmt) = @_;
135            
136 1 50 33     14 unless(UNIVERSAL::isa($stmt, 'ODO::Statement') ||
137             $self->__is_pattern_key($stmt)) {
138 0         0 return undef;
139             }
140            
141 1         3 my $key = $stmt;
142              
143             # The ternary operator wasn't working here.....
144 1 50       3 $key = $self->__make_pattern_key($stmt)
145             unless($self->__is_pattern_key($stmt) == 1);
146              
147 1 50       4 return []
148             unless(UNIVERSAL::isa($self->patterns()->{ $key }, 'ARRAY'));
149            
150 1         11 return $self->patterns()->{ $key };
151             }
152              
153              
154             =item make_query_list( $stmt_match )
155              
156             =cut
157              
158             sub make_query_list {
159 0     0 1 0 my ($self, $stmt) = @_;
160            
161 0         0 my @queryList;
162            
163 0         0 foreach my $p (keys(%{ $self->patterns() })) {
  0         0  
164              
165             next # Only continue if some component matches
166 0 0       0 unless($self->__compare_pattern($p, $stmt));
167              
168 0         0 my $mapper = ODO::Query::Simple::Mapper->new($stmt, $self->__key_to_triple_match($p));
169            
170             # Each result for a particular pattern has its own query that will
171             # be used to get that segment of results
172 0         0 foreach my $r (@{ $self->get_results_for_pattern($p) }) {
  0         0  
173            
174 0         0 my $newTM = ODO::Query::Simple->new($stmt->subject(), $stmt->predicate(), $stmt->object());
175            
176 0         0 foreach my $component ('subject', 'predicate', 'object') {
177            
178 0         0 my $destComponent = $mapper->$component();
179            
180             # When a variable is present in both triples, place the value result's
181             # Triple::Match in to the variable's position of the new Triple::Match
182 0 0       0 $newTM->$component($r->$destComponent()), next
183             if($destComponent);
184            
185             # Convert variables to Any nodes in the Triple::Match
186 0 0       0 $newTM->$component($ODO::Node::ANY)
187             if(UNIVERSAL::isa($stmt->$component(), 'ODO::Node::Variable'));
188             }
189            
190 0         0 push @queryList, $newTM;
191             }
192            
193 0         0 return \@queryList;
194             }
195              
196             # Simple case: Convert any variables to Any nodes and return the single
197             # query triple when there results are not available.
198 0         0 my $newTM = ODO::Query::Simple->new($stmt->subject(), $stmt->predicate(), $stmt->object());
199            
200 0         0 foreach my $comp ('subject', 'predicate', 'object') {
201             next
202 0 0       0 unless(UNIVERSAL::isa($stmt->$comp(), 'ODO::Node::Variable'));
203            
204 0         0 $newTM->$comp($ODO::Node::ANY);
205             }
206            
207 0         0 return [ $newTM ];
208             }
209              
210              
211             =item count_pattern_results( $stmt )
212              
213             =cut
214              
215             sub count_pattern_results {
216 8     8 1 74 my $self = shift;
217            
218 8         12 my $stmt = shift;
219 8 50 66     50 unless(UNIVERSAL::isa($stmt, 'ODO::Statement') ||
220             $self->__is_pattern_key($stmt)) {
221 0         0 return undef;
222             }
223              
224 8         13 my $key = $stmt;
225              
226             # The ternary operator wasn't working here.....
227 8 100       22 $key = $self->__make_pattern_key($stmt)
228             unless($self->__is_pattern_key($stmt) == 1);
229            
230 8 100       27 return 0
231             unless(UNIVERSAL::isa($self->patterns()->{ $key }, 'ARRAY'));
232              
233 6         46 my $num = scalar(@{ $self->patterns()->{ $key } });
  6         17  
234            
235 6         59 return $num;
236             }
237              
238              
239             =item clear_pattern_results( $stmt )
240              
241             =cut
242              
243             sub clear_pattern_results {
244 3     3 1 18 my $self = shift;
245            
246 3         6 my $stmt = shift;
247 3 50 66     21 unless(UNIVERSAL::isa($stmt, 'ODO::Statement') ||
248             $self->__is_pattern_key($stmt)) {
249 0         0 return undef;
250             }
251            
252 3         8 my $key = $stmt;
253              
254             # The ternary operator wasn't working here.....
255 3 100       10 $key = $self->__make_pattern_key($stmt)
256             unless($self->__is_pattern_key($stmt) == 1);
257              
258 3         11 my $numResults = $self->count_pattern_results( $key );
259            
260 3         20 delete $self->patterns()->{ $key };
261            
262 3         59 my @comps = split('-', $key);
263            
264 3         7 foreach my $c (@comps) {
265            
266             next
267 9 50       85 if($c eq '*');
268            
269 9 50       30 $self->known_var_map()->{ $c } -= $numResults
270             if(exists($self->known_var_map()->{ $c }));
271             }
272             }
273              
274              
275             =item invalidate_results( $stmt, $resultsGraph )
276              
277             =cut
278              
279             sub invalidate_results {
280 1     1 1 8 my ($self, $stmt, $results_graph) = @_;
281            
282             #
283             # Example: if this triple pattern uses the variable B, then
284             # if other triple patterns use the variable B and have results, then
285             # we must remove from those results anything that is not in the
286             # intersection of the graph.
287             #
288 1         6 $self->__prune_patterns($stmt, $results_graph);
289            
290             # Finally, record the results
291 1         3 $self->clear_pattern_results( $stmt );
292              
293 1         15 my $result_set = $results_graph->query($ODO::Query::Simple::ALL_STATEMENTS);
294 1         10 my $new_results = $result_set->results();
295 1         7 $self->add_list($stmt, $new_results);
296            
297 1         6 return $results_graph;
298             }
299              
300              
301             =item known_variable_count( $stmt )
302            
303             =cut
304              
305             sub known_variable_count {
306 1     1 1 2866 my ($self, $stmt) = @_;
307            
308 1         4 my $v = 0;
309 1         3 foreach my $comp ('subject', 'predicate', 'object') {
310            
311             next
312 3 50       60 unless(UNIVERSAL::isa($stmt->$comp(), 'ODO::Node::Variable'));
313            
314 3 50 33     30 $v++
315             if( exists($self->known_var_map()->{ $stmt->$comp()->value() })
316             && $self->known_var_map()->{ $stmt->$comp()->value() } > 0);
317             }
318            
319             # my $stmt_string = join(' - ', ($stmt->subject()->value(), $stmt->predicate()->value(), $stmt->object()->value()));
320            
321 1         24 return $v;
322             }
323              
324              
325             =head1 Internal methods
326              
327             =over
328              
329             =item __is_pattern_key( $pk )
330              
331             =cut
332              
333             sub __is_pattern_key {
334 26     26   50 my ($self, $pk) = @_;
335 26 100       208 return (($pk =~ /^.+?-.+?-.+?$/) ? 1 : 0);
336             }
337              
338              
339             =item __make_pattern_key( $stmt )
340              
341             Makes a key such that variable names are included while all other
342             nodes are replaced by a star.
343              
344             =cut
345             sub __make_pattern_key {
346 18     18   81 my ($self, $stmt) = @_;
347            
348 18         21 my $key;
349            
350 18         36 foreach my $component ('subject', 'predicate', 'object') {
351              
352 54 50       679 $key .= $stmt->$component()->value() . '-', next
353             if(UNIVERSAL::isa($stmt->$component(), 'ODO::Node::Variable'));
354            
355 0         0 $key .= '*-';
356             }
357              
358 18 50       344 chop($key)
359             if($key =~ /-$/);
360            
361 18         61 return $key;
362             }
363              
364              
365             =item __key_to_triple_match( $key )
366              
367             Create a TripleMatch based on the key with the non-variable
368             components as Any nodes.
369              
370             =cut
371              
372             sub __key_to_triple_match {
373 1     1   3 my ($self, $key) = @_;
374            
375 1         4 my ($s, $p, $o ) = split('-', $key);
376              
377             # XXX: Don't use $ODO::Query::Simple::ALL_STATEMENTS here because modify the object
378 1         8 my $stmt = ODO::Query::Simple->new(undef, undef, undef);
379            
380 1 50       18 $stmt->subject( ODO::Node::Variable->new($s) )
381             if($s ne '*');
382            
383 1 50       18 $stmt->predicate( ODO::Node::Variable->new($p) )
384             if($s ne '*');
385            
386 1 50       24 $stmt->object( ODO::Node::Variable->new($o) )
387             if($o ne '*');
388            
389 1         12 return $stmt;
390             }
391              
392              
393             =item __compare_pattern( $pattern, $stmt )
394              
395             =cut
396              
397             sub __compare_pattern {
398 1     1   4 my ($self, $pattern, $stmt) = @_;
399              
400 1         4 my $key = $stmt;
401              
402             # The ternary operator wasn't working here.....
403 1 50       6 $key = $self->__make_pattern_key($stmt)
404             unless($self->__is_pattern_key($stmt) == 1);
405              
406 1         7 my ($s, $p, $o ) = split('-', $key);
407              
408 1 50       7 $s = ($s ne '*') ? "(^$s-|-$s-|-$s\$)" : '';
409            
410 1 50       6 $p = ($p ne '*') ? "(^$p-|-$p-|-$p\$)" : '';
411            
412 1 50       6 $o = ($o ne '*') ? "(^$o-|-$o-|-$o\$)" : '';
413              
414 1         5 my $compare = join('|', ( $s, $p, $o) );
415            
416 1 50       78 return ($pattern =~ /$compare/) ? 1 : 0;
417             }
418              
419              
420             =item __prune_patterns( $stmt, $inter_results )
421              
422             =cut
423              
424             sub __prune_patterns {
425 1     1   2 my ($self, $stmt, $inter_results) = @_;
426            
427 1         2 my @patternKeys;
428              
429 1         3 foreach my $p (keys(%{ $self->patterns() })) {
  1         5  
430              
431             next # Skip this pattern if it doesn't match
432 1 50       11 unless($self->__compare_pattern($p, $stmt));
433              
434            
435             # Create a mapped triple match that will be used
436             # in the pruning process.
437 1         8 my $dest_stmt = $self->__key_to_triple_match($p);
438 1         13 my $mapper = ODO::Query::Simple::Mapper->new($stmt, $dest_stmt);
439            
440            
441             # It may be that there are no results for this triple pattern (yet)
442             # so we should just keep everything
443             next
444 1 50       9 unless($self->count_pattern_results($dest_stmt) > 0);
445            
446 1         3 my $sourceMatch;
447 1         3 my $destMatchMap = {};
448              
449 1         5 my $destPatternResults = $self->get_results_for_pattern($dest_stmt);
450            
451            
452             # See if the variables in the results just found match to
453             # the same variables in other triple pattern results
454 1         12 my $result_set = $inter_results->query($ODO::Query::Simple::ALL_STATEMENTS);
455 1         14 my $results = $result_set->results();
456              
457 1         6 foreach my $s (@{ $results }) {
  1         3  
458              
459 2         4 $sourceMatch = 0;
460            
461 2         4 foreach my $d (@{ $destPatternResults }) {
  2         3  
462             # If they match, then leave them alone
463 3 100       12 if($mapper->compare($s, $d)) {
464            
465 1         3 $sourceMatch = 1;
466 1         3 $destMatchMap->{$d} = 1;
467            
468 1         5 last;
469             }
470             }
471            
472 2 100       10 $inter_results->remove($s)
473             unless($sourceMatch)
474            
475             } # end result processing
476            
477 1         2 my @newDestPatternResults;
478            
479             # Remove the destination patterns that never matched
480 1         2 foreach my $d (@{ $destPatternResults }) {
  1         3  
481 2 100       9 if(exists($destMatchMap->{$d})) {
482 1         3 push @newDestPatternResults, $d;
483             }
484             }
485            
486 1         5 $self->clear_pattern_results($p);
487 1         15 $self->add_list($p, \@newDestPatternResults);
488            
489             } # end pattern processing
490             }
491              
492              
493             sub init {
494 1     1 1 3843 my ($self, $config) = @_;
495 1         7 $self->patterns( {} );
496 1         26 $self->known_var_map( {} );
497 1         8 return $self;
498             }
499              
500              
501             =back
502              
503             =head1 COPYRIGHT
504              
505             Copyright (c) 2004-2006 IBM Corporation.
506              
507             All rights reserved. This program and the accompanying materials
508             are made available under the terms of the Eclipse Public License v1.0
509             which accompanies this distribution, and is available at
510             http://www.eclipse.org/legal/epl-v10.html
511              
512             =cut
513              
514              
515             1;
516              
517             __END__