View | Details | Raw Unified | Return to bug 19365
Collapse All | Expand All

(-)a/Koha/SearchEngine/Elasticsearch/Search.pm (-1 / +1 lines)
Lines 407-413 sub max_result_window { Link Here
407
    my $index_name = $self->store->index_name;
407
    my $index_name = $self->store->index_name;
408
    my $settings = $self->store->es->indices->get_settings(
408
    my $settings = $self->store->es->indices->get_settings(
409
        index  => $index_name,
409
        index  => $index_name,
410
        params => { include_defaults => 1, flat_settings => 1 },
410
        params => { include_defaults => 'true', flat_settings => 'true' },
411
    );
411
    );
412
412
413
    my $max_result_window = $settings->{$index_name}->{settings}->{'index.max_result_window'};
413
    my $max_result_window = $settings->{$index_name}->{settings}->{'index.max_result_window'};
(-)a/t/Koha/SearchEngine/Elasticsearch/Indexer.t (+78 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::More tests => 1;
21
use Test::Exception;
22
23
use t::lib::Mocks;
24
25
use Test::MockModule;
26
27
use MARC::Record;
28
use Try::Tiny;
29
30
use Koha::SearchEngine::Elasticsearch;
31
use Koha::SearchEngine::Elasticsearch::Indexer;
32
33
subtest '_sanitise_records() tests' => sub {
34
    plan tests => 5;
35
36
    my $indexer;
37
    ok(
38
        $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new({ 'index' => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX }),
39
        'Creating a new indexer object'
40
    );
41
42
    my $record1 = MARC::Record->new();
43
    $record1->append_fields(
44
        MARC::Field->new('999', '', '', 'c' => 1, 'd' => 2)
45
    );
46
47
    my $record2 = MARC::Record->new();
48
    $record2->append_fields(
49
        MARC::Field->new('999', '', '', 'c' => 3, 'd' => 4)
50
    );
51
    my $records = [$record1, $record2];
52
53
    my $biblionumbers = [5, 6];
54
55
    $indexer->_sanitise_records($biblionumbers, $records);
56
57
    is(
58
        $record1->subfield('999', 'c'),
59
        '5',
60
        'First record has 5 in 999c'
61
    );
62
    is(
63
        $record1->subfield('999', 'd'),
64
        '5',
65
        'First record has 5 in 999d'
66
    );
67
68
    is(
69
        $record2->subfield('999', 'c'),
70
        '6',
71
        'Second record has 6 in 999c'
72
    );
73
    is(
74
        $record2->subfield('999', 'd'),
75
        '6',
76
        'Second record has 6 in 999d'
77
    );
78
};
(-)a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t (-15 / +338 lines)
Lines 19-26 use Modern::Perl; Link Here
19
19
20
use C4::Context;
20
use C4::Context;
21
use Test::Exception;
21
use Test::Exception;
22
use t::lib::Mocks;
22
use t::lib::TestBuilder;
23
use t::lib::TestBuilder;
23
use Test::More tests => 3;
24
use Test::More tests => 6;
24
25
25
use Koha::Database;
26
use Koha::Database;
26
use Koha::SearchEngine::Elasticsearch::QueryBuilder;
27
use Koha::SearchEngine::Elasticsearch::QueryBuilder;
Lines 28-35 use Koha::SearchEngine::Elasticsearch::QueryBuilder; Link Here
28
my $schema = Koha::Database->new->schema;
29
my $schema = Koha::Database->new->schema;
29
$schema->storage->txn_begin;
30
$schema->storage->txn_begin;
30
31
32
my $se = Test::MockModule->new( 'Koha::SearchEngine::Elasticsearch' );
33
$se->mock( 'get_elasticsearch_mappings', sub {
34
    my ($self) = @_;
35
36
    my %all_mappings;
37
38
    my $mappings = {
39
        data => {
40
            properties => {
41
                title => {
42
                    type => 'text'
43
                },
44
                title__sort => {
45
                    type => 'text'
46
                },
47
                subject => {
48
                    type => 'text'
49
                },
50
                itemnumber => {
51
                    type => 'integer'
52
                },
53
                sortablenumber => {
54
                    type => 'integer'
55
                },
56
                sortablenumber__sort => {
57
                    type => 'integer'
58
                },
59
                Heading => {
60
                    type => 'text'
61
                },
62
                Heading__sort => {
63
                    type => 'text'
64
                }
65
            }
66
        }
67
    };
68
    $all_mappings{$self->index} = $mappings;
69
70
    my $sort_fields = {
71
        $self->index => {
72
            title => 1,
73
            subject => 0,
74
            itemnumber => 0,
75
            sortablenumber => 1,
76
            mainentry => 1
77
        }
78
    };
79
    $self->sort_fields($sort_fields->{$self->index});
80
81
    return $all_mappings{$self->index};
82
});
83
31
subtest 'build_authorities_query_compat() tests' => sub {
84
subtest 'build_authorities_query_compat() tests' => sub {
32
    plan tests => 37;
85
    plan tests => 44;
33
86
34
    my $qb;
87
    my $qb;
35
88
Lines 42-48 subtest 'build_authorities_query_compat() tests' => sub { Link Here
42
    my $search_term = 'a';
95
    my $search_term = 'a';
43
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
96
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
44
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
97
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
45
        if ( $koha_name eq 'all' ) {
98
        if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
46
            is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"},
99
            is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"},
47
                "*a*");
100
                "*a*");
48
        } else {
101
        } else {
Lines 54-60 subtest 'build_authorities_query_compat() tests' => sub { Link Here
54
    $search_term = 'Donald Duck';
107
    $search_term = 'Donald Duck';
55
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
108
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
56
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
109
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
57
        if ( $koha_name eq 'all' ) {
110
        if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
58
            is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"},
111
            is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"},
59
                "*donald*");
112
                "*donald*");
60
            is( $query->{query}->{bool}->{must}[1]->{wildcard}->{"_all.phrase"},
113
            is( $query->{query}->{bool}->{must}[1]->{wildcard}->{"_all.phrase"},
Lines 69-94 subtest 'build_authorities_query_compat() tests' => sub { Link Here
69
122
70
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
123
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
71
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['is'], [$search_term], 'AUTH_TYPE', 'asc' );
124
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['is'], [$search_term], 'AUTH_TYPE', 'asc' );
72
        if ( $koha_name eq 'all' ) {
125
        if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
73
            is( $query->{query}->{bool}->{must}[0]->{term}->{"_all.phrase"},
126
            is( $query->{query}->{bool}->{must}[0]->{match_phrase}->{"_all.phrase"},
74
                "donald duck");
127
                "donald duck");
75
        } else {
128
        } else {
76
            is( $query->{query}->{bool}->{must}[0]->{term}->{$koha_to_index_name->{$koha_name}.".phrase"},
129
            is( $query->{query}->{bool}->{must}[0]->{match_phrase}->{$koha_to_index_name->{$koha_name}.".phrase"},
77
                "donald duck");
130
                "donald duck");
78
        }
131
        }
79
    }
132
    }
80
133
81
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
134
    foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
82
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'asc' );
135
        my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'asc' );
83
        if ( $koha_name eq 'all' ) {
136
        if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
84
            is( $query->{query}->{bool}->{must}[0]->{prefix}->{"_all.lc_raw"},
137
            is( $query->{query}->{bool}->{must}[0]->{match_phrase_prefix}->{"_all.phrase"},
85
                "donald duck");
138
                "donald duck");
86
        } else {
139
        } else {
87
            is( $query->{query}->{bool}->{must}[0]->{prefix}->{$koha_to_index_name->{$koha_name}.".lc_raw"},
140
            is( $query->{query}->{bool}->{must}[0]->{match_phrase_prefix}->{$koha_to_index_name->{$koha_name}.".phrase"},
88
                "donald duck");
141
                "donald duck");
89
        }
142
        }
90
    }
143
    }
91
144
145
    # Sorting
146
    my $query = $qb->build_authorities_query_compat( [ 'mainentry' ],  undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingAsc' );
147
    is_deeply(
148
        $query->{sort},
149
        [
150
            {
151
                'Heading__sort.phrase' => 'asc'
152
            }
153
        ],
154
        "ascending sort parameter properly formed"
155
    );
156
    $query = $qb->build_authorities_query_compat( [ 'mainentry' ],  undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingDsc' );
157
    is_deeply(
158
        $query->{sort},
159
        [
160
            {
161
                'Heading__sort.phrase' => 'desc'
162
            }
163
        ],
164
        "descending sort parameter properly formed"
165
    );
166
92
    # Failing case
167
    # Failing case
93
    throws_ok {
168
    throws_ok {
94
        $qb->build_authorities_query_compat( [ 'tomas' ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
169
        $qb->build_authorities_query_compat( [ 'tomas' ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
Lines 97-118 subtest 'build_authorities_query_compat() tests' => sub { Link Here
97
        'Exception thrown on invalid value in the marclist param';
172
        'Exception thrown on invalid value in the marclist param';
98
};
173
};
99
174
175
subtest 'build_query tests' => sub {
176
    plan tests => 26;
177
178
    my $qb;
179
180
    ok(
181
        $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
182
        'Creating new query builder object for biblios'
183
    );
184
185
    my @sort_by = 'title_asc';
186
    my @sort_params = $qb->_convert_sort_fields(@sort_by);
187
    my %options;
188
    $options{sort} = \@sort_params;
189
    my $query = $qb->build_query('test', %options);
190
191
    is_deeply(
192
        $query->{sort},
193
        [
194
            {
195
            'title__sort.phrase' => {
196
                    'order' => 'asc'
197
                }
198
            }
199
        ],
200
        "sort parameter properly formed"
201
    );
202
203
    t::lib::Mocks::mock_preference('DisplayLibraryFacets','both');
204
    $query = $qb->build_query();
205
    ok( defined $query->{aggregations}{homebranch},
206
        'homebranch added to facets if DisplayLibraryFacets=both' );
207
    ok( defined $query->{aggregations}{holdingbranch},
208
        'holdingbranch added to facets if DisplayLibraryFacets=both' );
209
    t::lib::Mocks::mock_preference('DisplayLibraryFacets','holding');
210
    $query = $qb->build_query();
211
    ok( !defined $query->{aggregations}{homebranch},
212
        'homebranch not added to facets if DisplayLibraryFacets=holding' );
213
    ok( defined $query->{aggregations}{holdingbranch},
214
        'holdingbranch added to facets if DisplayLibraryFacets=holding' );
215
    t::lib::Mocks::mock_preference('DisplayLibraryFacets','home');
216
    $query = $qb->build_query();
217
    ok( defined $query->{aggregations}{homebranch},
218
        'homebranch added to facets if DisplayLibraryFacets=home' );
219
    ok( !defined $query->{aggregations}{holdingbranch},
220
        'holdingbranch not added to facets if DisplayLibraryFacets=home' );
221
222
    t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '' );
223
224
    ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] );
225
    is(
226
        $query->{query}{query_string}{query},
227
        "(donald duck)",
228
        "query not altered if QueryAutoTruncate disabled"
229
    );
230
231
    t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '1' );
232
233
    ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] );
234
    is(
235
        $query->{query}{query_string}{query},
236
        "(donald* duck*)",
237
        "simple query is auto truncated when QueryAutoTruncate enabled"
238
    );
239
240
    # Ensure reserved words are not truncated
241
    ( undef, $query ) = $qb->build_query_compat( undef,
242
        ['donald or duck and mickey not mouse'] );
243
    is(
244
        $query->{query}{query_string}{query},
245
        "(donald* or duck* and mickey* not mouse*)",
246
        "reserved words are not affected by QueryAutoTruncate"
247
    );
248
249
    ( undef, $query ) = $qb->build_query_compat( undef, ['donald* duck*'] );
250
    is(
251
        $query->{query}{query_string}{query},
252
        "(donald* duck*)",
253
        "query with '*' is unaltered when QueryAutoTruncate is enabled"
254
    );
255
256
    ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck and the mouse'] );
257
    is(
258
        $query->{query}{query_string}{query},
259
        "(donald* duck* and the* mouse*)",
260
        "individual words are all truncated and stopwords ignored"
261
    );
262
263
    ( undef, $query ) = $qb->build_query_compat( undef, ['*'] );
264
    is(
265
        $query->{query}{query_string}{query},
266
        "(*)",
267
        "query of just '*' is unaltered when QueryAutoTruncate is enabled"
268
    );
269
270
    ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck"'] );
271
    is(
272
        $query->{query}{query_string}{query},
273
        '("donald duck")',
274
        "query with quotes is unaltered when QueryAutoTruncate is enabled"
275
    );
276
277
278
    ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck" and "the mouse"'] );
279
    is(
280
        $query->{query}{query_string}{query},
281
        '("donald duck" and "the mouse")',
282
        "all quoted strings are unaltered if more than one in query"
283
    );
284
285
    ( undef, $query ) = $qb->build_query_compat( undef, ['barcode:123456'] );
286
    is(
287
        $query->{query}{query_string}{query},
288
        '(barcode:123456*)',
289
        "query of specific field is truncated"
290
    );
291
292
    ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:"123456"'] );
293
    is(
294
        $query->{query}{query_string}{query},
295
        '(Local-number:"123456")',
296
        "query of specific field including hyphen and quoted is not truncated"
297
    );
298
299
    ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:123456'] );
300
    is(
301
        $query->{query}{query_string}{query},
302
        '(Local-number:123456*)',
303
        "query of specific field including hyphen and not quoted is truncated"
304
    );
305
306
    ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:123456'] );
307
    is(
308
        $query->{query}{query_string}{query},
309
        '(Local-number.raw:123456*)',
310
        "query of specific field including period and not quoted is truncated"
311
    );
312
313
    ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:"123456"'] );
314
    is(
315
        $query->{query}{query_string}{query},
316
        '(Local-number.raw:"123456")',
317
        "query of specific field including period and quoted is not truncated"
318
    );
319
320
    ( undef, $query ) = $qb->build_query_compat( undef, ['J.R.R'] );
321
    is(
322
        $query->{query}{query_string}{query},
323
        '(J.R.R*)',
324
        "query including period is truncated but not split at periods"
325
    );
326
327
    ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'] );
328
    is(
329
        $query->{query}{query_string}{query},
330
        '(title:"donald duck")',
331
        "query of specific field is not truncated when surrouned by quotes"
332
    );
333
334
    ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 1 } );
335
    is(
336
        $query->{query}{query_string}{query},
337
        '(title:"donald duck") AND suppress:0',
338
        "query of specific field is added AND suppress:0"
339
    );
340
341
    my ($simple_query, $query_cgi);
342
    ( undef, $query, $simple_query, $query_cgi ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 0 } );
343
    is(
344
        $query->{query}{query_string}{query},
345
        '(title:"donald duck")',
346
        "query of specific field is not added AND suppress:0"
347
    );
348
    is($query_cgi, 'q=title%3A%22donald%20duck%22', 'query cgi');
349
};
350
351
100
subtest 'build query from form subtests' => sub {
352
subtest 'build query from form subtests' => sub {
101
    plan tests => 5;
353
    plan tests => 5;
102
354
103
    my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }),
355
    my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }),
104
    #when searching for authorities from a record the form returns marclist with blanks for unentered terms
356
    #when searching for authorities from a record the form returns marclist with blanks for unentered terms
105
    my @marclist = ('mainmainentry','mainentry','match', 'all');
357
    my @marclist = ('mainmainentry','mainentry','match', 'all');
106
    my @values   = ( undef,         'Hamilton',  undef,   undef);
358
    my @values   = ( undef,         'Hamilton',  undef,   undef);
107
    my @operator = ( 'contains', 'contains', 'contains', 'contains');
359
    my @operator = ( 'contains', 'contains', 'contains', 'contains');
108
360
109
    my $query = $builder->build_authorities_query_compat( \@marclist, undef,
361
    my $query = $qb->build_authorities_query_compat( \@marclist, undef,
110
                    undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
362
                    undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
111
    is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","Expected search is populated");
363
    is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","Expected search is populated");
112
    is( scalar @{ $query->{query}->{bool}->{must} }, 1,"Only defined search is populated");
364
    is( scalar @{ $query->{query}->{bool}->{must} }, 1,"Only defined search is populated");
113
365
114
    @values[2] = 'Jefferson';
366
    @values[2] = 'Jefferson';
115
    $query = $builder->build_authorities_query_compat( \@marclist, undef,
367
    $query = $qb->build_authorities_query_compat( \@marclist, undef,
116
                    undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
368
                    undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
117
    is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","First index searched as expected");
369
    is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","First index searched as expected");
118
    is($query->{query}->{bool}->{must}[1]->{wildcard}->{'Match.phrase'}, "*jefferson*","Second index searched when populated");
370
    is($query->{query}->{bool}->{must}[1]->{wildcard}->{'Match.phrase'}, "*jefferson*","Second index searched when populated");
Lines 124-130 subtest 'build query from form subtests' => sub { Link Here
124
subtest 'build_query with weighted fields tests' => sub {
376
subtest 'build_query with weighted fields tests' => sub {
125
    plan tests => 4;
377
    plan tests => 4;
126
378
127
    my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } );
379
    my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } );
128
    my $db_builder = t::lib::TestBuilder->new();
380
    my $db_builder = t::lib::TestBuilder->new();
129
381
130
    Koha::SearchFields->search({})->delete;
382
    Koha::SearchFields->search({})->delete;
Lines 156-162 subtest 'build_query with weighted fields tests' => sub { Link Here
156
        }
408
        }
157
    });
409
    });
158
410
159
    my ( undef, $query ) = $builder->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
411
    my ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
160
    undef, undef, undef, { weighted_fields => 1 });
412
    undef, undef, undef, { weighted_fields => 1 });
161
413
162
    my $fields = $query->{query}{query_string}{fields};
414
    my $fields = $query->{query}{query_string}{fields};
Lines 166-169 subtest 'build_query with weighted fields tests' => sub { Link Here
166
    is($fields->[2], 'subject^15.00', 'Third search field is subject');
418
    is($fields->[2], 'subject^15.00', 'Third search field is subject');
167
};
419
};
168
420
421
subtest "_convert_sort_fields() tests" => sub {
422
    plan tests => 3;
423
424
    my $qb;
425
426
    ok(
427
        $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
428
        'Creating new query builder object for biblios'
429
    );
430
431
    my @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_dsc ));
432
    is_deeply(
433
        \@sort_by,
434
        [
435
            { field => 'callnum', direction => 'asc' },
436
            { field => 'author',  direction => 'desc' }
437
        ],
438
        'sort fields should have been split correctly'
439
    );
440
441
    # We could expect this to pass, but direction is undef instead of 'desc'
442
    @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_desc ));
443
    is_deeply(
444
        \@sort_by,
445
        [
446
            { field => 'callnum', direction => 'asc' },
447
            { field => 'author',  direction => 'desc' }
448
        ],
449
        'sort fields should have been split correctly'
450
    );
451
};
452
453
subtest "_sort_field() tests" => sub {
454
    plan tests => 5;
455
456
    my $qb;
457
458
    ok(
459
        $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
460
        'Creating new query builder object for biblios'
461
    );
462
463
    my $f = $qb->_sort_field('title');
464
    is(
465
        $f,
466
        'title__sort.phrase',
467
        'title sort mapped correctly'
468
    );
469
470
    $f = $qb->_sort_field('subject');
471
    is(
472
        $f,
473
        'subject.raw',
474
        'subject sort mapped correctly'
475
    );
476
477
    $f = $qb->_sort_field('itemnumber');
478
    is(
479
        $f,
480
        'itemnumber',
481
        'itemnumber sort mapped correctly'
482
    );
483
484
    $f = $qb->_sort_field('sortablenumber');
485
    is(
486
        $f,
487
        'sortablenumber__sort',
488
        'sortablenumber sort mapped correctly'
489
    );
490
};
491
169
$schema->storage->txn_rollback;
492
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t (+114 lines)
Line 0 Link Here
1
# Copyright 2015 Catalyst IT
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::More tests => 11;
21
use t::lib::Mocks;
22
23
use Koha::SearchEngine::Elasticsearch::QueryBuilder;
24
use Koha::SearchEngine::Elasticsearch::Indexer;
25
26
27
my $se = Test::MockModule->new( 'Koha::SearchEngine::Elasticsearch' );
28
$se->mock( 'get_elasticsearch_mappings', sub {
29
    my ($self) = @_;
30
31
    my %all_mappings;
32
33
    my $mappings = {
34
        data => {
35
            properties => {
36
                title => {
37
                    type => 'text'
38
                },
39
                title__sort => {
40
                    type => 'text'
41
                },
42
                subject => {
43
                    type => 'text'
44
                },
45
                itemnumber => {
46
                    type => 'integer'
47
                },
48
                sortablenumber => {
49
                    type => 'integer'
50
                },
51
                sortablenumber__sort => {
52
                    type => 'integer'
53
                }
54
            }
55
        }
56
    };
57
    $all_mappings{$self->index} = $mappings;
58
59
    my $sort_fields = {
60
        $self->index => {
61
            title => 1,
62
            subject => 0,
63
            itemnumber => 0,
64
            sortablenumber => 1
65
        }
66
    };
67
    $self->sort_fields($sort_fields->{$self->index});
68
69
    return $all_mappings{$self->index};
70
});
71
72
my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } );
73
74
use_ok('Koha::SearchEngine::Elasticsearch::Search');
75
76
ok(
77
    my $searcher = Koha::SearchEngine::Elasticsearch::Search->new(
78
        { 'nodes' => ['localhost:9200'], 'index' => 'mydb' }
79
    ),
80
    'Creating a Koha::SearchEngine::Elasticsearch::Search object'
81
);
82
83
is( $searcher->index, 'mydb', 'Testing basic accessor' );
84
85
ok( my $query = $builder->build_query('easy'), 'Build a search query');
86
87
SKIP: {
88
89
    eval { $builder->get_elasticsearch_params; };
90
91
    skip 'Elasticsearch configuration not available', 8
92
        if $@;
93
94
    Koha::SearchEngine::Elasticsearch::Indexer->new({ index => 'mydb' })->drop_index;
95
    Koha::SearchEngine::Elasticsearch::Indexer->new({ index => 'mydb' })->create_index;
96
97
    ok( my $results = $searcher->search( $query) , 'Do a search ' );
98
99
    is (my $count = $searcher->count( $query ), 0 , 'Get a count of the results, without returning results ');
100
101
    ok ($results = $searcher->search_compat( $query ), 'Test search_compat' );
102
103
    ok (($results,$count) = $searcher->search_auth_compat ( $query ), 'Test search_auth_compat' );
104
105
    is ( $count = $searcher->count_auth_use($searcher,1), 0, 'Testing count_auth_use');
106
107
    is ($searcher->max_result_window, 10000, 'By default, max_result_window is 10000');
108
    $searcher->store->es->indices->put_settings(index => $searcher->store->index_name, body => {
109
        'index' => {
110
            'max_result_window' => 12000,
111
        },
112
    });
113
    is ($searcher->max_result_window, 12000, 'max_result_window returns the correct value');
114
}
(-)a/t/db_dependent/Koha_SearchEngine_Elasticsearch_Search.t (-244 lines)
Lines 1-243 Link Here
1
# Copyright 2015 Catalyst IT
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::More tests => 13;
21
use t::lib::Mocks;
22
23
use Koha::SearchEngine::Elasticsearch::QueryBuilder;
24
use Koha::SearchEngine::Elasticsearch::Indexer;
25
26
27
my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } );
28
29
use_ok('Koha::SearchEngine::Elasticsearch::Search');
30
31
ok(
32
    my $searcher = Koha::SearchEngine::Elasticsearch::Search->new(
33
        { 'nodes' => ['localhost:9200'], 'index' => 'mydb' }
34
    ),
35
    'Creating a Koha::SearchEngine::Elasticsearch::Search object'
36
);
37
38
is( $searcher->index, 'mydb', 'Testing basic accessor' );
39
40
ok( my $query = $builder->build_query('easy'), 'Build a search query');
41
42
SKIP: {
43
44
    eval { $builder->get_elasticsearch_params; };
45
46
    skip 'ElasticSeatch configuration not available', 8
47
        if $@;
48
49
    Koha::SearchEngine::Elasticsearch::Indexer->new({ index => 'mydb' })->drop_index;
50
51
    ok( my $results = $searcher->search( $query) , 'Do a search ' );
52
53
    is (my $count = $searcher->count( $query ), 0 , 'Get a count of the results, without returning results ');
54
55
    ok ($results = $searcher->search_compat( $query ), 'Test search_compat' );
56
57
    ok (($results,$count) = $searcher->search_auth_compat ( $query ), 'Test search_auth_compat' );
58
59
    is ( $count = $searcher->count_auth_use($searcher,1), 0, 'Testing count_auth_use');
60
61
    is ($searcher->max_result_window, 10000, 'By default, max_result_window is 10000');
62
    $searcher->store->es->indices->put_settings(index => $searcher->store->index_name, body => {
63
        'index' => {
64
            'max_result_window' => 12000,
65
        },
66
    });
67
    is ($searcher->max_result_window, 12000, 'max_result_window returns the correct value');
68
}
69
70
subtest 'build_query tests' => sub {
71
    plan tests => 24;
72
73
    t::lib::Mocks::mock_preference('DisplayLibraryFacets','both');
74
    my $query = $builder->build_query();
75
    ok( defined $query->{aggregations}{homebranch},
76
        'homebranch added to facets if DisplayLibraryFacets=both' );
77
    ok( defined $query->{aggregations}{holdingbranch},
78
        'holdingbranch added to facets if DisplayLibraryFacets=both' );
79
    t::lib::Mocks::mock_preference('DisplayLibraryFacets','holding');
80
    $query = $builder->build_query();
81
    ok( !defined $query->{aggregations}{homebranch},
82
        'homebranch not added to facets if DisplayLibraryFacets=holding' );
83
    ok( defined $query->{aggregations}{holdingbranch},
84
        'holdingbranch added to facets if DisplayLibraryFacets=holding' );
85
    t::lib::Mocks::mock_preference('DisplayLibraryFacets','home');
86
    $query = $builder->build_query();
87
    ok( defined $query->{aggregations}{homebranch},
88
        'homebranch added to facets if DisplayLibraryFacets=home' );
89
    ok( !defined $query->{aggregations}{holdingbranch},
90
        'holdingbranch not added to facets if DisplayLibraryFacets=home' );
91
92
    t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '' );
93
94
    ( undef, $query ) = $builder->build_query_compat( undef, ['donald duck'] );
95
    is(
96
        $query->{query}{query_string}{query},
97
        "(donald duck)",
98
        "query not altered if QueryAutoTruncate disabled"
99
    );
100
101
    t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '1' );
102
103
    ( undef, $query ) = $builder->build_query_compat( undef, ['donald duck'] );
104
    is(
105
        $query->{query}{query_string}{query},
106
        "(donald* duck*)",
107
        "simple query is auto truncated when QueryAutoTruncate enabled"
108
    );
109
110
    # Ensure reserved words are not truncated
111
    ( undef, $query ) = $builder->build_query_compat( undef,
112
        ['donald or duck and mickey not mouse'] );
113
    is(
114
        $query->{query}{query_string}{query},
115
        "(donald* or duck* and mickey* not mouse*)",
116
        "reserved words are not affected by QueryAutoTruncate"
117
    );
118
119
    ( undef, $query ) = $builder->build_query_compat( undef, ['donald* duck*'] );
120
    is(
121
        $query->{query}{query_string}{query},
122
        "(donald* duck*)",
123
        "query with '*' is unaltered when QueryAutoTruncate is enabled"
124
    );
125
126
    ( undef, $query ) = $builder->build_query_compat( undef, ['donald duck and the mouse'] );
127
    is(
128
        $query->{query}{query_string}{query},
129
        "(donald* duck* and the* mouse*)",
130
        "individual words are all truncated and stopwords ignored"
131
    );
132
133
    ( undef, $query ) = $builder->build_query_compat( undef, ['*'] );
134
    is(
135
        $query->{query}{query_string}{query},
136
        "(*)",
137
        "query of just '*' is unaltered when QueryAutoTruncate is enabled"
138
    );
139
140
    ( undef, $query ) = $builder->build_query_compat( undef, ['"donald duck"'] );
141
    is(
142
        $query->{query}{query_string}{query},
143
        '("donald duck")',
144
        "query with quotes is unaltered when QueryAutoTruncate is enabled"
145
    );
146
147
148
    ( undef, $query ) = $builder->build_query_compat( undef, ['"donald duck" and "the mouse"'] );
149
    is(
150
        $query->{query}{query_string}{query},
151
        '("donald duck" and "the mouse")',
152
        "all quoted strings are unaltered if more than one in query"
153
    );
154
155
    ( undef, $query ) = $builder->build_query_compat( undef, ['barcode:123456'] );
156
    is(
157
        $query->{query}{query_string}{query},
158
        '(barcode:123456*)',
159
        "query of specific field is truncated"
160
    );
161
162
    ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number:"123456"'] );
163
    is(
164
        $query->{query}{query_string}{query},
165
        '(Local-number:"123456")',
166
        "query of specific field including hyphen and quoted is not truncated"
167
    );
168
169
    ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number:123456'] );
170
    is(
171
        $query->{query}{query_string}{query},
172
        '(Local-number:123456*)',
173
        "query of specific field including hyphen and not quoted is truncated"
174
    );
175
176
    ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number.raw:123456'] );
177
    is(
178
        $query->{query}{query_string}{query},
179
        '(Local-number.raw:123456*)',
180
        "query of specific field including period and not quoted is truncated"
181
    );
182
183
    ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number.raw:"123456"'] );
184
    is(
185
        $query->{query}{query_string}{query},
186
        '(Local-number.raw:"123456")',
187
        "query of specific field including period and quoted is not truncated"
188
    );
189
190
    ( undef, $query ) = $builder->build_query_compat( undef, ['J.R.R'] );
191
    is(
192
        $query->{query}{query_string}{query},
193
        '(J.R.R*)',
194
        "query including period is truncated but not split at periods"
195
    );
196
197
    ( undef, $query ) = $builder->build_query_compat( undef, ['title:"donald duck"'] );
198
    is(
199
        $query->{query}{query_string}{query},
200
        '(title:"donald duck")',
201
        "query of specific field is not truncated when surrouned by quotes"
202
    );
203
204
    ( undef, $query ) = $builder->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 1 } );
205
    is(
206
        $query->{query}{query_string}{query},
207
        '(title:"donald duck") AND suppress:0',
208
        "query of specific field is added AND suppress:0"
209
    );
210
211
    my ($simple_query, $query_cgi);
212
    ( undef, $query, $simple_query, $query_cgi ) = $builder->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 0 } );
213
    is(
214
        $query->{query}{query_string}{query},
215
        '(title:"donald duck")',
216
        "query of specific field is not added AND suppress:0"
217
    );
218
    is($query_cgi, 'q=title%3A%22donald%20duck%22', 'query cgi');
219
};
220
221
subtest "_convert_sort_fields" => sub {
222
    plan tests => 2;
223
    my @sort_by = $builder->_convert_sort_fields(qw( call_number_asc author_dsc ));
224
    is_deeply(
225
        \@sort_by,
226
        [
227
            { field => 'callnum', direction => 'asc' },
228
            { field => 'author',  direction => 'desc' }
229
        ],
230
        'sort fields should have been split correctly'
231
    );
232
233
    # We could expect this to pass, but direction is undef instead of 'desc'
234
    @sort_by = $builder->_convert_sort_fields(qw( call_number_asc author_desc ));
235
    is_deeply(
236
        \@sort_by,
237
        [
238
            { field => 'callnum', direction => 'asc' },
239
            { field => 'author',  direction => 'desc' }
240
        ],
241
        'sort fields should have been split correctly'
242
    );
243
};
244
- 

Return to bug 19365