File Coverage

blib/lib/CPAN/SQLite/Search.pm
Criterion Covered Total %
statement 150 169 88.7
branch 60 84 71.4
condition 10 17 58.8
subroutine 16 16 100.0
pod 1 3 33.3
total 237 289 82.0


line stmt bran cond sub pod time code
1             # $Id: Search.pm 85 2022-10-29 05:44:36Z stro $
2              
3             package CPAN::SQLite::Search;
4 5     5   142851 use strict;
  5         33  
  5         222  
5 5     5   31 use warnings;
  5         10  
  5         340  
6 5     5   35 no warnings qw(redefine);
  5         14  
  5         395  
7              
8             our $VERSION = '0.220';
9              
10 5     5   1085 use English qw/-no_match_vars/;
  5         7603  
  5         74  
11              
12 5     5   4337 use utf8;
  5         41  
  5         79  
13 5     5   1002 use CPAN::SQLite::Util qw($mode_info);
  5         12  
  5         592  
14 5     5   2708 use CPAN::SQLite::DBI::Search;
  5         14  
  5         3431  
15              
16             our $max_results = 0;
17              
18             my $cdbi_query;
19              
20             my %mode2obj;
21             $mode2obj{$_} = __PACKAGE__ . '::' . $_ for (qw(dist author module));
22              
23             sub new {
24 949     949 0 3509 my ($class, %args) = @_;
25 949         3707 $cdbi_query = CPAN::SQLite::DBI::Search->new(%args);
26 949 50       2720 $max_results = $args{max_results} if $args{max_results};
27 949         5362 my $self = { results => undef, error => '', %args };
28 949         3622 return bless $self, $class;
29             }
30              
31             sub query {
32 1605     1605 1 1347416 my ($self, %args) = @_;
33 1605   50     4333 my $mode = $args{mode} || 'module';
34 1605 50       3271 unless ($mode) {
35 0         0 $self->{error} = q{Please specify a 'mode' argument};
36 0         0 return;
37             }
38 1605         3104 my $info = $mode_info->{$mode};
39 1605         2827 my $table = $info->{table};
40 1605 50       2932 unless ($table) {
41 0         0 $self->{error} = qq{No table exists for '$mode'};
42 0         0 return;
43             }
44 1605         2803 my $cdbi = $cdbi_query->{objs}->{$table};
45 1605         3345 my $class = 'CPAN::SQLite::DBI::Search::' . $table;
46 1605 50 33     7145 unless ($cdbi and ref($cdbi) eq $class) {
47 0         0 $self->{error} = qq{No cdbi object exists for '$table'};
48 0         0 return;
49             }
50 1605         2339 my $obj;
51 1605         2459 eval { $obj = $mode2obj{$mode}->make(table => $table, cdbi => $cdbi); };
  1605         5141  
52 1605 50       3347 if ($@) {
53 0         0 $self->{error} = qq{Mode '$mode' is not known};
54 0         0 return;
55             }
56 1605         2193 my $value;
57             my $search = {
58             name => $info->{name},
59             text => $info->{text},
60             id => $info->{id},
61 1605         4899 };
62             TYPE: {
63 1605 100       2426 ($value = $args{query}) and do {
  1605         3374  
64 38         102 $search->{value} = $value;
65 38         86 $search->{type} = 'query';
66 38         91 $search->{wantarray} = 1;
67 38         95 last TYPE;
68             };
69 1567 50       3211 ($value = $args{id}) and do {
70 0         0 $search->{value} = $value;
71 0         0 $search->{type} = 'id';
72 0         0 $search->{distinct} = 1;
73 0         0 last TYPE;
74             };
75 1567 50       3373 ($value = $args{name}) and do {
76 1567         2904 $search->{value} = $value;
77 1567         2802 $search->{type} = 'name';
78 1567         2594 $search->{distinct} = 1;
79 1567         3307 last TYPE;
80             };
81 0         0 $self->{error} = q{Cannot determine the type of search};
82 0         0 return;
83             }
84              
85 1605         5421 $obj->search(search => $search, meta_obj => $self->{meta_obj});
86 1605         3530 $self->{results} = $obj->{results};
87 1605 50       3814 if (my $error = $obj->{error}) {
88 0         0 $self->{error} = $error;
89 0         0 return;
90             }
91 1605         8899 return 1;
92             }
93              
94             sub make {
95 1605     1605 0 4167 my ($class, %args) = @_;
96 1605         3317 for (qw(table cdbi)) {
97 3210 50       7133 die qq{Must supply an '$_' arg} unless defined $args{$_};
98             }
99             my $self = {
100             results => undef,
101             error => '',
102             table => $args{table},
103 1605         5410 cdbi => $args{cdbi} };
104 1605         4543 return bless $self, $class;
105             }
106              
107             package CPAN::SQLite::Search::author;
108 5     5   42 use parent 'CPAN::SQLite::Search';
  5         10  
  5         24  
109              
110             sub search {
111 26     26   92 my ($self, %args) = @_;
112 26 50       83 return unless $args{search};
113 26         62 my $cdbi = $self->{cdbi};
114 26         54 my $meta_obj = $args{meta_obj};
115 26         91 $args{fields} = [qw(auth_id cpanid fullname email)];
116 26         55 $args{table} = 'auths';
117 26 50       70 if ($max_results) {
118 0         0 $args{limit} = $max_results;
119             }
120 26         57 $args{order_by} = 'cpanid';
121 26         43 my $results;
122 26 100       187 return unless $results = (
    100          
123             $meta_obj
124             ? $cdbi->fetch_and_set(%args)
125             : $cdbi->fetch(%args));
126 13 100       40 unless ($meta_obj) {
127 7 50 66     59 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
128             }
129 13 100       45 return 1 if $meta_obj;
130              
131             # The following will get all the dists associated with the cpanid
132 7         15 $args{join} = undef;
133 7         18 $args{table} = 'dists';
134 7         32 $args{fields} = [qw(dist_file dist_abs)];
135 7         17 $args{order_by} = 'dist_file';
136 7 100       72 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
137 7         19 foreach my $item (@items) {
138             my $search = {
139             id => 'auth_id',
140             value => $item->{auth_id},
141 13         53 type => 'id',
142             wantarray => 1,
143             };
144 13         26 my $dists;
145 13 50       64 next unless ($dists = $cdbi->fetch(%args, search => $search));
146 13 50       75 $item->{dists} = (ref($dists) eq 'ARRAY') ? $dists : [$dists];
147             }
148 7 50 66     51 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
149 7         23 return 1;
150             }
151              
152             package CPAN::SQLite::Search::module;
153 5     5   2129 use parent 'CPAN::SQLite::Search';
  5         12  
  5         21  
154              
155             sub search {
156 1473     1473   4201 my ($self, %args) = @_;
157 1473 50       3505 return unless $args{search};
158 1473         2431 my $cdbi = $self->{cdbi};
159 1473         2391 my $meta_obj = $args{meta_obj};
160              
161             $args{fields} = [
162 1473         4817 qw(mod_id mod_name mod_abs mod_vers
163             dist_id dist_name dist_file dist_vers dist_abs
164             auth_id cpanid fullname email)
165             ];
166 1473         3005 $args{table} = 'dists';
167             $args{join} = {
168 1473         3759 mods => 'dist_id',
169             auths => 'auth_id',
170             };
171 1473         2519 $args{order_by} = 'mod_name';
172 1473 50       2989 if ($max_results) {
173 0         0 $args{limit} = $max_results;
174             }
175 1473         2094 my $results;
176 1473 100       6581 return unless $results = (
    100          
177             $meta_obj
178             ? $cdbi->fetch_and_set(%args, want_ids => 1)
179             : $cdbi->fetch(%args));
180              
181             # if running under CPAN.pm, need to build a list of modules
182             # contained in the distribution
183 1464 100       4016 if ($meta_obj) {
184 917         1249 my %seen;
185 917         2286 $args{join} = undef;
186 917         1526 $args{table} = 'mods';
187 917 100       2806 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
188 917         1909 foreach my $item (@items) {
189 1153         1957 my $dist_id = $item->{dist_id};
190 1153 100       2620 next if $seen{$dist_id};
191 943         2824 $args{fields} = [qw(mod_name mod_abs)];
192 943         1677 $args{order_by} = 'mod_name';
193 943         1401 $args{join} = undef;
194             my $search = {
195             id => 'dist_id',
196             value => $item->{dist_id},
197 943         3443 type => 'id',
198             wantarray => 1,
199             };
200 943         2408 $seen{$dist_id}++;
201 943         1338 my $mods;
202             next unless $mods = $cdbi->fetch_and_set(
203             %args,
204             search => $search,
205             set_list => 1,
206 943 50       3497 download => $item->{download});
207             }
208             }
209 1464 100       3304 unless ($meta_obj) {
210 547 50 66     1957 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
211             }
212 1464         5040 return 1;
213             }
214              
215             package CPAN::SQLite::Search::dist;
216 5     5   2225 use parent 'CPAN::SQLite::Search';
  5         12  
  5         28  
217              
218             sub search {
219 106     106   404 my ($self, %args) = @_;
220 106 50       305 return unless $args{search};
221 106         189 my $cdbi = $self->{cdbi};
222 106         191 my $meta_obj = $args{meta_obj};
223              
224             $args{fields} = [
225 106         347 qw(dist_id dist_name dist_abs dist_vers
226             dist_file auth_id cpanid fullname email)
227             ];
228 106         207 $args{table} = 'dists';
229 106         296 $args{join} = { auths => 'auth_id' };
230 106         210 $args{order_by} = 'dist_name';
231 106 50       243 if ($max_results) {
232 0         0 $args{limit} = $max_results;
233             }
234 106         174 my $results;
235 106 100       560 return unless $results = (
    100          
236             $meta_obj
237             ? $cdbi->fetch_and_set(%args, want_ids => 1)
238             : $cdbi->fetch(%args));
239              
240 101         327 $args{join} = undef;
241 101         202 $args{table} = 'mods';
242 101         298 $args{fields} = [qw(mod_name mod_abs)];
243 101         179 $args{order_by} = 'mod_name';
244 101 100       359 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
245 101         230 foreach my $item (@items) {
246             my $search = {
247             id => 'dist_id',
248             value => $item->{dist_id},
249 252         915 type => 'id',
250             wantarray => 1,
251             };
252 252         379 my $mods;
253             next
254             unless $mods = (
255             $meta_obj
256             ? $cdbi->fetch_and_set(
257             %args,
258             search => $search,
259             set_list => 1,
260             download => $item->{download})
261 252 100       1015 : $cdbi->fetch(%args, search => $search));
    50          
262 252 100       729 next if $meta_obj;
263 200 50       904 $item->{mods} = (ref($mods) eq 'ARRAY') ? $mods : [$mods];
264             }
265 101 100       275 unless ($meta_obj) {
266 95 50 66     311 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
267             }
268 101         314 return 1;
269             }
270              
271             1;
272              
273             =head1 NAME
274              
275             CPAN::SQLite::Search - perform queries on the database
276              
277             =head1 VERSION
278              
279             version 0.220
280              
281             =head1 SYNOPSIS
282              
283             my $max_results = 200;
284             my $query = CPAN::SQLite::Search->new(db_dir => $db_dir,
285             db_name => $db_name,
286             max_results => $max_results);
287             $query->query(mode => 'module', name => 'Net::FTP');
288             my $results = $query->{results};
289              
290             =head1 CONSTRUCTING THE QUERY
291              
292             This module queries the database via various types of queries
293             and returns the results for subsequent display. The
294             C object is created via the C method as
295              
296             my $query = CPAN::SQLite::Search->new(db_dir => $db_dir,
297             db_name => $db_name,
298             max_results => $max_results);
299              
300             which takes as arguments
301              
302             =over 3
303              
304             =item * db_dir =E $db_dir
305              
306             This is the directory where the database file is stored. This is
307             optional if the C option is given.
308              
309             =item * CPAN =E $CPAN
310              
311             This option specifies the C directory of an
312             already configured CPAN.pm, which is where the database
313             file will be stored if C is not given.
314              
315             =item * max_results =E $max_results
316              
317             This is the maximum value used to limit the number of results
318             returned under a user query. If not specified, a value contained
319             within C will be used.
320              
321             =back
322              
323             A basic query then is constructed as
324              
325             $query->query(mode => $mode, $type => $value);
326              
327             with the results available as
328              
329             my $results = $query->{results}
330              
331             There are three basic modes:
332              
333             =over 3
334              
335             =item * module
336              
337             This is for information on modules.
338              
339             =item * dist
340              
341             This is for information on distributions.
342              
343             =item * author
344              
345             This is for information on CPAN authors or cpanids.
346              
347             =back
348              
349             =head2 C, C, and C modes
350              
351             For a mode of C, C, and C, there are
352             four basic options to be used for the C<$type =E $value> option:
353              
354             =over 3
355              
356             =item * query =E $query_term
357              
358             This will search through module names,
359             distribution names, or CPAN author names and ids
360             (for C, C, and C modes
361             respectively). The results are case insensitive,
362             and Perl regular expressions for the C<$query_term>
363             are recognized.
364              
365             =item * name =E $name
366              
367             This will report exact matches (in a case sensitive manner)
368             for the module name, distribution name, or CPAN author id,
369             for C, C, and C modes
370             respectively.
371              
372             =item * id =E $id
373              
374             This will look up information on the primary key according
375             to the mode specified. This is more for internal use,
376             to help speed up queries; using this "publically" is
377             probably not a good idea, as the ids may change over the
378             course of time.
379              
380             =back
381              
382             =head1 RESULTS
383              
384             After making the query, the results can be accessed through
385              
386             my $results = $query->{results};
387              
388             No results either can mean no matches were found, or
389             else an error in making the query resulted (in which case,
390             a brief error message is contained in C<$query-E{error}>).
391             Assuming there are results, what is returned depends on
392             the mode and on the type of query. See L
393             for a description of the fields in the various tables
394             listed below - these fields are used as the keys of the
395             hash references that arise.
396              
397             =head2 C mode
398              
399             =over 3
400              
401             =item * C or C query
402              
403             This returns the C, C, C, and C
404             of the C table. As well, an array reference
405             C<$results-E{dists}> is returned representing
406             all distributions associated with that C - each
407             member of the array reference is a hash reference
408             describing the C, C,
409             C, C, and C fields in the
410             C table. An additional entry, C, is
411             supplied, which can be used as C<$CPAN/authors/id/$download>
412             to specify the url of the distribution.
413              
414             =item * C query
415              
416             If this results in more than one match, an array reference
417             is returned, each member of which is a hash reference containing
418             the C, C, and C fields. If there
419             is only one result found, a C query based on the
420             matched C is performed.
421              
422             =back
423              
424             =head2 C mode
425              
426             =over 3
427              
428             =item * C or C query
429              
430             This returns the C, C, C, C,
431             C, C, C,
432             C, C, C, and C
433             of the C, C, and C tables.
434             As well, the following entries may be present.
435              
436             =over 3
437              
438             =item * C
439              
440             This can be used as C<$CPAN/authors/id/$download>
441             to specify the url of the distribution.
442              
443             =back
444              
445             =item * C query
446              
447             If this results in more than one match, an array reference
448             is returned, each member of which is a hash reference containing
449             the C, C, C, C, C, C,
450             C, C, C, C, and C.
451             As well, a C field which
452             can be used as C<$CPAN/authors/id/$download>
453             to specify the url of the distribution is provided. If there
454             is only one result found, a C query based on the
455             matched C is performed.
456              
457             =back
458              
459             =head2 C mode
460              
461             =over 3
462              
463             =item * C or C query
464              
465             This returns the C, C, C, C,
466             C, C, C, C, C, and C
467             of the C, C, and C tables.
468             As well, the following entries may be present.
469              
470             =over 3
471              
472             =item * C
473              
474             This can be used as C<$CPAN/authors/id/$download>
475             to specify the url of the distribution.
476              
477             =item * C
478              
479             This is an array reference containing information on the
480             modules present. Each entry is a hash reference containing the
481             C, C, C, and C
482             fields for the module.
483              
484             =back
485              
486             =item * C query
487              
488             If this results in more than one match, an array reference
489             is returned, each member of which is a hash reference containing
490             the C, C, C, C,
491             and C fields. As well, a C field which
492             can be used as C<$CPAN/authors/id/$download>
493             to specify the url of the distribution is provided. If there
494             is only one result found, a C query based on the
495             matched C is performed.
496              
497             =back
498              
499             =head1 SEE ALSO
500              
501             L.
502              
503             =head1 AUTHORS
504              
505             Randy Kobes (passed away on September 18, 2010)
506              
507             Serguei Trouchelle Estro@cpan.orgE
508              
509             =head1 COPYRIGHT
510              
511             Copyright 2006,2008 by Randy Kobes Er.kobes@uwinnipeg.caE.
512              
513             Copyright 2011-2013 by Serguei Trouchelle Estro@cpan.orgE.
514              
515             Use and redistribution are under the same terms as Perl itself.
516              
517             =cut