File Coverage

blib/lib/Dezi/Searcher.pm
Criterion Covered Total %
statement 33 59 55.9
branch 0 10 0.0
condition 0 5 0.0
subroutine 11 18 61.1
pod 5 5 100.0
total 49 97 50.5


line stmt bran cond sub pod time code
1             package Dezi::Searcher;
2 3     3   3873 use Moose;
  3         8  
  3         22  
3 3     3   21051 use MooseX::StrictConstructor;
  3         8  
  3         28  
4             with 'Dezi::Role';
5 3     3   9887 use Types::Standard qw( InstanceOf HashRef Int );
  3         9  
  3         35  
6 3     3   2813 use Dezi::Types qw( DeziInvIndexArr );
  3         8  
  3         36  
7 3     3   1949 use Dezi::Searcher::SearchOpts;
  3         8  
  3         108  
8 3     3   19 use Carp;
  3         7  
  3         250  
9 3     3   19 use Scalar::Util qw( blessed );
  3         7  
  3         170  
10 3     3   19 use Class::Load;
  3         7  
  3         119  
11 3     3   2703 use Search::Query;
  3         1094141  
  3         105  
12 3     3   30 use Search::Query::Parser;
  3         8  
  3         74  
13 3     3   17 use namespace::autoclean;
  3         8  
  3         32  
14              
15             our $VERSION = '0.014';
16              
17             has 'max_hits' => ( is => 'rw', isa => Int, default => 1000 );
18             has 'invindex' => (
19             is => 'rw',
20             isa => DeziInvIndexArr,
21             required => 1,
22             coerce => 1,
23             );
24             has 'qp_config' =>
25             ( is => 'rw', isa => HashRef, builder => 'init_qp_config', lazy => 1, );
26             has 'qp' => (
27             is => 'rw',
28             isa => InstanceOf ['Search::Query::Parser'],
29             builder => 'init_qp',
30             lazy => 1,
31             );
32             has 'property_map' => ( is => 'ro', isa => HashRef );
33              
34             =head1 NAME
35              
36             Dezi::Searcher - base searcher class
37              
38             =head1 SYNOPSIS
39              
40             my $searcher = Dezi::Searcher->new(
41             invindex => 'path/to/index',
42             max_hits => 1000,
43             );
44            
45             my $results = $searcher->search( 'foo bar' );
46             while (my $result = $results->next) {
47             printf("%4d %s\n", $result->score, $result->uri);
48             }
49              
50             =head1 DESCRIPTION
51              
52             Dezi::Searcher is a base searcher class. It defines
53             the APIs that all Dezi storage backends adhere to in
54             returning results from a Dezi::InvIndex.
55              
56             =head1 METHODS
57              
58             =head2 BUILD
59              
60             Build searcher object. Called internally by new().
61              
62             =head2 invindex
63              
64             A Dezi::InvIndex object or directory path. Required. Set in new().
65              
66             May be a single value or an array ref of values (for searching multiple
67             indexes at once).
68              
69             =head2 max_hits
70              
71             The maximum number of hits to return. Optional. Default is 1000.
72              
73             =head2 qp_config
74              
75             Optional hashref passed to Search::Query::Parser->new().
76              
77             =cut
78              
79             sub BUILD {
80 0     0 1   my $self = shift;
81              
82 0           for my $invindex ( @{ $self->{invindex} } ) {
  0            
83              
84             # make sure invindex is blessed into invindex_class
85             # and re-bless if necessary
86 0 0 0       if ( !blessed $invindex or !$invindex->isa( $self->invindex_class ) )
87             {
88 0           Class::Load::load_class( $self->invindex_class );
89 0           $invindex = $self->invindex_class->new( path => "$invindex" );
90             }
91              
92 0           $invindex->open_ro;
93             }
94              
95             # subclasses can cache however they need to. e.g. Test::Searcher
96 0           $self->_cache_property_map();
97             }
98              
99             sub _cache_property_map {
100 0     0     my $self = shift;
101              
102             # assumes same for every invindex so grab the first one.
103             $self->{property_map}
104 0           = $self->invindex->[0]->get_header->get_property_map();
105             }
106              
107             =head2 invindex_class
108              
109             Returns string 'Dezi::InvIndex'. Override this in a subclass to indicate
110             the corresponding InvIndex class for your Searcher.
111              
112             =cut
113              
114 0     0 1   sub invindex_class {'Dezi::InvIndex'}
115              
116             =head2 init_qp_config
117              
118             Returns empty hashref by default. Override this to provide
119             custom default config for the qp (L<Search::Query::Parser>).
120              
121             =cut
122              
123 0     0 1   sub init_qp_config { {} }
124              
125             =head2 init_qp
126              
127             Returns Search::Query::Parser->new( $self->init_qp_config )
128             by default. Override in a subclass to customize the L<Search::Query::Parser>
129             object.
130              
131             =cut
132              
133             sub init_qp {
134 0     0 1   my $self = shift;
135 0   0       my $qp_config = $self->qp_config || {};
136 0           return Search::Query::Parser->new(%$qp_config);
137             }
138              
139             =head2 search( I<query>, I<opts> )
140              
141             Returns a Dezi::Results object.
142              
143             I<query> should be a L<Search::Query::Dialect> object or a string parse-able
144             by L<Search::Query::Parser>.
145              
146             I<opts> should be a Dezi::Searcher::SearchOpts object or a hashref.
147              
148             =cut
149              
150             sub search {
151 0     0 1   my $self = shift;
152 0           my ( $query, $opts ) = @_;
153              
154 0           confess "$self does not implement search() method";
155             }
156              
157             sub _coerce_search_opts {
158 0     0     my $self = shift;
159 0 0         my $opts = shift or return Dezi::Searcher::SearchOpts->new();
160 0 0         if ( !blessed($opts) ) {
    0          
161 0 0         if ( ref $opts ne 'HASH' ) {
162 0           confess "opts must be a hashref";
163             }
164 0           $opts = Dezi::Searcher::SearchOpts->new($opts);
165             }
166             elsif ( !$opts->isa('Dezi::Searcher::SearchOpts') ) {
167 0           confess "opts must be a hashref or Dezi::Searcher::SearchOpts object";
168             }
169 0           return $opts;
170             }
171              
172             =head2 property_map
173              
174             Build from the InvIndex::Header, a hashref of property aliases to real names.
175             A read-only attribute propagated to the Results from search().
176              
177             =cut
178              
179             __PACKAGE__->meta->make_immutable;
180              
181             1;
182              
183             __END__
184              
185             =head1 AUTHOR
186              
187             Peter Karman, E<lt>karpet@dezi.orgE<gt>
188              
189             =head1 BUGS
190              
191             Please report any bugs or feature requests to C<bug-dezi-app at rt.cpan.org>, or through
192             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Dezi-App>.
193             I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
194              
195             =head1 SUPPORT
196              
197             You can find documentation for this module with the perldoc command.
198              
199             perldoc Dezi::Searcher
200              
201             You can also look for information at:
202              
203             =over 4
204              
205             =item * Website
206              
207             L<http://dezi.org/>
208              
209             =item * IRC
210              
211             #dezisearch at freenode
212              
213             =item * Mailing list
214              
215             L<https://groups.google.com/forum/#!forum/dezi-search>
216              
217             =item * RT: CPAN's request tracker
218              
219             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Dezi-App>
220              
221             =item * AnnoCPAN: Annotated CPAN documentation
222              
223             L<http://annocpan.org/dist/Dezi-App>
224              
225             =item * CPAN Ratings
226              
227             L<http://cpanratings.perl.org/d/Dezi-App>
228              
229             =item * Search CPAN
230              
231             L<https://metacpan.org/dist/Dezi-App/>
232              
233             =back
234              
235             =head1 COPYRIGHT AND LICENSE
236              
237             Copyright 2014 by Peter Karman
238              
239             This library is free software; you can redistribute it and/or modify
240             it under the terms of the GPL v2 or later.
241              
242             =head1 SEE ALSO
243              
244             L<http://dezi.org/>, L<http://swish-e.org/>, L<http://lucy.apache.org/>
245