3 # This file is part of Koha.
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.
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.
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>.
20 use Test::More tests => 7;
24 use t::lib::TestBuilder;
30 use List::Util qw( any );
32 use C4::AuthoritiesMarc qw( AddAuthority );
34 use Koha::SearchEngine::Elasticsearch;
35 use Koha::SearchEngine::Elasticsearch::Search;
37 my $schema = Koha::Database->new->schema;
38 $schema->storage->txn_begin;
40 subtest '_read_configuration() tests' => sub {
45 t::lib::Mocks::mock_config( 'elasticsearch', undef );
47 # 'elasticsearch' missing in configuration
49 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
51 'Koha::Exceptions::Config::MissingEntry',
52 'Configuration problem, exception thrown';
55 "Missing <elasticsearch> entry in koha-conf.xml",
56 'Exception message is correct'
59 # 'elasticsearch' present but no 'server' entry
60 t::lib::Mocks::mock_config( 'elasticsearch', {} );
62 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
64 'Koha::Exceptions::Config::MissingEntry',
65 'Configuration problem, exception thrown';
68 "Missing <elasticsearch>/<server> entry in koha-conf.xml",
69 'Exception message is correct'
72 # 'elasticsearch' and 'server' entries present, but no 'index_name'
73 t::lib::Mocks::mock_config( 'elasticsearch', { server => 'a_server' } );
75 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
77 'Koha::Exceptions::Config::MissingEntry',
78 'Configuration problem, exception thrown';
81 "Missing <elasticsearch>/<index_name> entry in koha-conf.xml",
82 'Exception message is correct'
85 # Correct configuration, only one server
86 t::lib::Mocks::mock_config( 'elasticsearch', { server => 'a_server', index_name => 'index' } );
88 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
89 is( $configuration->{index_name}, 'index', 'Index configuration parsed correctly' );
90 is_deeply( $configuration->{nodes}, ['a_server'], 'Server configuration parsed correctly' );
92 # Correct configuration, two servers
93 my @servers = ('a_server', 'another_server');
94 t::lib::Mocks::mock_config( 'elasticsearch', { server => \@servers, index_name => 'index' } );
96 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
97 is( $configuration->{index_name}, 'index', 'Index configuration parsed correctly' );
98 is( $configuration->{cxn_pool}, 'Static', 'cxn_pool configuration set correctly to Static if not specified' );
99 is_deeply( $configuration->{nodes}, \@servers , 'Server configuration parsed correctly' );
101 t::lib::Mocks::mock_config( 'elasticsearch', { server => \@servers, index_name => 'index', cxn_pool => 'Sniff' } );
103 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
104 is( $configuration->{cxn_pool}, 'Sniff', 'cxn_pool configuration parsed correctly' );
105 isnt( defined $configuration->{trace_to}, 'trace_to is not defined if not set' );
107 my $params = Koha::SearchEngine::Elasticsearch::get_elasticsearch_params;
108 is_deeply( $configuration->{nodes}, \@servers , 'get_elasticsearch_params is just a wrapper for _read_configuration' );
110 t::lib::Mocks::mock_config( 'elasticsearch', { server => \@servers, index_name => 'index', cxn_pool => 'Sniff', trace_to => 'Stderr', request_timeout => 42 } );
112 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
113 is( $configuration->{trace_to}, 'Stderr', 'trace_to configuration parsed correctly' );
114 is( $configuration->{request_timeout}, '42', 'additional configuration (request_timeout) parsed correctly' );
117 subtest 'get_elasticsearch_settings() tests' => sub {
123 # test reading index settings
124 my $es = Koha::SearchEngine::Elasticsearch->new( {index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX} );
125 $settings = $es->get_elasticsearch_settings();
126 is( $settings->{index}{analysis}{analyzer}{analyzer_phrase}{tokenizer}, 'keyword', 'Index settings parsed correctly' );
129 subtest 'get_elasticsearch_mappings() tests' => sub {
138 type => 'callnumber',
143 marc_type => 'marc21',
153 marc_type => 'marc21',
154 marc_field => '020a',
157 my $search_engine_module = Test::MockModule->new('Koha::SearchEngine::Elasticsearch');
158 $search_engine_module->mock('_foreach_mapping', sub {
159 my ($self, $sub) = @_;
161 foreach my $map (@mappings) {
175 my $search_engine_elasticsearch = Koha::SearchEngine::Elasticsearch::Search->new({ index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX });
176 $mappings = $search_engine_elasticsearch->get_elasticsearch_mappings();
178 is( $mappings->{properties}{"cn-sort__sort"}{index}, 'false', 'Field mappings parsed correctly for sort for callnumber type' );
179 is( $mappings->{properties}{"cn-sort__sort"}{numeric}, 'false', 'Field mappings parsed correctly for sort for callnumber type' );
180 is( $mappings->{properties}{isbn__sort}{index}, 'false', 'Field mappings parsed correctly' );
184 subtest 'Koha::SearchEngine::Elasticsearch::marc_records_to_documents () tests' => sub {
188 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
189 t::lib::Mocks::mock_preference('ElasticsearchMARCFormat', 'ISO2709');
193 name => 'control_number',
199 marc_type => 'marc21',
209 marc_type => 'marc21',
210 marc_field => '020a',
219 marc_type => 'marc21',
220 marc_field => '100a',
229 marc_type => 'marc21',
230 marc_field => '110a',
239 marc_type => 'marc21',
240 marc_field => '245(ab)ab',
243 name => 'unimarc_title',
249 marc_type => 'unimarc',
250 marc_field => '245a',
256 suggestible => undef,
259 marc_type => 'marc21',
263 name => 'uniform_title',
269 marc_type => 'marc21',
270 marc_field => '240a',
273 name => 'title_wildcard',
279 marc_type => 'marc21',
283 name => 'sum_item_price',
289 marc_type => 'marc21',
290 marc_field => '952g',
293 name => 'items_withdrawn_status',
299 marc_type => 'marc21',
300 marc_field => '9520',
303 name => 'local_classification',
309 marc_type => 'marc21',
310 marc_field => '952o',
313 name => 'type_of_record',
319 marc_type => 'marc21',
320 marc_field => 'leader_/6',
323 name => 'type_of_record_and_bib_level',
329 marc_type => 'marc21',
330 marc_field => 'leader_/6-7',
339 marc_type => 'marc21',
340 marc_field => '007_/0',
349 marc_type => 'marc21',
350 marc_field => '952l',
359 marc_type => 'marc21',
360 marc_field => '260c',
363 name => 'date-of-publication',
369 marc_type => 'marc21',
370 marc_field => '008_/7-10',
379 marc_type => 'marc21',
380 marc_field => '650(avxyz)',
384 my $se = Test::MockModule->new('Koha::SearchEngine::Elasticsearch');
385 $se->mock('_foreach_mapping', sub {
386 my ($self, $sub) = @_;
388 foreach my $map (@mappings) {
402 my $see = Koha::SearchEngine::Elasticsearch::Search->new({ index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX });
404 my $callno = 'ABC123';
405 my $callno2 = 'ABC456';
406 my $long_callno = '1234567890' x 30;
408 my $marc_record_1 = MARC::Record->new();
409 $marc_record_1->leader(' cam 22 a 4500');
410 $marc_record_1->append_fields(
411 MARC::Field->new('001', '123'),
412 MARC::Field->new('007', 'ku'),
413 MARC::Field->new('008', '901111s1962 xxk|||| |00| ||eng c'),
414 MARC::Field->new('020', '', '', a => '1-56619-909-3'),
415 MARC::Field->new('100', '', '', a => 'Author 1'),
416 MARC::Field->new('110', '', '', a => 'Corp Author'),
417 MARC::Field->new('210', '', '', a => 'Title 1'),
418 MARC::Field->new('240', '', '4', a => 'The uniform title with nonfiling indicator'),
419 MARC::Field->new('245', '', '', a => 'Title:', b => 'first record'),
420 MARC::Field->new('260', '', '', a => 'New York :', b => 'Ace ,', c => 'c1962'),
421 MARC::Field->new('650', '', '', a => 'Heading', z => 'Geohead', v => 'Formhead'),
422 MARC::Field->new('650', '', '', a => 'Heading', x => 'Gensubhead', z => 'Geohead'),
423 MARC::Field->new('999', '', '', c => '1234567'),
424 # ' ' for testing trimming of white space in boolean value callback:
425 MARC::Field->new('952', '', '', 0 => ' ', g => '123.30', o => $callno, l => 3),
426 MARC::Field->new('952', '', '', 0 => 0, g => '127.20', o => $callno2, l => 2),
427 MARC::Field->new('952', '', '', 0 => 1, g => '0.00', o => $long_callno, l => 1),
429 my $marc_record_2 = MARC::Record->new();
430 $marc_record_2->leader(' cam 22 a 4500');
431 $marc_record_2->append_fields(
432 MARC::Field->new('008', '901111s19uu xxk|||| |00| ||eng c'),
433 MARC::Field->new('100', '', '', a => 'Author 2'),
434 # MARC::Field->new('210', '', '', a => 'Title 2'),
435 # MARC::Field->new('245', '', '', a => 'Title: second record'),
436 MARC::Field->new('260', '', '', a => 'New York :', b => 'Ace ,', c => '1963-2003'),
437 MARC::Field->new('999', '', '', c => '1234568'),
438 MARC::Field->new('952', '', '', 0 => 1, g => 'string where should be numeric', o => $long_callno),
441 my $marc_record_3 = MARC::Record->new();
442 $marc_record_3->leader(' cam 22 a 4500');
443 $marc_record_3->append_fields(
444 MARC::Field->new('008', '901111s19uu xxk|||| |00| ||eng c'),
445 MARC::Field->new('100', '', '', a => 'Author 2'),
446 # MARC::Field->new('210', '', '', a => 'Title 3'),
447 # MARC::Field->new('245', '', '', a => 'Title: third record'),
448 MARC::Field->new('260', '', '', a => 'New York :', b => 'Ace ,', c => ' 89 '),
449 MARC::Field->new('999', '', '', c => '1234568'),
450 MARC::Field->new('952', '', '', 0 => 1, g => 'string where should be numeric', o => $long_callno),
452 my $records = [$marc_record_1, $marc_record_2, $marc_record_3];
454 $see->get_elasticsearch_mappings(); #sort_fields will call this and use the actual db values unless we call it first
456 my $docs = $see->marc_records_to_documents($records);
459 is(scalar @{$docs}, 3, 'Two records converted to documents');
461 is_deeply($docs->[0]->{control_number}, ['123'], 'First record control number should be set correctly');
463 is_deeply($docs->[0]->{'ff7-00'}, ['k'], 'First record ff7-00 should be set correctly');
465 is(scalar @{$docs->[0]->{author}}, 2, 'First document author field should contain two values');
466 is_deeply($docs->[0]->{author}, ['Author 1', 'Corp Author'], 'First document author field should be set correctly');
468 is(scalar @{$docs->[0]->{subject}}, 2, 'First document subject field should contain two values');
469 is_deeply($docs->[0]->{subject}, ['Heading Geohead Formhead', 'Heading Gensubhead Geohead'], 'First document asubject field should be set correctly, record order preserved for grouped subfield mapping');
471 is(scalar @{$docs->[0]->{author__sort}}, 1, 'First document author__sort field should have a single value');
472 is_deeply($docs->[0]->{author__sort}, ['Author 1 Corp Author'], 'First document author__sort field should be set correctly');
474 is(scalar @{$docs->[0]->{title__sort}}, 1, 'First document title__sort field should have a single');
475 is_deeply($docs->[0]->{title__sort}, ['Title: first record Title: first record'], 'First document title__sort field should be set correctly');
477 is($docs->[0]->{issues}, 6, 'Issues field should be sum of the issues for each item');
478 is($docs->[0]->{issues__sort}, 6, 'Issues sort field should also be a sum of the issues');
480 is(scalar @{$docs->[0]->{title_wildcard}}, 2, 'First document title_wildcard field should have two values');
481 is_deeply($docs->[0]->{title_wildcard}, ['Title:', 'first record'], 'First document title_wildcard field should be set correctly');
484 is(scalar @{$docs->[0]->{author__suggestion}}, 2, 'First document author__suggestion field should contain two values');
486 $docs->[0]->{author__suggestion},
489 'input' => 'Author 1'
492 'input' => 'Corp Author'
495 'First document author__suggestion field should be set correctly'
498 is(scalar @{$docs->[0]->{title__suggestion}}, 3, 'First document title__suggestion field should contain three values');
500 $docs->[0]->{title__suggestion},
502 { 'input' => 'Title:' },
503 { 'input' => 'first record' },
504 { 'input' => 'Title: first record' }
506 'First document title__suggestion field should be set correctly'
509 ok(!(defined $docs->[0]->{title__facet}), 'First document should have no title__facet field');
511 is(scalar @{$docs->[0]->{author__facet}}, 2, 'First document author__facet field should have two values');
513 $docs->[0]->{author__facet},
514 ['Author 1', 'Corp Author'],
515 'First document author__facet field should be set correctly'
518 is(scalar @{$docs->[0]->{items_withdrawn_status}}, 2, 'First document items_withdrawn_status field should have two values');
520 $docs->[0]->{items_withdrawn_status},
522 'First document items_withdrawn_status field should be set correctly'
526 $docs->[0]->{sum_item_price},
528 'First document sum_item_price field should be set correctly'
531 ok(defined $docs->[0]->{marc_data}, 'First document marc_data field should be set');
532 ok(defined $docs->[0]->{marc_format}, 'First document marc_format field should be set');
533 is($docs->[0]->{marc_format}, 'base64ISO2709', 'First document marc_format should be set correctly');
535 my $decoded_marc_record = $see->decode_record_from_result($docs->[0]);
537 ok($decoded_marc_record->isa('MARC::Record'), "base64ISO2709 record successfully decoded from result");
538 is($decoded_marc_record->as_usmarc(), $marc_record_1->as_usmarc(), "Decoded base64ISO2709 record has same data as original record");
540 is(scalar @{$docs->[0]->{type_of_record}}, 1, 'First document type_of_record field should have one value');
542 $docs->[0]->{type_of_record},
544 'First document type_of_record field should be set correctly'
547 is(scalar @{$docs->[0]->{type_of_record_and_bib_level}}, 1, 'First document type_of_record_and_bib_level field should have one value');
549 $docs->[0]->{type_of_record_and_bib_level},
551 'First document type_of_record_and_bib_level field should be set correctly'
554 is(scalar @{$docs->[0]->{isbn}}, 4, 'First document isbn field should contain four values');
555 is_deeply($docs->[0]->{isbn}, ['978-1-56619-909-4', '9781566199094', '1-56619-909-3', '1566199093'], 'First document isbn field should be set correctly');
558 $docs->[0]->{'local_classification'},
559 [$callno, $callno2, $long_callno],
560 'First document local_classification field should be set correctly'
563 # Nonfiling characters for sort fields
565 $docs->[0]->{uniform_title},
566 ['The uniform title with nonfiling indicator'],
567 'First document uniform_title field should contain the title verbatim'
570 $docs->[0]->{uniform_title__sort},
571 ['uniform title with nonfiling indicator'],
572 'First document uniform_title__sort field should contain the title with the first four initial characters removed'
575 # Tests for 'year' type
576 is(scalar @{$docs->[0]->{'date-of-publication'}}, 1, 'First document date-of-publication field should contain one value');
577 is_deeply($docs->[0]->{'date-of-publication'}, ['1962'], 'First document date-of-publication field should be set correctly');
580 $docs->[0]->{'copydate'},
582 'First document copydate field should be set correctly'
587 is(scalar @{$docs->[1]->{author}}, 1, 'Second document author field should contain one value');
588 is_deeply($docs->[1]->{author}, ['Author 2'], 'Second document author field should be set correctly');
590 is(scalar @{$docs->[1]->{items_withdrawn_status}}, 1, 'Second document items_withdrawn_status field should have one value');
592 $docs->[1]->{items_withdrawn_status},
594 'Second document items_withdrawn_status field should be set correctly'
598 $docs->[1]->{sum_item_price},
600 'Second document sum_item_price field should be set correctly'
604 $docs->[1]->{local_classification__sort},
605 [substr($long_callno, 0, 255)],
606 'Second document local_classification__sort field should be set correctly'
609 # Tests for 'year' type
611 $docs->[1]->{'copydate'},
613 'Second document copydate field should be set correctly'
616 $docs->[1]->{'date-of-publication'},
618 'Second document date-of-publication field should be set correctly'
624 $docs->[2]->{'copydate'},
626 'Third document copydate field should be set correctly'
629 # Mappings marc_type:
631 ok(!(defined $docs->[0]->{unimarc_title}), "No mapping when marc_type doesn't match marc flavour");
633 # Marc serialization format fallback for records exceeding ISO2709 max record size
635 my $large_marc_record = MARC::Record->new();
636 $large_marc_record->leader(' cam 22 a 4500');
638 $large_marc_record->append_fields(
639 MARC::Field->new('100', '', '', a => 'Author 1'),
640 MARC::Field->new('110', '', '', a => 'Corp Author'),
641 MARC::Field->new('210', '', '', a => 'Title 1'),
642 MARC::Field->new('245', '', '', a => 'Title:', b => 'large record'),
643 MARC::Field->new('999', '', '', c => '1234567'),
646 my $item_field = MARC::Field->new('952', '', '', o => '123456789123456789123456789', p => '123456789', z => 'test');
647 my $items_count = 1638;
648 while(--$items_count) {
649 $large_marc_record->append_fields($item_field);
652 $docs = $see->marc_records_to_documents([$large_marc_record]);
654 is($docs->[0]->{marc_format}, 'MARCXML', 'For record exceeding max record size marc_format should be set correctly');
656 $decoded_marc_record = $see->decode_record_from_result($docs->[0]);
658 ok($decoded_marc_record->isa('MARC::Record'), "MARCXML record successfully decoded from result");
659 is($decoded_marc_record->as_xml_record(), $large_marc_record->as_xml_record(), "Decoded MARCXML record has same data as original record");
667 marc_type => 'marc21',
668 marc_field => '245((ab)ab',
671 my $exception = try {
672 $see->marc_records_to_documents($records);
678 ok(defined $exception, "Exception has been thrown when processing mapping with unmatched opening parenthesis");
679 ok($exception->isa("Koha::Exceptions::Elasticsearch::MARCFieldExprParseError"), "Exception is of correct class");
680 ok($exception->message =~ /Unmatched opening parenthesis/, "Exception has the correct message");
689 marc_type => 'marc21',
690 marc_field => '245(ab))ab',
694 $see->marc_records_to_documents($records);
700 ok(defined $exception, "Exception has been thrown when processing mapping with unmatched closing parenthesis");
701 ok($exception->isa("Koha::Exceptions::Elasticsearch::MARCFieldExprParseError"), "Exception is of correct class");
702 ok($exception->message =~ /Unmatched closing parenthesis/, "Exception has the correct message");
705 my $marc_record_with_blank_field = MARC::Record->new();
706 $marc_record_with_blank_field->leader(' cam 22 a 4500');
708 $marc_record_with_blank_field->append_fields(
709 MARC::Field->new('100', '', '', a => ''),
710 MARC::Field->new('210', '', '', a => 'Title 1'),
711 MARC::Field->new('245', '', '', a => 'Title:', b => 'large record'),
712 MARC::Field->new('999', '', '', c => '1234567'),
714 $docs = $see->marc_records_to_documents([$marc_record_with_blank_field]);
715 is_deeply( $docs->[0]->{author},[],'No value placed into field if mapped marc field is blank');
716 is_deeply( $docs->[0]->{author__suggestion},[],'No value placed into suggestion if mapped marc field is blank');
720 subtest 'Koha::SearchEngine::Elasticsearch::marc_records_to_documents_array () tests' => sub {
724 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
725 t::lib::Mocks::mock_preference('ElasticsearchMARCFormat', 'ARRAY');
729 name => 'control_number',
735 marc_type => 'marc21',
740 my $se = Test::MockModule->new('Koha::SearchEngine::Elasticsearch');
741 $se->mock('_foreach_mapping', sub {
742 my ($self, $sub) = @_;
744 foreach my $map (@mappings) {
758 my $see = Koha::SearchEngine::Elasticsearch::Search->new({ index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX });
760 my $marc_record_1 = MARC::Record->new();
761 $marc_record_1->leader(' cam 22 a 4500');
762 $marc_record_1->append_fields(
763 MARC::Field->new('001', '123'),
764 MARC::Field->new('020', '', '', a => '1-56619-909-3'),
765 MARC::Field->new('100', '', '', a => 'Author 1'),
766 MARC::Field->new('110', '', '', a => 'Corp Author'),
767 MARC::Field->new('210', '', '', a => 'Title 1'),
768 MARC::Field->new('245', '', '', a => 'Title:', b => 'first record'),
769 MARC::Field->new('999', '', '', c => '1234567'),
771 my $marc_record_2 = MARC::Record->new();
772 $marc_record_2->leader(' cam 22 a 4500');
773 $marc_record_2->append_fields(
774 MARC::Field->new('100', '', '', a => 'Author 2'),
775 # MARC::Field->new('210', '', '', a => 'Title 2'),
776 # MARC::Field->new('245', '', '', a => 'Title: second record'),
777 MARC::Field->new('999', '', '', c => '1234568'),
778 MARC::Field->new('952', '', '', 0 => 1, g => 'string where should be numeric'),
780 my $records = [ $marc_record_1, $marc_record_2 ];
782 $see->get_elasticsearch_mappings(); #sort_fields will call this and use the actual db values unless we call it first
784 my $docs = $see->marc_records_to_documents($records);
787 is(scalar @{$docs}, 2, 'Two records converted to documents');
789 is_deeply($docs->[0]->{control_number}, ['123'], 'First record control number should be set correctly');
791 is($docs->[0]->{marc_format}, 'ARRAY', 'First document marc_format should be set correctly');
793 my $decoded_marc_record = $see->decode_record_from_result($docs->[0]);
795 ok($decoded_marc_record->isa('MARC::Record'), "ARRAY record successfully decoded from result");
796 is($decoded_marc_record->as_usmarc(), $marc_record_1->as_usmarc(), "Decoded ARRAY record has same data as original record");
799 subtest 'Koha::SearchEngine::Elasticsearch::marc_records_to_documents () authority tests' => sub {
803 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
804 t::lib::Mocks::mock_preference('ElasticsearchMARCFormat', 'ISO2709');
806 my $builder = t::lib::TestBuilder->new;
807 my $auth_type = $builder->build_object({ class => 'Koha::Authority::Types', value =>{
808 auth_tag_to_report => '150'
820 marc_type => 'marc21',
821 marc_field => '150(aevxyz)',
830 marc_type => 'marc21',
831 marc_field => '185v',
835 my $se = Test::MockModule->new('Koha::SearchEngine::Elasticsearch');
836 $se->mock('_foreach_mapping', sub {
837 my ($self, $sub) = @_;
839 foreach my $map (@mappings) {
853 my $see = Koha::SearchEngine::Elasticsearch::Search->new({ index => $Koha::SearchEngine::Elasticsearch::AUTHORITIES_INDEX });
854 my $marc_record_1 = MARC::Record->new();
855 $marc_record_1->append_fields(
856 MARC::Field->new('001', '123'),
857 MARC::Field->new('007', 'ku'),
858 MARC::Field->new('020', '', '', a => '1-56619-909-3'),
859 MARC::Field->new('150', '', '', a => 'Subject', v => 'Genresubdiv', x => 'Generalsubdiv', z => 'Geosubdiv'),
861 my $marc_record_2 = MARC::Record->new();
862 $marc_record_2->append_fields(
863 MARC::Field->new('150', '', '', a => 'Subject', v => 'Genresubdiv', z => 'Geosubdiv', x => 'Generalsubdiv', e => 'wrongsubdiv' ),
865 my $marc_record_3 = MARC::Record->new();
866 $marc_record_3->append_fields(
867 MARC::Field->new('185', '', '', v => 'Formsubdiv' ),
869 my $records = [ $marc_record_1, $marc_record_2, $marc_record_3 ];
871 $see->get_elasticsearch_mappings(); #sort_fields will call this and use the actual db values unless we call it first
873 my $docs = $see->marc_records_to_documents($records);
876 [ "Subject formsubdiv Genresubdiv generalsubdiv Generalsubdiv geographicsubdiv Geosubdiv" ],
877 $docs->[0]->{'match-heading'},
878 "First record match-heading should contain the correctly formatted heading"
881 [ "Subject formsubdiv Genresubdiv geographicsubdiv Geosubdiv generalsubdiv Generalsubdiv" ],
882 $docs->[1]->{'match-heading'},
883 "Second record match-heading should contain the correctly formatted heading without wrong subfield"
886 [ "Subject Genresubdiv Geosubdiv Generalsubdiv wrongsubdiv" ],
887 $docs->[1]->{'match'} ,
888 "Second record heading should contain the subfields with record order retained"
890 ok( !exists $docs->[2]->{'match-heading'}, "No match heading defined for subdivision record");
893 $docs->[2]->{'match'} ,
894 "Third record heading should contain the subfield"
899 subtest 'Koha::SearchEngine::Elasticsearch::marc_records_to_documents with IncludeSeeFromInSearches' => sub {
903 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
904 t::lib::Mocks::mock_preference('IncludeSeeFromInSearches', '1');
905 my $dbh = C4::Context->dbh;
907 my $builder = t::lib::TestBuilder->new;
908 my $auth_type = $builder->build_object({
909 class => 'Koha::Authority::Types',
911 auth_tag_to_report => '150'
914 my $authority_record = MARC::Record->new();
915 $authority_record->append_fields(
916 MARC::Field->new(150, '', '', a => 'Foo'),
917 MARC::Field->new(450, '', '', a => 'Bar'),
919 $dbh->do( "INSERT INTO auth_header (datecreated,marcxml) values (NOW(),?)", undef, ($authority_record->as_xml_record('MARC21') ) );
920 my $authid = $dbh->last_insert_id( undef, undef, 'auth_header', 'authid' );
930 marc_type => 'marc21',
931 marc_field => '650a',
935 my $se = Test::MockModule->new('Koha::SearchEngine::Elasticsearch');
936 $se->mock('_foreach_mapping', sub {
937 my ($self, $sub) = @_;
939 foreach my $map (@mappings) {
953 my $see = Koha::SearchEngine::Elasticsearch::Search->new({ index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX });
955 my $marc_record_1 = MARC::Record->new();
956 $marc_record_1->leader(' cam 22 a 4500');
957 $marc_record_1->append_fields(
958 MARC::Field->new('001', '123'),
959 MARC::Field->new('245', '', '', a => 'Title'),
960 MARC::Field->new('650', '', '', a => 'Foo', 9 => $authid),
961 MARC::Field->new('999', '', '', c => '1234567'),
964 # sort_fields will call this and use the actual db values unless we call it first
965 $see->get_elasticsearch_mappings();
967 my $docs = $see->marc_records_to_documents([$marc_record_1]);
969 is_deeply($docs->[0]->{subject}, ['Foo', 'Bar'], 'subject should include "See from"');
970 is_deeply($docs->[0]->{subject__facet}, ['Foo'], 'subject__facet should not include "See from"');
971 is_deeply($docs->[0]->{subject__suggestion}, [{ input => 'Foo' }], 'subject__suggestion should not include "See from"');
972 is_deeply($docs->[0]->{subject__sort}, ['Foo'], 'subject__sort should not include "See from"');
975 $schema->storage->txn_rollback;