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   6 use Catmandu::Sane;
  1         2  
  1         6  
4 1     1   184 use Carp qw(confess);
  1         1  
  1         43  
5 1     1   384 use Catmandu::Hits;
  1         23390  
  1         28  
6 1     1   372 use Lucy::Search::ANDQuery;
  1         121  
  1         34  
7 1     1   364 use Lucy::Search::TermQuery;
  1         117  
  1         29  
8 1     1   335 use Lucy::Search::QueryParser;
  1         113  
  1         27  
9 1     1   333 use Lucy::Search::SortSpec;
  1         111  
  1         28  
10 1     1   324 use Lucy::Search::SortRule;
  1         108  
  1         26  
11 1     1   5 use Moo;
  1         2  
  1         5  
12              
13             with 'Catmandu::Bag';
14             with 'Catmandu::Searchable';
15              
16             our $VERSION = '0.0104';
17              
18             has _bag_query => (is => 'ro', lazy => 1, builder => '_build_bag_query');
19              
20 1     1   95 sub _build_bag_query { Lucy::Search::TermQuery->new(field => '_bag', term => $_[0]->name) }
21              
22             sub _searcher {
23 9     9   16 my ($self) = @_;
24             eval {
25 9         164 $self->store->_searcher;
26 9 100       13 } or do {
27 1 50       455 my $e = $@; die $e if $e !~ /index doesn't seem to contain any data/i;
  1         34  
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 306 my ($self) = @_;
52 5   100     14 my $searcher = $self->_searcher || return 0;
53 4         1855 $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 2929 my ($self, $query) = @_;
170 3 100       10 if (!defined $query) {
171 1         16 return $self->_bag_query;
172             }
173 2 50       7 if (ref $query) {
174 0         0 return Lucy::Search::ANDQuery->new(children => [
175             $self->_bag_query,
176             $query,
177             ]);
178             }
179 2         31 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   7440 my ($self, $data) = @_;
187              
188 4         8 my $flat = {};
189              
190 4         9 my @ref_stack = ($data);
191 4         6 my @key_stack;
192 4         11 while (@ref_stack) {
193 10         18 my $ref = shift @ref_stack;
194 10         11 my $key = shift @key_stack;
195              
196 10 100       22 if (ref $ref eq 'ARRAY') {
197 4         6 for my $val (@$ref) {
198 6 100       12 if (ref $val) {
    50          
199 2         4 push @key_stack, $key;
200 2         3 push @ref_stack, $val;
201             } elsif (defined $val) {
202 4         8 $flat->{$key} = $val;
203             }
204             }
205 4         7 next;
206             }
207              
208 6         16 for my $k (keys %$ref) {
209 13         18 my $val = $ref->{$k};
210 13 100       22 $k = "$key.$k" if defined $key;
211 13 100       28 if (ref $val) {
    50          
212 4         6 push @key_stack, $k;
213 4         8 push @ref_stack, $val;
214             } elsif (defined $val) {
215 9         19 $flat->{$k} = $val;
216             }
217             }
218             }
219              
220 4         17 $flat;
221             }
222              
223             =head1 SEE ALSO
224              
225             L<Catmandu::Bag>, L<Catmandu::Searchable>
226              
227             =cut
228              
229             1;