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 82 2020-05-30 06:14:27Z stro $
2              
3             package CPAN::SQLite::Search;
4 5     5   138862 use strict;
  5         29  
  5         222  
5 5     5   29 use warnings;
  5         17  
  5         314  
6 5     5   30 no warnings qw(redefine);
  5         11  
  5         375  
7              
8             our $VERSION = '0.218';
9              
10 5     5   1054 use English qw/-no_match_vars/;
  5         7733  
  5         91  
11              
12 5     5   4384 use utf8;
  5         43  
  5         86  
13 5     5   1197 use CPAN::SQLite::Util qw($mode_info);
  5         20  
  5         641  
14 5     5   2847 use CPAN::SQLite::DBI::Search;
  5         14  
  5         3508  
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 3771 my ($class, %args) = @_;
25 949         4003 $cdbi_query = CPAN::SQLite::DBI::Search->new(%args);
26 949 50       2767 $max_results = $args{max_results} if $args{max_results};
27 949         5422 my $self = { results => undef, error => '', %args };
28 949         3739 return bless $self, $class;
29             }
30              
31             sub query {
32 1605     1605 1 1295140 my ($self, %args) = @_;
33 1605   50     4339 my $mode = $args{mode} || 'module';
34 1605 50       3274 unless ($mode) {
35 0         0 $self->{error} = q{Please specify a 'mode' argument};
36 0         0 return;
37             }
38 1605         3112 my $info = $mode_info->{$mode};
39 1605         2862 my $table = $info->{table};
40 1605 50       2850 unless ($table) {
41 0         0 $self->{error} = qq{No table exists for '$mode'};
42 0         0 return;
43             }
44 1605         2717 my $cdbi = $cdbi_query->{objs}->{$table};
45 1605         3244 my $class = 'CPAN::SQLite::DBI::Search::' . $table;
46 1605 50 33     7419 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         2607 my $obj;
51 1605         2535 eval { $obj = $mode2obj{$mode}->make(table => $table, cdbi => $cdbi); };
  1605         4729  
52 1605 50       3390 if ($@) {
53 0         0 $self->{error} = qq{Mode '$mode' is not known};
54 0         0 return;
55             }
56 1605         2232 my $value;
57             my $search = {
58             name => $info->{name},
59             text => $info->{text},
60             id => $info->{id},
61 1605         4665 };
62             TYPE: {
63 1605 100       2463 ($value = $args{query}) and do {
  1605         3340  
64 38         95 $search->{value} = $value;
65 38         88 $search->{type} = 'query';
66 38         87 $search->{wantarray} = 1;
67 38         100 last TYPE;
68             };
69 1567 50       2972 ($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       3315 ($value = $args{name}) and do {
76 1567         3027 $search->{value} = $value;
77 1567         2635 $search->{type} = 'name';
78 1567         2364 $search->{distinct} = 1;
79 1567         3106 last TYPE;
80             };
81 0         0 $self->{error} = q{Cannot determine the type of search};
82 0         0 return;
83             }
84              
85 1605         5492 $obj->search(search => $search, meta_obj => $self->{meta_obj});
86 1605         3662 $self->{results} = $obj->{results};
87 1605 50       3630 if (my $error = $obj->{error}) {
88 0         0 $self->{error} = $error;
89 0         0 return;
90             }
91 1605         8877 return 1;
92             }
93              
94             sub make {
95 1605     1605 0 4255 my ($class, %args) = @_;
96 1605         2828 for (qw(table cdbi)) {
97 3210 50       7117 die qq{Must supply an '$_' arg} unless defined $args{$_};
98             }
99             my $self = {
100             results => undef,
101             error => '',
102             table => $args{table},
103 1605         5282 cdbi => $args{cdbi} };
104 1605         4460 return bless $self, $class;
105             }
106              
107             package CPAN::SQLite::Search::author;
108 5     5   58 use parent 'CPAN::SQLite::Search';
  5         12  
  5         23  
109              
110             sub search {
111 26     26   86 my ($self, %args) = @_;
112 26 50       78 return unless $args{search};
113 26         60 my $cdbi = $self->{cdbi};
114 26         43 my $meta_obj = $args{meta_obj};
115 26         84 $args{fields} = [qw(auth_id cpanid fullname email)];
116 26         48 $args{table} = 'auths';
117 26 50       68 if ($max_results) {
118 0         0 $args{limit} = $max_results;
119             }
120 26         56 $args{order_by} = 'cpanid';
121 26         36 my $results;
122 26 100       144 return unless $results = (
    100          
123             $meta_obj
124             ? $cdbi->fetch_and_set(%args)
125             : $cdbi->fetch(%args));
126 13 100       45 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       46 return 1 if $meta_obj;
130              
131             # The following will get all the dists associated with the cpanid
132 7         16 $args{join} = undef;
133 7         13 $args{table} = 'dists';
134 7         22 $args{fields} = [qw(dist_file dist_abs)];
135 7         15 $args{order_by} = 'dist_file';
136 7 100       23 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
137 7         18 foreach my $item (@items) {
138             my $search = {
139             id => 'auth_id',
140             value => $item->{auth_id},
141 13         51 type => 'id',
142             wantarray => 1,
143             };
144 13         23 my $dists;
145 13 50       51 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     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   2237 use parent 'CPAN::SQLite::Search';
  5         19  
  5         25  
154              
155             sub search {
156 1473     1473   4305 my ($self, %args) = @_;
157 1473 50       3400 return unless $args{search};
158 1473         2241 my $cdbi = $self->{cdbi};
159 1473         2129 my $meta_obj = $args{meta_obj};
160              
161             $args{fields} = [
162 1473         4672 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         2541 $args{table} = 'dists';
167             $args{join} = {
168 1473         3719 mods => 'dist_id',
169             auths => 'auth_id',
170             };
171 1473         2807 $args{order_by} = 'mod_name';
172 1473 50       2961 if ($max_results) {
173 0         0 $args{limit} = $max_results;
174             }
175 1473         1892 my $results;
176 1473 100       6475 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       4090 if ($meta_obj) {
184 917         1474 my %seen;
185 917         2382 $args{join} = undef;
186 917         1498 $args{table} = 'mods';
187 917 100       2726 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
188 917         1818 foreach my $item (@items) {
189 1153         1857 my $dist_id = $item->{dist_id};
190 1153 100       2539 next if $seen{$dist_id};
191 943         2877 $args{fields} = [qw(mod_name mod_abs)];
192 943         1575 $args{order_by} = 'mod_name';
193 943         1409 $args{join} = undef;
194             my $search = {
195             id => 'dist_id',
196             value => $item->{dist_id},
197 943         3282 type => 'id',
198             wantarray => 1,
199             };
200 943         2447 $seen{$dist_id}++;
201 943         1286 my $mods;
202             next unless $mods = $cdbi->fetch_and_set(
203             %args,
204             search => $search,
205             set_list => 1,
206 943 50       3619 download => $item->{download});
207             }
208             }
209 1464 100       3326 unless ($meta_obj) {
210 547 50 66     1839 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
211             }
212 1464         4968 return 1;
213             }
214              
215             package CPAN::SQLite::Search::dist;
216 5     5   2312 use parent 'CPAN::SQLite::Search';
  5         14  
  5         24  
217              
218             sub search {
219 106     106   372 my ($self, %args) = @_;
220 106 50       281 return unless $args{search};
221 106         189 my $cdbi = $self->{cdbi};
222 106         155 my $meta_obj = $args{meta_obj};
223              
224             $args{fields} = [
225 106         340 qw(dist_id dist_name dist_abs dist_vers
226             dist_file auth_id cpanid fullname email)
227             ];
228 106         205 $args{table} = 'dists';
229 106         285 $args{join} = { auths => 'auth_id' };
230 106         192 $args{order_by} = 'dist_name';
231 106 50       232 if ($max_results) {
232 0         0 $args{limit} = $max_results;
233             }
234 106         138 my $results;
235 106 100       550 return unless $results = (
    100          
236             $meta_obj
237             ? $cdbi->fetch_and_set(%args, want_ids => 1)
238             : $cdbi->fetch(%args));
239              
240 101         356 $args{join} = undef;
241 101         195 $args{table} = 'mods';
242 101         284 $args{fields} = [qw(mod_name mod_abs)];
243 101         163 $args{order_by} = 'mod_name';
244 101 100       340 my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
245 101         220 foreach my $item (@items) {
246             my $search = {
247             id => 'dist_id',
248             value => $item->{dist_id},
249 252         908 type => 'id',
250             wantarray => 1,
251             };
252 252         373 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       1024 : $cdbi->fetch(%args, search => $search));
    50          
262 252 100       761 next if $meta_obj;
263 200 50       889 $item->{mods} = (ref($mods) eq 'ARRAY') ? $mods : [$mods];
264             }
265 101 100       266 unless ($meta_obj) {
266 95 50 66     305 $self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
267             }
268 101         303 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.218
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