File Coverage

blib/lib/Catmandu/Searchable.pm
Criterion Covered Total %
statement 16 16 100.0
branch n/a
condition n/a
subroutine 8 8 100.0
pod 0 4 0.0
total 24 28 85.7


line stmt bran cond sub pod time code
1             package Catmandu::Searchable;
2              
3 3     3   105448 use Catmandu::Sane;
  3         10  
  3         19  
4              
5             our $VERSION = '1.2020';
6              
7 3     3   24 use Catmandu::Util qw(is_natural is_positive);
  3         7  
  3         164  
8 3     3   16 use Moo::Role;
  3         7  
  3         18  
9 3     3   1156 use namespace::clean;
  3         7  
  3         17  
10              
11             with 'Catmandu::Logger';
12              
13             requires 'search';
14             requires 'searcher';
15             requires 'delete_by_query';
16              
17             has default_limit => (is => 'ro', builder => 'default_default_limit');
18             has maximum_limit => (is => 'ro', builder => 'default_maximum_limit');
19             has maximum_offset => (is => 'ro');
20              
21 6     6 0 14988 sub default_default_limit {10}
22 6     6 0 102 sub default_maximum_limit {1000}
23              
24 7     7 0 1671 sub normalize_query {$_[1]}
25              
26 5     5 0 11 sub normalize_sort {$_[1]}
27              
28             my $AROUND_SEARCH = sub {
29             my ($orig, $self, %args) = @_;
30             $args{limit} = $self->default_limit unless is_natural($args{limit});
31             $args{start} = 0 unless is_natural($args{start});
32             $args{start} += 0;
33             $args{limit} += 0;
34             if ($args{limit} > $self->maximum_limit) {
35             $args{limit} = $self->maximum_limit;
36             }
37             if (is_positive(my $page = delete $args{page})) {
38             $args{start} = ($page - 1) * $args{limit};
39             }
40              
41             $args{query} = $self->normalize_query($args{query});
42             $args{sort} = $self->normalize_sort($args{sort});
43              
44             defined $args{$_} || delete $args{$_} for keys %args;
45              
46             $self->log->debugf("called with params %s", [%args]);
47              
48             # TODO apply maximum offset more cleanly
49             if (my $max_offset = $self->maximum_offset) {
50             my $start = $args{start};
51             my $limit = $args{limit};
52             if ($start + $limit > $max_offset) {
53             $limit = ($max_offset - $start) + 1;
54             $limit = 0 if $limit < 0;
55             }
56             my $hits = $orig->($self, %args, limit => $limit);
57             $hits->{limit} = $args{limit};
58             $hits->{maximum_offset} = $max_offset;
59             return $hits;
60             }
61              
62             $orig->($self, %args);
63             };
64              
65             around search => $AROUND_SEARCH;
66             around searcher => $AROUND_SEARCH;
67              
68             around delete_by_query => sub {
69             my ($orig, $self, %args) = @_;
70              
71             $args{query} = $self->normalize_query($args{query});
72              
73             $self->log->debugf("called with params %s", [%args]);
74             $orig->($self, %args);
75             return;
76             };
77              
78             1;
79              
80             __END__
81              
82             =pod
83              
84             =head1 NAME
85              
86             Catmandu::Searchable - Optional role for searchable stores
87              
88             =head1 SYNOPSIS
89              
90             my $store = Catmandu::Store::Solr->new();
91              
92             # Return one page of search results (page size = 1000)
93             my $hits = $store->bag->search(
94             query => 'dna' ,
95             start => 0 ,
96             limit => 100 ,
97             sort => 'title desc',
98             );
99              
100             # Return all the search results as iterator
101             my $it = $store->bag->searcher(query => 'dna');
102             $it->each(sub { ...});
103              
104             $store->bag->delete_by_query(query => 'dna');
105              
106             =head1 CONFIGURATION
107              
108             =over
109              
110             =item default_limit
111              
112             The default value for C<limit>. By default this is C<10>.
113              
114             =item maximum_limit
115              
116             The maximum allowed value for C<limit>. By default this is C<1000>.
117              
118             =item maximum_offset
119              
120             The maximum allowed offset. When set no hits will be returned after hit offset
121             is greater than C<maximum_offset>, this to avoid deep paging problems.
122             Pagination values will be also be adjusted accordingly.
123              
124             =back
125              
126             =head1 METHODS
127              
128             =head2 search(query => $query, start => $start, page => $page, limit => $num, sort => $sort)
129              
130             Search the database and returns a L<Catmandu::Hits> on success. The Hits represents one
131             result page of at most $num results. The $query and $sort should implement the
132             query and sort syntax of the underlying search engine.
133              
134             Optionally provide the index of the first result using the C<start> option, or the starting page using
135             the C<page> option. The number of records in a result page can be set using the C<limit> option. Sorting
136             options are being sent verbatim to the underlying search engine.
137              
138             =head2 searcher(query => $query, start => $start, limit => $num, sort => $sort, cql_query => $cql)
139              
140             Search the database and return a L<Catmandu::Iterable> on success. This iterator can be
141             used to loop over the complete result set. The $query and $sort should implement the
142             query and sort syntax of the underlying search engine.
143              
144             Optionally provide the index of the first result using the C<start> option. The number of records in
145             a page can be set using the C<limit> option. Sorting options are being sent verbatim to the underlying
146             search engine.
147              
148             =head2 delete_by_query(query => $query)
149              
150             Delete items from the database that match $query
151              
152             =head1 CQL support
153              
154             Stores that are support the L<CQL query language|https://www.loc.gov/standards/sru/cql/> also accept the C<cql_query>
155             and C<sru_sortkeys> arguments. See L<Catmandu::CQLSearchable> for more information.
156              
157             =head1 SEE ALSO
158              
159             L<Catmandu::CQLSearchable>, L<Catmandu::Hits>, L<Catmandu::Paged>
160              
161             =cut