File Coverage

blib/lib/Catmandu/Store/ElasticSearch.pm
Criterion Covered Total %
statement 12 25 48.0
branch 0 4 0.0
condition n/a
subroutine 4 7 57.1
pod 1 2 50.0
total 17 38 44.7


line stmt bran cond sub pod time code
1             package Catmandu::Store::ElasticSearch;
2              
3 1     1   1260 use Catmandu::Sane;
  1         181771  
  1         7  
4 1     1   292 use Moo;
  1         3  
  1         6  
5 1     1   1354 use Search::Elasticsearch::Compat;
  1         24594  
  1         44  
6 1     1   789 use Catmandu::Store::ElasticSearch::Bag;
  1         4  
  1         456  
7              
8             with 'Catmandu::Store';
9              
10             =head1 NAME
11              
12             Catmandu::Store::ElasticSearch - A searchable store backed by Elasticsearch
13              
14             =head1 DEPRECIATION NOTICE
15              
16             This is the last version of L. Development will
17             continue as L using the official
18             L client.
19              
20             =head1 VERSION
21              
22             Version 0.0206
23              
24             =cut
25              
26             our $VERSION = '0.0206';
27              
28             =head1 SYNOPSIS
29              
30             use Catmandu::Store::ElasticSearch;
31              
32             my $store = Catmandu::Store::ElasticSearch->new(index_name => 'catmandu');
33              
34             my $obj1 = $store->bag->add({ name => 'Patrick' });
35              
36             printf "obj1 stored as %s\n" , $obj1->{_id};
37              
38             # Force an id in the store
39             my $obj2 = $store->bag->add({ _id => 'test123' , name => 'Nicolas' });
40              
41             # Commit all changes
42             $store->bag->commit;
43              
44             my $obj3 = $store->bag->get('test123');
45              
46             $store->bag->delete('test123');
47              
48             $store->bag->delete_all;
49              
50             # All bags are iterators
51             $store->bag->each(sub { ... });
52             $store->bag->take(10)->each(sub { ... });
53              
54             # Some stores can be searched
55             my $hits = $store->bag->search(query => 'name:Patrick');
56              
57             # Catmandu::Store::ElasticSearch supports CQL...
58             my $hits = $store->bag->search(cql_query => 'name any "Patrick"');
59              
60             =cut
61              
62             my $ELASTIC_SEARCH_ARGS = [qw(
63             transport
64             servers
65             trace_calls
66             timeout
67             max_requests
68             no_refresh
69             )];
70              
71             has index_name => (is => 'ro', required => 1);
72             has index_settings => (is => 'ro', lazy => 1, default => sub { +{} });
73             has index_mappings => (is => 'ro', lazy => 1, default => sub { +{} });
74              
75             has elastic_search => (
76             is => 'ro',
77             lazy => 1,
78             builder => '_build_elastic_search',
79             );
80              
81             sub _build_elastic_search {
82 0     0     my $self = $_[0];
83 0           my $args = delete $self->{_args};
84 0           my $es = Search::Elasticsearch::Compat->new($args);
85 0 0         unless ($es->index_exists(index => $self->index_name)) {
86 0           $es->create_index(
87             index => $self->index_name,
88             settings => $self->index_settings,
89             mappings => $self->index_mappings,
90             );
91             }
92 0           $es->use_index($self->index_name);
93 0           $es;
94             }
95              
96             sub BUILD {
97 0     0 0   my ($self, $args) = @_;
98 0           $self->{_args} = {};
99 0           for my $key (@$ELASTIC_SEARCH_ARGS) {
100 0 0         $self->{_args}{$key} = $args->{$key} if exists $args->{$key};
101             }
102             }
103              
104             sub drop {
105 0     0 1   my ($self) = @_;
106 0           $self->elastic_search->delete_index;
107             }
108              
109             =head1 METHODS
110              
111             =head2 new(index_name => $name)
112              
113             =head2 new(index_name => $name , bags => { data => { cql_mapping => \%mapping } })
114              
115             =head2 new(index_name => $name , index_mapping => $mapping)
116              
117             Create a new Catmandu::Store::ElasticSearch store connected to index $name.
118              
119             The store supports CQL searches when a cql_mapping is provided. This hash
120             contains a translation of CQL fields into Elasticsearch searchable fields.
121              
122             # Example mapping
123             $cql_mapping = {
124             title => {
125             op => {
126             'any' => 1 ,
127             'all' => 1 ,
128             '=' => 1 ,
129             '<>' => 1 ,
130             'exact' => {field => [qw(mytitle.exact myalttitle.exact)]}
131             } ,
132             sort => 1,
133             field => 'mytitle',
134             cb => ['Biblio::Search', 'normalize_title']
135             }
136             }
137              
138             The CQL mapping above will support for the 'title' field the CQL operators: any, all, =, <> and exact.
139              
140             For all the operators the 'title' field will be mapping into the Elasticsearch field 'mytitle', except
141             for the 'exact' operator. In case of 'exact' we will search both the 'mytitle.exact' and 'myalttitle.exact'
142             fields.
143              
144             The CQL mapping allows for sorting on the 'title' field. If, for instance, we would like to use a special
145             Elasticsearch field for sorting we could have written "sort => { field => 'mytitle.sort' }".
146              
147             The CQL has an optional callback field 'cb' which contains a reference to subroutines to rewrite or
148             augment the search query. In this case, in the Biblio::Search package there is a normalize_title
149             subroutine which returns a string or an ARRAY of string with augmented title(s). E.g.
150              
151             package Biblio::Search;
152              
153             sub normalize_title {
154             my ($self,$title) = @_;
155             my $new_title =~ s{[^A-Z0-9]+}{}g;
156             $new_title;
157             }
158              
159             1;
160              
161             Optionally, index_mappings contain Elastic Search schema mappings. E.g.
162              
163             # The 'data' index can ony contain one field 'title' of type 'string'
164             index_mappings => {
165             data => {
166             dynamic => 'strict',
167             properties => {
168             title => { type => 'string' }
169             }
170             }
171             }
172              
173             =head2 drop
174              
175             Deletes the elasticsearch index backing this store. Calling functions after
176             this may fail until this class is reinstantiated, creating a new index.
177              
178             =head1 ERROR HANDLING
179              
180             Error handling can be activated by specifying an error handling callback for index when creating
181             a store. E.g. to create an error handler for the bag 'data' index use:
182              
183             my $store = Catmandu::Store::ElasticSearch->new(
184             index_name => 'catmandu'
185             bags => { data => { on_error => \&error_handler } }
186             });
187              
188             sub error_handler {
189             my ($action, $document, $error, $req_no ) = @_;
190              
191             }
192              
193             See: http://search.cpan.org/~drtech/ElasticSearch-0.68/lib/ElasticSearch.pm#Error_handlers for more
194             information.
195              
196             =head1 SEE ALSO
197              
198             L
199              
200             =head1 AUTHOR
201              
202             Nicolas Steenlant, C<< >>
203              
204             =head1 CONTRIBUTORS
205              
206             =over 4
207              
208             =item Dave Sherohman, C<< dave.sherohman at ub.lu.se >>
209              
210             =item Robin Sheat, C<< robin at kallisti.net.nz >>
211              
212             =item Patrick Hochstenbach, C<< patrick.hochstenbach at ugent.be >>
213              
214             =back
215              
216             =head1 LICENSE AND COPYRIGHT
217              
218             This program is free software; you can redistribute it and/or modify it
219             under the terms of either: the GNU General Public License as published
220             by the Free Software Foundation; or the Artistic License.
221              
222             See http://dev.perl.org/licenses/ for more information.
223              
224             =cut
225              
226             1;