File Coverage

blib/lib/Catmandu/Store/Lucy/Bag.pm
Criterion Covered Total %
statement 63 77 81.8
branch 16 22 72.7
condition 2 9 22.2
subroutine 14 16 87.5
pod 0 3 0.0
total 95 127 74.8


line stmt bran cond sub pod time code
1             package Catmandu::Store::Lucy::Bag;
2              
3 1     1   3 use Catmandu::Sane;
  1         1  
  1         9  
4 1     1   180 use Carp qw(confess);
  1         1  
  1         58  
5 1     1   356 use Catmandu::Hits;
  1         19172  
  1         30  
6 1     1   370 use Lucy::Search::ANDQuery;
  1         133  
  1         58  
7 1     1   361 use Lucy::Search::TermQuery;
  1         101  
  1         27  
8 1     1   350 use Lucy::Search::QueryParser;
  1         102  
  1         41  
9 1     1   414 use Lucy::Search::SortSpec;
  1         96  
  1         27  
10 1     1   320 use Lucy::Search::SortRule;
  1         96  
  1         27  
11 1     1   6 use Moo;
  1         1  
  1         7  
12              
13             with 'Catmandu::Bag';
14             with 'Catmandu::Searchable';
15              
16             our $VERSION = '0.0103';
17              
18             has _bag_query => (is => 'ro', lazy => 1, builder => '_build_bag_query');
19              
20 1     1   100 sub _build_bag_query { Lucy::Search::TermQuery->new(field => '_bag', term => $_[0]->name) }
21              
22             sub _searcher {
23 9     9   8 my ($self) = @_;
24             eval {
25 9         169 $self->store->_searcher;
26 9 100       11 } or do {
27 1 50       428 my $e = $@; die $e if $e !~ /index doesn't seem to contain any data/i;
  1         32  
28             };
29             }
30              
31             sub generator {
32 0     0 0 0 my ($self) = @_;
33             sub {
34 0   0 0   0 state $searcher = $self->_searcher || return;
35 0         0 state $messagepack = $self->store->_messagepack;
36 0         0 state $start = 0;
37 0         0 state $limit = 100;
38 0         0 state $hits;
39              
40 0         0 my $hit;
41 0 0 0     0 unless ($hits and $hit = $hits->next) {
42 0         0 $hits = $searcher->hits(query => $self->_bag_query, num_wanted => $limit, offset => $start);
43 0         0 $start += $limit;
44 0   0     0 $hit = $hits->next || return;
45             }
46 0         0 $messagepack->unpack($hit->{_data});
47 0         0 };
48             }
49              
50             sub count {
51 5     5 0 269 my ($self) = @_;
52 5   100     12 my $searcher = $self->_searcher || return 0;
53 4         1714 $searcher->hits(
54             query => $self->_bag_query,
55             num_wanted => 0,
56             )->total_hits;
57             }
58              
59             sub get {
60             my ($self, $id) = @_;
61             my $searcher = $self->_searcher || return;
62             my $hits = $searcher->hits(
63             query => Lucy::Search::ANDQuery->new(children => [
64             Lucy::Search::TermQuery->new(field => '_id', term => $id),
65             $self->_bag_query,
66             ]),
67             num_wanted => 1,
68             );
69             $hits->total_hits || return;
70             $self->store->_messagepack->unpack($hits->next->{_data});
71             }
72              
73             sub add {
74             my ($self, $data) = @_;
75              
76             my $store = $self->store;
77             my $bag = $self->name;
78             my $data_blob = $store->_messagepack->pack($data);
79              
80             $data = $self->_flatten_data($data);
81              
82             my $type = $store->_ft_field_type;
83             my $schema = $store->_schema;
84             for my $key (keys %$data) {
85             next if $key eq '_id';
86             $schema->spec_field(name => $key, type => $type);
87             }
88              
89             $data->{_data} = $data_blob;
90             $data->{_bag} = $bag;
91             $store->_indexer->add_doc($data);
92             $data;
93             }
94              
95             sub commit {
96             my ($self) = @_;
97             $self->store->_commit;
98             }
99              
100             sub search {
101             my ($self, %args) = @_;
102              
103             my $start = delete $args{start};
104             my $limit = delete $args{limit};
105             my $sort = delete $args{sort};
106             my $bag = delete $args{reify};
107              
108             if ($sort) {
109             $args{sort_spec} = $sort;
110             }
111              
112             my $searcher = $self->_searcher || return Catmandu::Hits->new(
113             start => $start,
114             limit => $limit,
115             total => 0,
116             hits => [],
117             );
118              
119             my $lucy_hits = $searcher->hits(
120             %args,
121             num_wanted => $limit,
122             offset => $start,
123             );
124              
125             my $hits = [];
126              
127             if ($bag) {
128             while (my $hit = $lucy_hits->next) {
129             push @$hits, $bag->get($hit->{_id});
130             }
131             } else {
132             while (my $hit = $lucy_hits->next) {
133             push @$hits, $self->store->_messagepack->unpack($hit->{_data});
134             }
135             }
136              
137             Catmandu::Hits->new(
138             start => $start,
139             limit => $limit,
140             total => $lucy_hits->total_hits,
141             hits => $hits,
142             );
143             }
144              
145             sub searcher {
146             confess 'TODO';
147             }
148              
149             sub delete {
150             my ($self, $id) = @_;
151             $self->store->_indexer->delete_by_query(Lucy::Search::ANDQuery->new(children => [
152             Lucy::Search::TermQuery->new(field => '_id', term => $id),
153             $self->_bag_query,
154             ]));
155             }
156              
157             sub delete_all {
158             my ($self) = @_;
159             $self->store->_indexer->delete_by_query($self->_bag_query);
160             }
161              
162             sub delete_by_query {
163             my ($self, %args) = @_;
164             $self->store->_indexer->delete_by_query($args{query});
165              
166             }
167              
168             sub normalize_query {
169 3     3 0 2322 my ($self, $query) = @_;
170 3 100       15 if (!defined $query) {
171 1         15 return $self->_bag_query;
172             }
173 2 50       5 if (ref $query) {
174 0         0 return Lucy::Search::ANDQuery->new(children => [
175             $self->_bag_query,
176             $query,
177             ]);
178             }
179 2         42 Lucy::Search::ANDQuery->new(children => [
180             $self->_bag_query,
181             Lucy::Search::QueryParser->new(default_boolop => 'AND', schema => $self->store->_schema)->parse($query),
182             ]);
183             }
184              
185             sub _flatten_data {
186 4     4   5876 my ($self, $data) = @_;
187              
188 4         5 my $flat = {};
189              
190 4         7 my @ref_stack = ($data);
191 4         3 my @key_stack;
192 4         10 while (@ref_stack) {
193 10         10 my $ref = shift @ref_stack;
194 10         7 my $key = shift @key_stack;
195              
196 10 100       18 if (ref $ref eq 'ARRAY') {
197 4         3 for my $val (@$ref) {
198 6 100       11 if (ref $val) {
    50          
199 2         2 push @key_stack, $key;
200 2         3 push @ref_stack, $val;
201             } elsif (defined $val) {
202 4         5 $flat->{$key} = $val;
203             }
204             }
205 4         5 next;
206             }
207              
208 6         10 for my $k (keys %$ref) {
209 13         10 my $val = $ref->{$k};
210 13 100       20 $k = "$key.$k" if defined $key;
211 13 100       24 if (ref $val) {
    50          
212 4         5 push @key_stack, $k;
213 4         5 push @ref_stack, $val;
214             } elsif (defined $val) {
215 9         20 $flat->{$k} = $val;
216             }
217             }
218             }
219              
220 4         18 $flat;
221             }
222              
223             =head1 SEE ALSO
224              
225             L<Catmandu::Bag>, L<Catmandu::Searchable>
226              
227             =cut
228              
229             1;