518 lines
13 KiB
Perl
518 lines
13 KiB
Perl
# $Id: Search.pm 84 2020-05-31 06:29:34Z stro $
|
|
|
|
package CPAN::SQLite::Search;
|
|
use strict;
|
|
use warnings;
|
|
no warnings qw(redefine);
|
|
|
|
our $VERSION = '0.219';
|
|
|
|
use English qw/-no_match_vars/;
|
|
|
|
use utf8;
|
|
use CPAN::SQLite::Util qw($mode_info);
|
|
use CPAN::SQLite::DBI::Search;
|
|
|
|
our $max_results = 0;
|
|
|
|
my $cdbi_query;
|
|
|
|
my %mode2obj;
|
|
$mode2obj{$_} = __PACKAGE__ . '::' . $_ for (qw(dist author module));
|
|
|
|
sub new {
|
|
my ($class, %args) = @_;
|
|
$cdbi_query = CPAN::SQLite::DBI::Search->new(%args);
|
|
$max_results = $args{max_results} if $args{max_results};
|
|
my $self = { results => undef, error => '', %args };
|
|
return bless $self, $class;
|
|
}
|
|
|
|
sub query {
|
|
my ($self, %args) = @_;
|
|
my $mode = $args{mode} || 'module';
|
|
unless ($mode) {
|
|
$self->{error} = q{Please specify a 'mode' argument};
|
|
return;
|
|
}
|
|
my $info = $mode_info->{$mode};
|
|
my $table = $info->{table};
|
|
unless ($table) {
|
|
$self->{error} = qq{No table exists for '$mode'};
|
|
return;
|
|
}
|
|
my $cdbi = $cdbi_query->{objs}->{$table};
|
|
my $class = 'CPAN::SQLite::DBI::Search::' . $table;
|
|
unless ($cdbi and ref($cdbi) eq $class) {
|
|
$self->{error} = qq{No cdbi object exists for '$table'};
|
|
return;
|
|
}
|
|
my $obj;
|
|
eval { $obj = $mode2obj{$mode}->make(table => $table, cdbi => $cdbi); };
|
|
if ($@) {
|
|
$self->{error} = qq{Mode '$mode' is not known};
|
|
return;
|
|
}
|
|
my $value;
|
|
my $search = {
|
|
name => $info->{name},
|
|
text => $info->{text},
|
|
id => $info->{id},
|
|
};
|
|
TYPE: {
|
|
($value = $args{query}) and do {
|
|
$search->{value} = $value;
|
|
$search->{type} = 'query';
|
|
$search->{wantarray} = 1;
|
|
last TYPE;
|
|
};
|
|
($value = $args{id}) and do {
|
|
$search->{value} = $value;
|
|
$search->{type} = 'id';
|
|
$search->{distinct} = 1;
|
|
last TYPE;
|
|
};
|
|
($value = $args{name}) and do {
|
|
$search->{value} = $value;
|
|
$search->{type} = 'name';
|
|
$search->{distinct} = 1;
|
|
last TYPE;
|
|
};
|
|
$self->{error} = q{Cannot determine the type of search};
|
|
return;
|
|
}
|
|
|
|
$obj->search(search => $search, meta_obj => $self->{meta_obj});
|
|
$self->{results} = $obj->{results};
|
|
if (my $error = $obj->{error}) {
|
|
$self->{error} = $error;
|
|
return;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub make {
|
|
my ($class, %args) = @_;
|
|
for (qw(table cdbi)) {
|
|
die qq{Must supply an '$_' arg} unless defined $args{$_};
|
|
}
|
|
my $self = {
|
|
results => undef,
|
|
error => '',
|
|
table => $args{table},
|
|
cdbi => $args{cdbi} };
|
|
return bless $self, $class;
|
|
}
|
|
|
|
package CPAN::SQLite::Search::author;
|
|
use parent 'CPAN::SQLite::Search';
|
|
|
|
sub search {
|
|
my ($self, %args) = @_;
|
|
return unless $args{search};
|
|
my $cdbi = $self->{cdbi};
|
|
my $meta_obj = $args{meta_obj};
|
|
$args{fields} = [qw(auth_id cpanid fullname email)];
|
|
$args{table} = 'auths';
|
|
if ($max_results) {
|
|
$args{limit} = $max_results;
|
|
}
|
|
$args{order_by} = 'cpanid';
|
|
my $results;
|
|
return unless $results = (
|
|
$meta_obj
|
|
? $cdbi->fetch_and_set(%args)
|
|
: $cdbi->fetch(%args));
|
|
unless ($meta_obj) {
|
|
$self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
|
|
}
|
|
return 1 if $meta_obj;
|
|
|
|
# The following will get all the dists associated with the cpanid
|
|
$args{join} = undef;
|
|
$args{table} = 'dists';
|
|
$args{fields} = [qw(dist_file dist_abs)];
|
|
$args{order_by} = 'dist_file';
|
|
my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
|
|
foreach my $item (@items) {
|
|
my $search = {
|
|
id => 'auth_id',
|
|
value => $item->{auth_id},
|
|
type => 'id',
|
|
wantarray => 1,
|
|
};
|
|
my $dists;
|
|
next unless ($dists = $cdbi->fetch(%args, search => $search));
|
|
$item->{dists} = (ref($dists) eq 'ARRAY') ? $dists : [$dists];
|
|
}
|
|
$self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
|
|
return 1;
|
|
}
|
|
|
|
package CPAN::SQLite::Search::module;
|
|
use parent 'CPAN::SQLite::Search';
|
|
|
|
sub search {
|
|
my ($self, %args) = @_;
|
|
return unless $args{search};
|
|
my $cdbi = $self->{cdbi};
|
|
my $meta_obj = $args{meta_obj};
|
|
|
|
$args{fields} = [
|
|
qw(mod_id mod_name mod_abs mod_vers
|
|
dist_id dist_name dist_file dist_vers dist_abs
|
|
auth_id cpanid fullname email)
|
|
];
|
|
$args{table} = 'dists';
|
|
$args{join} = {
|
|
mods => 'dist_id',
|
|
auths => 'auth_id',
|
|
};
|
|
$args{order_by} = 'mod_name';
|
|
if ($max_results) {
|
|
$args{limit} = $max_results;
|
|
}
|
|
my $results;
|
|
return unless $results = (
|
|
$meta_obj
|
|
? $cdbi->fetch_and_set(%args, want_ids => 1)
|
|
: $cdbi->fetch(%args));
|
|
|
|
# if running under CPAN.pm, need to build a list of modules
|
|
# contained in the distribution
|
|
if ($meta_obj) {
|
|
my %seen;
|
|
$args{join} = undef;
|
|
$args{table} = 'mods';
|
|
my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
|
|
foreach my $item (@items) {
|
|
my $dist_id = $item->{dist_id};
|
|
next if $seen{$dist_id};
|
|
$args{fields} = [qw(mod_name mod_abs)];
|
|
$args{order_by} = 'mod_name';
|
|
$args{join} = undef;
|
|
my $search = {
|
|
id => 'dist_id',
|
|
value => $item->{dist_id},
|
|
type => 'id',
|
|
wantarray => 1,
|
|
};
|
|
$seen{$dist_id}++;
|
|
my $mods;
|
|
next unless $mods = $cdbi->fetch_and_set(
|
|
%args,
|
|
search => $search,
|
|
set_list => 1,
|
|
download => $item->{download});
|
|
}
|
|
}
|
|
unless ($meta_obj) {
|
|
$self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
package CPAN::SQLite::Search::dist;
|
|
use parent 'CPAN::SQLite::Search';
|
|
|
|
sub search {
|
|
my ($self, %args) = @_;
|
|
return unless $args{search};
|
|
my $cdbi = $self->{cdbi};
|
|
my $meta_obj = $args{meta_obj};
|
|
|
|
$args{fields} = [
|
|
qw(dist_id dist_name dist_abs dist_vers
|
|
dist_file auth_id cpanid fullname email)
|
|
];
|
|
$args{table} = 'dists';
|
|
$args{join} = { auths => 'auth_id' };
|
|
$args{order_by} = 'dist_name';
|
|
if ($max_results) {
|
|
$args{limit} = $max_results;
|
|
}
|
|
my $results;
|
|
return unless $results = (
|
|
$meta_obj
|
|
? $cdbi->fetch_and_set(%args, want_ids => 1)
|
|
: $cdbi->fetch(%args));
|
|
|
|
$args{join} = undef;
|
|
$args{table} = 'mods';
|
|
$args{fields} = [qw(mod_name mod_abs)];
|
|
$args{order_by} = 'mod_name';
|
|
my @items = (ref($results) eq 'ARRAY') ? @$results : ($results);
|
|
foreach my $item (@items) {
|
|
my $search = {
|
|
id => 'dist_id',
|
|
value => $item->{dist_id},
|
|
type => 'id',
|
|
wantarray => 1,
|
|
};
|
|
my $mods;
|
|
next
|
|
unless $mods = (
|
|
$meta_obj
|
|
? $cdbi->fetch_and_set(
|
|
%args,
|
|
search => $search,
|
|
set_list => 1,
|
|
download => $item->{download})
|
|
: $cdbi->fetch(%args, search => $search));
|
|
next if $meta_obj;
|
|
$item->{mods} = (ref($mods) eq 'ARRAY') ? $mods : [$mods];
|
|
}
|
|
unless ($meta_obj) {
|
|
$self->{results} = (ref($results) eq 'ARRAY' and scalar @$results == 1) ? $results->[0] : $results;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
1;
|
|
|
|
=head1 NAME
|
|
|
|
CPAN::SQLite::Search - perform queries on the database
|
|
|
|
=head1 VERSION
|
|
|
|
version 0.219
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
my $max_results = 200;
|
|
my $query = CPAN::SQLite::Search->new(db_dir => $db_dir,
|
|
db_name => $db_name,
|
|
max_results => $max_results);
|
|
$query->query(mode => 'module', name => 'Net::FTP');
|
|
my $results = $query->{results};
|
|
|
|
=head1 CONSTRUCTING THE QUERY
|
|
|
|
This module queries the database via various types of queries
|
|
and returns the results for subsequent display. The
|
|
C<CPAN::SQLite::Search> object is created via the C<new> method as
|
|
|
|
my $query = CPAN::SQLite::Search->new(db_dir => $db_dir,
|
|
db_name => $db_name,
|
|
max_results => $max_results);
|
|
|
|
which takes as arguments
|
|
|
|
=over 3
|
|
|
|
=item * db_dir =E<gt> $db_dir
|
|
|
|
This is the directory where the database file is stored. This is
|
|
optional if the C<CPAN> option is given.
|
|
|
|
=item * CPAN =E<gt> $CPAN
|
|
|
|
This option specifies the C<cpan_home> directory of an
|
|
already configured CPAN.pm, which is where the database
|
|
file will be stored if C<db_dir> is not given.
|
|
|
|
=item * max_results =E<gt> $max_results
|
|
|
|
This is the maximum value used to limit the number of results
|
|
returned under a user query. If not specified, a value contained
|
|
within C<CPAN::SQLite::Search> will be used.
|
|
|
|
=back
|
|
|
|
A basic query then is constructed as
|
|
|
|
$query->query(mode => $mode, $type => $value);
|
|
|
|
with the results available as
|
|
|
|
my $results = $query->{results}
|
|
|
|
There are three basic modes:
|
|
|
|
=over 3
|
|
|
|
=item * module
|
|
|
|
This is for information on modules.
|
|
|
|
=item * dist
|
|
|
|
This is for information on distributions.
|
|
|
|
=item * author
|
|
|
|
This is for information on CPAN authors or cpanids.
|
|
|
|
=back
|
|
|
|
=head2 C<module>, C<dist>, and C<author> modes
|
|
|
|
For a mode of C<module>, C<dist>, and C<author>, there are
|
|
four basic options to be used for the C<$type =E<gt> $value> option:
|
|
|
|
=over 3
|
|
|
|
=item * query =E<gt> $query_term
|
|
|
|
This will search through module names,
|
|
distribution names, or CPAN author names and ids
|
|
(for C<module>, C<dist>, and C<author> modes
|
|
respectively). The results are case insensitive,
|
|
and Perl regular expressions for the C<$query_term>
|
|
are recognized.
|
|
|
|
=item * name =E<gt> $name
|
|
|
|
This will report exact matches (in a case sensitive manner)
|
|
for the module name, distribution name, or CPAN author id,
|
|
for C<module>, C<dist>, and C<author> modes
|
|
respectively.
|
|
|
|
=item * id =E<gt> $id
|
|
|
|
This will look up information on the primary key according
|
|
to the mode specified. This is more for internal use,
|
|
to help speed up queries; using this "publically" is
|
|
probably not a good idea, as the ids may change over the
|
|
course of time.
|
|
|
|
=back
|
|
|
|
=head1 RESULTS
|
|
|
|
After making the query, the results can be accessed through
|
|
|
|
my $results = $query->{results};
|
|
|
|
No results either can mean no matches were found, or
|
|
else an error in making the query resulted (in which case,
|
|
a brief error message is contained in C<$query-E<gt>{error}>).
|
|
Assuming there are results, what is returned depends on
|
|
the mode and on the type of query. See L<CPAN::SQLite::Populate>
|
|
for a description of the fields in the various tables
|
|
listed below - these fields are used as the keys of the
|
|
hash references that arise.
|
|
|
|
=head2 C<author> mode
|
|
|
|
=over 3
|
|
|
|
=item * C<name> or C<id> query
|
|
|
|
This returns the C<auth_id>, C<cpanid>, C<email>, and C<fullname>
|
|
of the C<auths> table. As well, an array reference
|
|
C<$results-E<gt>{dists}> is returned representing
|
|
all distributions associated with that C<cpanid> - each
|
|
member of the array reference is a hash reference
|
|
describing the C<dist_id>, C<dist_name>,
|
|
C<dist_abs>, C<dist_vers>, and C<dist_file> fields in the
|
|
C<dists> table. An additional entry, C<download>, is
|
|
supplied, which can be used as C<$CPAN/authors/id/$download>
|
|
to specify the url of the distribution.
|
|
|
|
=item * C<query> query
|
|
|
|
If this results in more than one match, an array reference
|
|
is returned, each member of which is a hash reference containing
|
|
the C<auth_id>, C<cpanid>, and C<fullname> fields. If there
|
|
is only one result found, a C<name> query based on the
|
|
matched C<cpanid> is performed.
|
|
|
|
=back
|
|
|
|
=head2 C<module> mode
|
|
|
|
=over 3
|
|
|
|
=item * C<name> or C<id> query
|
|
|
|
This returns the C<mod_id>, C<mod_name>, C<mod_abs>, C<mod_vers>,
|
|
C<dist_id>, C<dist_name>, C<dist_file>,
|
|
C<auth_id>, C<cpanid>, C<fullname>, and C<email>
|
|
of the C<auths>, C<mods>, and C<dists> tables.
|
|
As well, the following entries may be present.
|
|
|
|
=over 3
|
|
|
|
=item * C<download>
|
|
|
|
This can be used as C<$CPAN/authors/id/$download>
|
|
to specify the url of the distribution.
|
|
|
|
=back
|
|
|
|
=item * C<query> query
|
|
|
|
If this results in more than one match, an array reference
|
|
is returned, each member of which is a hash reference containing
|
|
the C<mod_id>, C<mod_name>, C<mod_abs>, C<mod_abs>, C<dist_vers>, C<dist_abs>,
|
|
C<auth_id>, C<cpanid>, C<dist_id>, C<dist_name>, and C<dist_file>.
|
|
As well, a C<download> field which
|
|
can be used as C<$CPAN/authors/id/$download>
|
|
to specify the url of the distribution is provided. If there
|
|
is only one result found, a C<name> query based on the
|
|
matched C<mod_name> is performed.
|
|
|
|
=back
|
|
|
|
=head2 C<dist> mode
|
|
|
|
=over 3
|
|
|
|
=item * C<name> or C<id> query
|
|
|
|
This returns the C<dist_id>, C<dist_name>, C<dist_abs>, C<dist_vers>,
|
|
C<dist_file>, C<size>, C<birth>, C<auth_id>, C<cpanid>, and C<fullname>
|
|
of the C<auths>, C<mods>, and C<dists> tables.
|
|
As well, the following entries may be present.
|
|
|
|
=over 3
|
|
|
|
=item * C<download>
|
|
|
|
This can be used as C<$CPAN/authors/id/$download>
|
|
to specify the url of the distribution.
|
|
|
|
=item * C<mods>
|
|
|
|
This is an array reference containing information on the
|
|
modules present. Each entry is a hash reference containing the
|
|
C<mod_id>, C<mod_name>, C<mod_abs>, and C<mod_vers>
|
|
fields for the module.
|
|
|
|
=back
|
|
|
|
=item * C<query> query
|
|
|
|
If this results in more than one match, an array reference
|
|
is returned, each member of which is a hash reference containing
|
|
the C<dist_id>, C<dist_name>, C<dist_abs>, C<dist_file>,
|
|
and C<cpanid> fields. As well, a C<download> field which
|
|
can be used as C<$CPAN/authors/id/$download>
|
|
to specify the url of the distribution is provided. If there
|
|
is only one result found, a C<name> query based on the
|
|
matched C<dist_name> is performed.
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<CPAN::SQLite::Populate>.
|
|
|
|
=head1 AUTHORS
|
|
|
|
Randy Kobes (passed away on September 18, 2010)
|
|
|
|
Serguei Trouchelle E<lt>stro@cpan.orgE<gt>
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright 2006,2008 by Randy Kobes E<lt>r.kobes@uwinnipeg.caE<gt>.
|
|
|
|
Copyright 2011-2013 by Serguei Trouchelle E<lt>stro@cpan.orgE<gt>.
|
|
|
|
Use and redistribution are under the same terms as Perl itself.
|
|
|
|
=cut
|