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 84 2020-05-31 06:29:34Z stro $
2              
3             package CPAN::SQLite::Search;
4 5     5   143554 use strict;
  5         36  
  5         270  
5 5     5   41 use warnings;
  5         11  
  5         365  
6 5     5   45 no warnings qw(redefine);
  5         15  
  5         465  
7              
8             our $VERSION = '0.219';
9              
10 5     5   1113 use English qw/-no_match_vars/;
  5         7697  
  5         108  
11              
12 5     5   4707 use utf8;
  5         39  
  5         93  
13 5     5   1091 use CPAN::SQLite::Util qw($mode_info);
  5         12  
  5         679  
14 5     5   2888 use CPAN::SQLite::DBI::Search;
  5         16  
  5         3582  
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 3560 my ($class, %args) = @_;
25 949         3915 $cdbi_query = CPAN::SQLite::DBI::Search->new(%args);
26 949 50       2701 $max_results = $args{max_results} if $args{max_results};
27 949         5258 my $self = { results => undef, error => '', %args };
28 949         3581 return bless $self, $class;
29             }
30              
31             sub query {
32 1605     1605 1 1317648 my ($self, %args) = @_;
33 1605   50     4388 my $mode = $args{mode} || 'module';
34 1605 50       3331 unless ($mode) {
35 0         0 $self->{error} = q{Please specify a 'mode' argument};
36 0         0 return;
37             }
38 1605         3215 my $info = $mode_info->{$mode};
39 1605         2733 my $table = $info->{table};
40 1605 50       3069 unless ($table) {
41 0         0 $self->{error} = qq{No table exists for '$mode'};
42 0         0 return;
43             }
44 1605         2866 my $cdbi = $cdbi_query->{objs}->{$table};
45 1605         3170 my $class = 'CPAN::SQLite::DBI::Search::' . $table;
46 1605 50 33     7400 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         2526 my $obj;
51 1605         2613 eval { $obj = $mode2obj{$mode}->make(table => $table, cdbi => $cdbi); };
  1605         4893  
52 1605 50       3424 if ($@) {
53 0         0 $self->{error} = qq{Mode '$mode' is not known};
54 0         0 return;
55             }
56 1605         2436 my $value;
57             my $search = {
58             name => $info->{name},
59             text => $info->{text},
60             id => $info->{id},
61 1605         4804 };
62             TYPE: {
63 1605 100       2462 ($value = $args{query}) and do {
  1605         3708  
64 38         95 $search->{value} = $value;
65 38         87 $search->{type} = 'query';
66 38         90 $search->{wantarray} = 1;
67 38         88 last TYPE;
68             };
69 1567 50       3213 ($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       3328 ($value = $args{name}) and do {
76 1567         2806 $search->{value} = $value;
77 1567         3213 $search->{type} = 'name';
78 1567         2610 $search->{distinct} = 1;
79 1567         3255 last TYPE;
80             };
81 0         0 $self->{error} = q{Cannot determine the type of search};
82 0         0 return;
83             }
84              
85 1605         5521 $obj->search(search => $search, meta_obj => $self->{meta_obj});
86 1605         3880 $self->{results} = $obj->{results};
87 1605 50       3792 if (my $error = $obj->{error}) {
88 0         0 $self->{error} = $error;
89 0         0 return;
90             }
91 1605         9515 return 1;
92             }
93              
94             sub make {
95 1605     1605 0 4281 my ($class, %args) = @_;
96 1605         3080 for (qw(table cdbi)) {
97 3210 50       7432 die qq{Must supply an '$_' arg} unless defined $args{$_};
98             }
99             my $self = {
100             results => undef,
101             error => '',
102             table => $args{table},
103 1605         5100 cdbi => $args{cdbi} };
104 1605         4397 return bless $self, $class;
105             }
106              
107             package CPAN::SQLite::Search::author;
108 5     5   53 use parent 'CPAN::SQLite::Search';
  5         15  
  5         38  
109              
110             sub search {
111 26     26   93 my ($self, %args) = @_;
112 26 50       73 return unless $args{search};
113 26         76 my $cdbi = $self->{cdbi};
114 26         40 my $meta_obj = $args{meta_obj};
115 26         88 $args{fields} = [qw(auth_id cpanid fullname email)];
116 26         53 $args{table} = 'auths';
117 26 50       63 if ($max_results) {
118 0         0 $args{limit} = $max_results;
119             }
120 26         50 $args{order_by} = 'cpanid';
121 26         37 my $results;
122 26 100       181 return unless $results = (
    100          
123             $meta_obj
124             ? $cdbi->fetch_and_set(%args)
125             : $cdbi->fetch(%args));
126 13 100       43 unless ($meta_obj) {
127 7 50 66     43 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
128             }
129 13 100       61 return 1 if $meta_obj;
130              
131             # The following will get all the dists associated with the cpanid
132 7         17 $args{join} = undef;
133 7         14 $args{table} = 'dists';
134 7         25 $args{fields} = [qw(dist_file dist_abs)];
135 7         16 $args{order_by} = 'dist_file';
136 7 100       23 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         59 type => 'id',
142             wantarray => 1,
143             };
144 13         23 my $dists;
145 13 50       52 next unless ($dists = $cdbi->fetch(%args, search => $search));
146 13 50       88 $item->{dists} = (ref($dists) eq 'ARRAY') ? $dists : [$dists];
147             }
148 7 50 66     42 $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   2137 use parent 'CPAN::SQLite::Search';
  5         18  
  5         40  
154              
155             sub search {
156 1473     1473   4311 my ($self, %args) = @_;
157 1473 50       3536 return unless $args{search};
158 1473         2413 my $cdbi = $self->{cdbi};
159 1473         2437 my $meta_obj = $args{meta_obj};
160              
161             $args{fields} = [
162 1473         4694 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         2651 $args{table} = 'dists';
167             $args{join} = {
168 1473         3546 mods => 'dist_id',
169             auths => 'auth_id',
170             };
171 1473         2575 $args{order_by} = 'mod_name';
172 1473 50       2912 if ($max_results) {
173 0         0 $args{limit} = $max_results;
174             }
175 1473         1913 my $results;
176 1473 100       6513 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       4008 if ($meta_obj) {
184 917         1434 my %seen;
185 917         2263 $args{join} = undef;
186 917         1500 $args{table} = 'mods';
187 917 100       2889 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
188 917         1964 foreach my $item (@items) {
189 1153         1793 my $dist_id = $item->{dist_id};
190 1153 100       2729 next if $seen{$dist_id};
191 943         2832 $args{fields} = [qw(mod_name mod_abs)];
192 943         1468 $args{order_by} = 'mod_name';
193 943         1416 $args{join} = undef;
194             my $search = {
195             id => 'dist_id',
196             value => $item->{dist_id},
197 943         3224 type => 'id',
198             wantarray => 1,
199             };
200 943         2453 $seen{$dist_id}++;
201 943         1344 my $mods;
202             next unless $mods = $cdbi->fetch_and_set(
203             %args,
204             search => $search,
205             set_list => 1,
206 943 50       3714 download => $item->{download});
207             }
208             }
209 1464 100       3369 unless ($meta_obj) {
210 547 50 66     1853 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
211             }
212 1464         4909 return 1;
213             }
214              
215             package CPAN::SQLite::Search::dist;
216 5     5   2278 use parent 'CPAN::SQLite::Search';
  5         22  
  5         25  
217              
218             sub search {
219 106     106   406 my ($self, %args) = @_;
220 106 50       280 return unless $args{search};
221 106         195 my $cdbi = $self->{cdbi};
222 106         274 my $meta_obj = $args{meta_obj};
223              
224             $args{fields} = [
225 106         358 qw(dist_id dist_name dist_abs dist_vers
226             dist_file auth_id cpanid fullname email)
227             ];
228 106         206 $args{table} = 'dists';
229 106         300 $args{join} = { auths => 'auth_id' };
230 106         197 $args{order_by} = 'dist_name';
231 106 50       230 if ($max_results) {
232 0         0 $args{limit} = $max_results;
233             }
234 106         149 my $results;
235 106 100       562 return unless $results = (
    100          
236             $meta_obj
237             ? $cdbi->fetch_and_set(%args, want_ids => 1)
238             : $cdbi->fetch(%args));
239              
240 101         336 $args{join} = undef;
241 101         222 $args{table} = 'mods';
242 101         304 $args{fields} = [qw(mod_name mod_abs)];
243 101         180 $args{order_by} = 'mod_name';
244 101 100       340 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
245 101         225 foreach my $item (@items) {
246             my $search = {
247             id => 'dist_id',
248             value => $item->{dist_id},
249 252         958 type => 'id',
250             wantarray => 1,
251             };
252 252         381 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       1372 : $cdbi->fetch(%args, search => $search));
    50          
262 252 100       722 next if $meta_obj;
263 200 50       893 $item->{mods} = (ref($mods) eq 'ARRAY') ? $mods : [$mods];
264             }
265 101 100       233 unless ($meta_obj) {
266 95 50 66     325 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
267             }
268 101         328 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.219
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