| 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; |