File Coverage

blib/lib/Search/Mousse.pm
Criterion Covered Total %
statement 51 137 37.2
branch 5 32 15.6
condition 3 8 37.5
subroutine 10 17 58.8
pod 6 6 100.0
total 75 200 37.5


line stmt bran cond sub pod time code
1             package Search::Mousse;
2 2     2   2155 use strict;
  2         6  
  2         158  
3             our $VERSION = '0.32';
4 2     2   13 use base qw(Class::Accessor::Chained::Fast);
  2         4  
  2         282  
5             __PACKAGE__->mk_accessors(
6             qw(directory name stemmer key_to_id id_to_key id_to_value word_to_id
7             id_to_related and
8             )
9             );
10 2     2   2078 use CDB_File;
  2         4121  
  2         128  
11 2     2   2310 use CDB_File_Thawed;
  2         9  
  2         105  
12 2     2   1614 use List::Uniq qw(uniq);
  2         896  
  2         133  
13 2     2   2447 use Path::Class;
  2         480455  
  2         297  
14 2     2   2284 use Search::QueryParser;
  2         6512  
  2         96  
15 2     2   2264 use Set::Scalar;
  2         30305  
  2         3120  
16              
17             sub new {
18 2     2 1 47 my $class = shift;
19 2         9 my $self = {};
20 2         8 bless $self, $class;
21              
22 2         18 my %args = @_;
23 2         26 $self->directory($args{directory});
24 2         68 $self->name($args{name});
25             $self->stemmer(
26             $args{stemmer} ||
27             sub {
28 0     0   0 my $words = lc shift;
29 0         0 return uniq(split / /, $words);
30             }
31 2   100     49 );
32 2   50     41 $self->and($args{and} || 0);
33              
34 2         23 $self->_init;
35 2         132 return $self;
36             }
37              
38             sub _init {
39 2     2   7 my ($self) = @_;
40 2         8 my $name = $self->name;
41 2         22 my $dir = $self->directory;
42              
43 2         30 my $filename = file($dir, "${name}_key_to_id.cdb");
44 2 50       27107 tie my %cdb1, 'CDB_File', $filename or die "tie failed: $!\n";
45 2         344 $self->key_to_id(\%cdb1);
46              
47 2         33 $filename = file($dir, "${name}_id_to_key.cdb");
48 2 50       181 tie my %cdb2, 'CDB_File', $filename or die "tie failed: $!\n";
49 2         142 $self->id_to_key(\%cdb2);
50              
51 2         21 $filename = file($dir, "${name}_id_to_value.cdb");
52 2 50       162 tie my %cdb3, 'CDB_File_Thawed', $filename or die "tie failed: $!\n";
53 2         233 $self->id_to_value(\%cdb3);
54              
55 2         21 $filename = file($dir, "${name}_word_to_id.cdb");
56 2 50       149 tie my %cdb4, 'CDB_File_Thawed', $filename or die "tie failed: $!\n";
57 2         130 $self->word_to_id(\%cdb4);
58              
59 2         24 $filename = file($dir, "${name}_id_to_related.cdb");
60 2 50       144 if (-f $filename) {
61 0 0         tie my %cdb7, 'CDB_File_Thawed', $filename or die "tie failed: $!\n";
62 0           $self->id_to_related(\%cdb7);
63             }
64             }
65              
66             sub fetch {
67 0     0 1   my ($self, $key) = @_;
68              
69 0           my $id = $self->key_to_id->{$key};
70 0 0         return unless $id;
71 0           return $self->id_to_value->{$id};
72             }
73              
74             sub fetch_related {
75 0     0 1   my ($self, $key) = @_;
76 0           my $id_to_value = $self->id_to_value;
77            
78 0           my $id = $self->key_to_id->{$key};
79 0 0         return unless $id;
80 0   0       my $ids = $self->id_to_related->{$id} || [];
81 0           return map { $id_to_value->{$_} } @$ids;
  0            
82             }
83              
84             sub fetch_related_keys {
85 0     0 1   my ($self, $key) = @_;
86 0           my $id_to_key = $self->id_to_key;
87            
88 0           my $id = $self->key_to_id->{$key};
89 0 0         return unless $id;
90 0   0       my $ids = $self->id_to_related->{$id} || [];
91 0           return map { $id_to_key->{$_} } @$ids;
  0            
92             }
93              
94             sub search {
95 0     0 1   my ($self, $words) = @_;
96              
97 0           my @ids = $self->_search_ids($words);
98              
99 0           my @values = map { $self->id_to_value->{$_} } @ids;
  0            
100 0           return @values;
101             }
102              
103             sub search_keys {
104 0     0 1   my ($self, $words) = @_;
105 0           my @ids = $self->_search_ids($words);
106              
107 0           my @keys = map { $self->id_to_key->{$_} } @ids;
  0            
108 0           return @keys;
109             }
110              
111             sub _search_ids {
112 0     0     my ($self, $words) = @_;
113              
114 0           my $qp = Search::QueryParser->new;
115 0           my $query = $qp->parse($words);
116 0 0         return unless $query;
117              
118 0           my @union;
119 0           foreach my $term (@{$query->{""}}) {
  0            
120 0           my $value = $term->{value};
121 0           my @values = $self->stemmer->($value);
122 0           push @union, $values[0];
123             }
124            
125 0           my @plus;
126 0           foreach my $term (@{$query->{"+"}}) {
  0            
127 0           my $value = $term->{value};
128 0           my @values = $self->stemmer->($value);
129 0           push @plus, $values[0];
130             }
131            
132 0           my @minus;
133 0           foreach my $term (@{$query->{"-"}}) {
  0            
134 0           my $value = $term->{value};
135 0           my @values = $self->stemmer->($value);
136 0           push @minus, $values[0];
137             }
138            
139 0 0         if ($self->and) {
140 0           push @plus, @union;
141 0           @union = ();
142             }
143            
144 0           my $s = Set::Scalar->new;
145              
146 0 0         if (@union) {
147 0           foreach my $word (@union) {
148 0 0         next unless exists $self->word_to_id->{$word};
149 0           my @ids = @{ $self->word_to_id->{$word} };
  0            
150 0           $s->insert(@ids);
151             }
152            
153 0           foreach my $word (@plus) {
154 0 0         return unless exists $self->word_to_id->{$word};
155 0           my @ids = @{ $self->word_to_id->{$word} };
  0            
156 0           my $s2 = Set::Scalar->new(@ids);
157 0           $s = $s->intersection($s2);
158             }
159             } else {
160 0           my $word = pop @plus;
161 0           my @ids = @{ $self->word_to_id->{$word} };
  0            
162 0           $s->insert(@ids);
163              
164 0           foreach my $word (@plus) {
165 0 0         return unless exists $self->word_to_id->{$word};
166 0           my @ids = @{ $self->word_to_id->{$word} };
  0            
167 0           my $s2 = Set::Scalar->new(@ids);
168 0           $s = $s->intersection($s2);
169             }
170             }
171              
172 0           foreach my $word (@minus) {
173 0 0         next unless exists $self->word_to_id->{$word};
174 0           my @ids = @{ $self->word_to_id->{$word} };
  0            
175 0           $s = $s->delete(@ids);
176             }
177            
178 0           return $s->members;
179             }
180              
181             1;
182              
183             __END__