441daf32a6c4937ea50cae0d63c264033acffb1d
[koha-ffzg.git] / Koha / SearchEngine / Elasticsearch / Browse.pm
1 package Koha::SearchEngine::Elasticsearch::Browse;
2
3 # Copyright 2015 Catalyst IT
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 =head1 NAME
21
22 Koha::SearchEngine::ElasticSearch::Browse - browse functions for Elasticsearch
23
24 =head1 SYNOPSIS
25
26     my $browser =
27       Koha::SearchEngine::Elasticsearch::Browse->new( { index => 'biblios' } );
28     my $results = $browser->browse(
29         'prefi', 'title',
30         {
31             results   => '500',
32             fuzziness => 2,
33         }
34     );
35     foreach my $r (@$results) {
36         push @hits, $r->{text};
37     }
38
39 =head1 DESCRIPTION
40
41 This provides an easy interface to the "browse" functionality. Essentially,
42 it does a fast prefix search on defined fields. The fields have to be marked
43 as "suggestible" in the database when indexing takes place.
44
45 =head1 METHODS
46
47 =cut
48
49 use base qw(Koha::SearchEngine::Elasticsearch);
50 use Modern::Perl;
51
52 use Catmandu::Store::ElasticSearch;
53
54 Koha::SearchEngine::Elasticsearch::Browse->mk_accessors(qw( store ));
55
56 =head2 browse
57
58     my $results = $browser->browse($prefix, $field, \%options);
59
60 Does a prefix search for C<$prefix>, looking in C<$field>. Options are:
61
62 =over 4
63
64 =item count
65
66 The number of results to return. For Koha browse purposes, this should
67 probably be fairly high. Defaults to 500.
68
69 =item fuzziness
70
71 How much allowing for typos and misspellings is done. If 0, then it must match
72 exactly. If unspecified, it defaults to '1', which is probably the most useful.
73 Otherwise, it is a number specifying the Levenshtein edit distance relative to
74 the string length, according to the following lengths:
75
76 =over 4
77
78 =item 0..2
79
80 must match exactly
81
82 =item 3..5
83
84 C<fuzziness> edits allowed
85
86 =item >5
87
88 C<fuzziness>+1 edits allowed
89
90 =back
91
92 In all cases the maximum number of edits allowed is two (an elasticsearch
93 restriction.)
94
95 =back
96
97 =head3 Returns
98
99 This returns an arrayref of hashrefs. Each hashref contains a "text" element
100 that contains the field as returned. There may be other fields in that
101 hashref too, but they're less likely to be important.
102
103 The array will be ordered as returned from Elasticsearch, which seems to be
104 in order of some form of relevance.
105
106 =cut
107
108 sub browse {
109     my ($self, $prefix, $field, $options) = @_;
110
111     my $params = $self->get_elasticsearch_params();
112     $self->store(
113         Catmandu::Store::ElasticSearch->new(
114             %$params,
115         )
116     ) unless $self->store;
117
118     my $query = $self->_build_query($prefix, $field, $options);
119     my $results = $self->store->bag->search(%$query);
120     return $results->{suggest}{suggestions}[0]{options};
121 }
122
123 =head2 _build_query
124
125     my $query = $self->_build_query($prefix, $field, $options);
126
127 Arguments are the same as for L<browse>. This will return a query structure
128 for elasticsearch to use.
129
130 =cut
131
132 sub _build_query {
133     my ( $self, $prefix, $field, $options ) = @_;
134
135     $options = {} unless $options;
136     my $f = $options->{fuzziness} // 1;
137     my $l = length($prefix);
138     my $fuzzie;
139     if ( $l <= 2 ) {
140         $fuzzie = 0;
141     }
142     elsif ( $l <= 5 ) {
143         $fuzzie = $f;
144     }
145     else {
146         $fuzzie = $f + 1;
147     }
148     $fuzzie = 2 if $fuzzie > 2;
149
150     my $size = $options->{count} // 500;
151     my $query = {
152         # this is an annoying thing, if we set size to 0 it gets rewritten
153         # to 10. There's a bug somewhere in one of the libraries.
154         size    => 1,
155         suggest => {
156             suggestions => {
157                 text       => $prefix,
158                 completion => {
159                     field => $field . '__suggestion',
160                     size  => $size,
161                     fuzzy => {
162                         fuzziness => $fuzzie,
163                     }
164                 }
165             }
166         }
167     };
168     return $query;
169 }
170
171 1;
172
173 __END__
174
175 =head1 AUTHOR
176
177 =over 4
178
179 =item Robin Sheat << <robin@catalyst.net.nz> >>
180
181 =back
182
183 =cut