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

(-)a/C4/AuthoritiesMarc.pm (-2 / +5 lines)
Lines 34-39 use Koha::Authority::Types; Link Here
34
use Koha::Authority;
34
use Koha::Authority;
35
use Koha::Libraries;
35
use Koha::Libraries;
36
use Koha::SearchEngine;
36
use Koha::SearchEngine;
37
use Koha::SearchEngine::Indexer;
37
use Koha::SearchEngine::Search;
38
use Koha::SearchEngine::Search;
38
39
39
use vars qw(@ISA @EXPORT);
40
use vars qw(@ISA @EXPORT);
Lines 628-634 sub AddAuthority { Link Here
628
    $record->insert_fields_ordered( MARC::Field->new( '001', $authid ) );
629
    $record->insert_fields_ordered( MARC::Field->new( '001', $authid ) );
629
    # Update
630
    # Update
630
    $dbh->do( "UPDATE auth_header SET authtypecode=?, marc=?, marcxml=? WHERE authid=?", undef, $authtypecode, $record->as_usmarc, $record->as_xml_record($format), $authid ) or die $DBI::errstr;
631
    $dbh->do( "UPDATE auth_header SET authtypecode=?, marc=?, marcxml=? WHERE authid=?", undef, $authtypecode, $record->as_usmarc, $record->as_xml_record($format), $authid ) or die $DBI::errstr;
631
    ModZebra( $authid, 'specialUpdate', 'authorityserver', $record );
632
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::AUTHORITIES_INDEX });
633
    $indexer->index_records( $authid, "specialUpdate", "authorityserver", $record );
632
634
633
    return ( $authid );
635
    return ( $authid );
634
}
636
}
Lines 656-662 sub DelAuthority { Link Here
656
    merge({ mergefrom => $authid }) if !$skip_merge;
658
    merge({ mergefrom => $authid }) if !$skip_merge;
657
    $dbh->do( "DELETE FROM auth_header WHERE authid=?", undef, $authid );
659
    $dbh->do( "DELETE FROM auth_header WHERE authid=?", undef, $authid );
658
    logaction( "AUTHORITIES", "DELETE", $authid, "authority" ) if C4::Context->preference("AuthoritiesLog");
660
    logaction( "AUTHORITIES", "DELETE", $authid, "authority" ) if C4::Context->preference("AuthoritiesLog");
659
    ModZebra( $authid, "recordDelete", "authorityserver", undef);
661
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::AUTHORITIES_INDEX });
662
    $indexer->index_records( $authid, "recordDelete", "authorityserver", undef );
660
}
663
}
661
664
662
=head2 ModAuthority
665
=head2 ModAuthority
(-)a/C4/Biblio.pm (-47 / +16 lines)
Lines 103-108 use Koha::Holds; Link Here
103
use Koha::ItemTypes;
103
use Koha::ItemTypes;
104
use Koha::Plugins;
104
use Koha::Plugins;
105
use Koha::SearchEngine;
105
use Koha::SearchEngine;
106
use Koha::SearchEngine::Indexer;
106
use Koha::Libraries;
107
use Koha::Libraries;
107
use Koha::Util::MARC;
108
use Koha::Util::MARC;
108
109
Lines 366-372 C<$error> : undef unless an error occurs Link Here
366
=cut
367
=cut
367
368
368
sub DelBiblio {
369
sub DelBiblio {
369
    my ($biblionumber) = @_;
370
    my ($biblionumber, $params) = @_;
370
371
371
    my $biblio = Koha::Biblios->find( $biblionumber );
372
    my $biblio = Koha::Biblios->find( $biblionumber );
372
    return unless $biblio; # Should we throw an exception instead?
373
    return unless $biblio; # Should we throw an exception instead?
Lines 391-401 sub DelBiblio { Link Here
391
        $hold->cancel;
392
        $hold->cancel;
392
    }
393
    }
393
394
394
    # Delete in Zebra. Be careful NOT to move this line after _koha_delete_biblio
395
    unless ( $params->{skip_record_index} ){
395
    # for at least 2 reasons :
396
        my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
396
    # - if something goes wrong, the biblio may be deleted from Koha but not from zebra
397
        $indexer->index_records( $biblionumber, "recordDelete", "biblioserver" );
397
    #   and we would have no way to remove it (except manually in zebra, but I bet it would be very hard to handle the problem)
398
    }
398
    ModZebra( $biblionumber, "recordDelete", "biblioserver" );
399
399
400
    # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
400
    # delete biblioitems and items from Koha tables and save in deletedbiblioitems,deleteditems
401
    $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
401
    $sth = $dbh->prepare("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
Lines 2489-2538 sub CountItemsIssued { Link Here
2489
2489
2490
=head2 ModZebra
2490
=head2 ModZebra
2491
2491
2492
  ModZebra( $biblionumber, $op, $server, $record );
2492
  ModZebra( $record_number, $op, $server, $record );
2493
2493
2494
$biblionumber is the biblionumber we want to index
2494
$record_number is the authid or biblionumber we want to index
2495
2495
2496
$op is specialUpdate or recordDelete, and is used to know what we want to do
2496
$op is the operation: specialUpdate or recordDelete
2497
2497
2498
$server is the server that we want to update
2498
$server is authorityserver or biblioserver
2499
2500
$record is the updated MARC record. If it's not supplied
2501
and is needed it will be loaded from the database.
2502
2499
2503
=cut
2500
=cut
2504
2501
2505
sub ModZebra {
2502
sub ModZebra {
2506
###Accepts a $server variable thus we can use it for biblios authorities or other zebra dbs
2503
    my ( $record_number, $op, $server ) = @_;
2507
    my ( $biblionumber, $op, $server, $record ) = @_;
2504
    $debug && warn "ModZebra: updates requested for: $record_number $op $server\n";
2508
    $debug && warn "ModZebra: update requested for: $biblionumber $op $server\n";
2509
    if ( C4::Context->preference('SearchEngine') eq 'Elasticsearch' ) {
2510
2511
        # TODO abstract to a standard API that'll work for whatever
2512
        require Koha::SearchEngine::Elasticsearch::Indexer;
2513
        my $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new(
2514
            {
2515
                index => $server eq 'biblioserver'
2516
                ? $Koha::SearchEngine::BIBLIOS_INDEX
2517
                : $Koha::SearchEngine::AUTHORITIES_INDEX
2518
            }
2519
        );
2520
        if ( $op eq 'specialUpdate' ) {
2521
            unless ($record) {
2522
                $record = GetMarcBiblio({
2523
                    biblionumber => $biblionumber,
2524
                    embed_items  => 1 });
2525
            }
2526
            $indexer->update_index_background( [$biblionumber], [$record] );
2527
        }
2528
        elsif ( $op eq 'recordDelete' ) {
2529
            $indexer->delete_index_background( [$biblionumber] );
2530
        }
2531
        else {
2532
            croak "ModZebra called with unknown operation: $op";
2533
        }
2534
    }
2535
2536
    my $dbh = C4::Context->dbh;
2505
    my $dbh = C4::Context->dbh;
2537
2506
2538
    # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
2507
    # true ModZebra commented until indexdata fixes zebraDB crashes (it seems they occur on multiple updates
Lines 2545-2561 sub ModZebra { Link Here
2545
        AND   operation = ?
2514
        AND   operation = ?
2546
        AND   done = 0";
2515
        AND   done = 0";
2547
    my $check_sth = $dbh->prepare_cached($check_sql);
2516
    my $check_sth = $dbh->prepare_cached($check_sql);
2548
    $check_sth->execute( $server, $biblionumber, $op );
2517
    $check_sth->execute( $server, $record_number, $op );
2549
    my ($count) = $check_sth->fetchrow_array;
2518
    my ($count) = $check_sth->fetchrow_array;
2550
    $check_sth->finish();
2519
    $check_sth->finish();
2551
    if ( $count == 0 ) {
2520
    if ( $count == 0 ) {
2552
        my $sth = $dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2521
        my $sth = $dbh->prepare("INSERT INTO zebraqueue  (biblio_auth_number,server,operation) VALUES(?,?,?)");
2553
        $sth->execute( $biblionumber, $server, $op );
2522
        $sth->execute( $record_number, $server, $op );
2554
        $sth->finish;
2523
        $sth->finish;
2555
    }
2524
    }
2556
}
2525
}
2557
2526
2558
2559
=head2 EmbedItemsInMarcBiblio
2527
=head2 EmbedItemsInMarcBiblio
2560
2528
2561
    EmbedItemsInMarcBiblio({
2529
    EmbedItemsInMarcBiblio({
Lines 3147-3153 sub ModBiblioMarc { Link Here
3147
    $m_rs->metadata( $record->as_xml_record($encoding) );
3115
    $m_rs->metadata( $record->as_xml_record($encoding) );
3148
    $m_rs->store;
3116
    $m_rs->store;
3149
3117
3150
    ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
3118
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
3119
    $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
3151
3120
3152
    return $biblionumber;
3121
    return $biblionumber;
3153
}
3122
}
(-)a/C4/Circulation.pm (-2 / +16 lines)
Lines 3775-3783 sub ReturnLostItem{ Link Here
3775
    MarkIssueReturned( $borrowernumber, $itemnum );
3775
    MarkIssueReturned( $borrowernumber, $itemnum );
3776
}
3776
}
3777
3777
3778
=head2 LostItem
3779
3780
  LostItem( $itemnumber, $mark_lost_from, $force_mark_returned, [$params] );
3781
3782
The final optional parameter, C<$params>, expected to contain
3783
'skip_record_index' key, which relayed down to Koha::Item/store,
3784
there it prevents calling of ModZebra index_records,
3785
which takes most of the time in batch adds/deletes: index_records better
3786
to be called later in C<additem.pl> after the whole loop.
3787
3788
$params:
3789
    skip_record_index => 1|0
3790
3791
=cut
3778
3792
3779
sub LostItem{
3793
sub LostItem{
3780
    my ($itemnumber, $mark_lost_from, $force_mark_returned) = @_;
3794
    my ($itemnumber, $mark_lost_from, $force_mark_returned, $params) = @_;
3781
3795
3782
    unless ( $mark_lost_from ) {
3796
    unless ( $mark_lost_from ) {
3783
        # Temporary check to avoid regressions
3797
        # Temporary check to avoid regressions
Lines 3828-3834 sub LostItem{ Link Here
3828
3842
3829
    #When item is marked lost automatically cancel its outstanding transfers and set items holdingbranch to the transfer source branch (frombranch)
3843
    #When item is marked lost automatically cancel its outstanding transfers and set items holdingbranch to the transfer source branch (frombranch)
3830
    if (my ( $datesent,$frombranch,$tobranch ) = GetTransfers($itemnumber)) {
3844
    if (my ( $datesent,$frombranch,$tobranch ) = GetTransfers($itemnumber)) {
3831
        Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store;
3845
        Koha::Items->find($itemnumber)->holdingbranch($frombranch)->store({ skip_record_index => $params->{skip_record_index} });
3832
    }
3846
    }
3833
    my $transferdeleted = DeleteTransfer($itemnumber);
3847
    my $transferdeleted = DeleteTransfer($itemnumber);
3834
}
3848
}
(-)a/C4/Items.pm (-11 / +26 lines)
Lines 70-75 use Koha::Biblioitems; Link Here
70
use Koha::Items;
70
use Koha::Items;
71
use Koha::ItemTypes;
71
use Koha::ItemTypes;
72
use Koha::SearchEngine;
72
use Koha::SearchEngine;
73
use Koha::SearchEngine::Indexer;
73
use Koha::SearchEngine::Search;
74
use Koha::SearchEngine::Search;
74
use Koha::Libraries;
75
use Koha::Libraries;
75
76
Lines 143-155 Given a MARC::Record object containing an embedded item Link Here
143
record and a biblionumber, create a new item record.
144
record and a biblionumber, create a new item record.
144
145
145
The final optional parameter, C<$params>, expected to contain
146
The final optional parameter, C<$params>, expected to contain
146
'skip_modzebra_update' key, which relayed down to Koha::Item/store,
147
'skip_record_index' key, which relayed down to Koha::Item/store,
147
there it prevents calling of ModZebra (and Elasticsearch update),
148
there it prevents calling of index_records,
148
which takes most of the time in batch adds/deletes: ModZebra better
149
which takes most of the time in batch adds/deletes: index_records
149
to be called later in C<additem.pl> after the whole loop.
150
to be called later in C<additem.pl> after the whole loop.
150
151
151
$params:
152
$params:
152
    skip_modzebra_update => 1|0
153
    skip_record_index => 1|0
153
154
154
=cut
155
=cut
155
156
Lines 173-179 sub AddItemFromMarc { Link Here
173
    $item_values->{biblionumber} = $biblionumber;
174
    $item_values->{biblionumber} = $biblionumber;
174
    $item_values->{cn_source} = delete $item_values->{'items.cn_source'}; # Because of C4::Biblio::_disambiguate
175
    $item_values->{cn_source} = delete $item_values->{'items.cn_source'}; # Because of C4::Biblio::_disambiguate
175
    $item_values->{cn_sort}   = delete $item_values->{'items.cn_sort'};   # Because of C4::Biblio::_disambiguate
176
    $item_values->{cn_sort}   = delete $item_values->{'items.cn_sort'};   # Because of C4::Biblio::_disambiguate
176
    my $item = Koha::Item->new( $item_values )->store({ skip_modzebra_update => $params->{skip_modzebra_update} });
177
    my $item = Koha::Item->new( $item_values )->store({ skip_record_index => $params->{skip_record_index} });
177
    return ( $item->biblionumber, $item->biblioitemnumber, $item->itemnumber );
178
    return ( $item->biblionumber, $item->biblioitemnumber, $item->itemnumber );
178
}
179
}
179
180
Lines 281-290 sub AddItemBatchFromMarc { Link Here
281
    return (\@itemnumbers, \@errors);
282
    return (\@itemnumbers, \@errors);
282
}
283
}
283
284
285
=head2 ModItemFromMarc
286
287
my $item = ModItemFromMarc($item_marc, $biblionumber, $itemnumber[, $params]);
288
289
The final optional parameter, C<$params>, expected to contain
290
'skip_record_index' key, which relayed down to Koha::Item/store,
291
there it prevents calling of index_records,
292
which takes most of the time in batch adds/deletes: index_records better
293
to be called later in C<additem.pl> after the whole loop.
294
295
$params:
296
    skip_record_index => 1|0
297
298
=cut
299
284
sub ModItemFromMarc {
300
sub ModItemFromMarc {
285
    my $item_marc = shift;
301
    my ( $item_marc, $biblionumber, $itemnumber, $params ) = @_;
286
    my $biblionumber = shift;
287
    my $itemnumber = shift;
288
302
289
    my $frameworkcode = C4::Biblio::GetFrameworkCode($biblionumber);
303
    my $frameworkcode = C4::Biblio::GetFrameworkCode($biblionumber);
290
    my ( $itemtag, $itemsubfield ) = C4::Biblio::GetMarcFromKohaField( "items.itemnumber" );
304
    my ( $itemtag, $itemsubfield ) = C4::Biblio::GetMarcFromKohaField( "items.itemnumber" );
Lines 313-319 sub ModItemFromMarc { Link Here
313
    $item_object = $item_object->set_or_blank($item);
327
    $item_object = $item_object->set_or_blank($item);
314
    my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
328
    my $unlinked_item_subfields = _get_unlinked_item_subfields( $localitemmarc, $frameworkcode );
315
    $item_object->more_subfields_xml(_get_unlinked_subfields_xml($unlinked_item_subfields));
329
    $item_object->more_subfields_xml(_get_unlinked_subfields_xml($unlinked_item_subfields));
316
    $item_object->store;
330
    $item_object->store({ skip_record_index => $params->{skip_record_index} });
317
331
318
    return $item_object->unblessed;
332
    return $item_object->unblessed;
319
}
333
}
Lines 1094-1101 sub MoveItemFromBiblio { Link Here
1094
            AND biblionumber = ?
1108
            AND biblionumber = ?
1095
    |, undef, $tobiblioitem, $tobiblio, $itemnumber, $frombiblio );
1109
    |, undef, $tobiblioitem, $tobiblio, $itemnumber, $frombiblio );
1096
    if ($return == 1) {
1110
    if ($return == 1) {
1097
        ModZebra( $tobiblio, "specialUpdate", "biblioserver" );
1111
        my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
1098
        ModZebra( $frombiblio, "specialUpdate", "biblioserver" );
1112
        $indexer->index_records( $tobiblio, "specialUpdate", "biblioserver" );
1113
        $indexer->index_records( $frombiblio, "specialUpdate", "biblioserver" );
1099
	    # Checking if the item we want to move is in an order 
1114
	    # Checking if the item we want to move is in an order 
1100
        require C4::Acquisition;
1115
        require C4::Acquisition;
1101
        my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
1116
        my $order = C4::Acquisition::GetOrderFromItemnumber($itemnumber);
(-)a/Koha/Item.pm (-7 / +9 lines)
Lines 30-41 use Koha::DateUtils qw( dt_from_string ); Link Here
30
use C4::Context;
30
use C4::Context;
31
use C4::Circulation;
31
use C4::Circulation;
32
use C4::Reserves;
32
use C4::Reserves;
33
use C4::Biblio qw( ModZebra ); # FIXME This is terrible, we should move the indexation code outside of C4::Biblio
34
use C4::ClassSource; # FIXME We would like to avoid that
33
use C4::ClassSource; # FIXME We would like to avoid that
35
use C4::Log qw( logaction );
34
use C4::Log qw( logaction );
36
35
37
use Koha::Checkouts;
36
use Koha::Checkouts;
38
use Koha::CirculationRules;
37
use Koha::CirculationRules;
38
use Koha::SearchEngine::Indexer;
39
use Koha::Item::Transfer::Limits;
39
use Koha::Item::Transfer::Limits;
40
use Koha::Item::Transfers;
40
use Koha::Item::Transfers;
41
use Koha::Patrons;
41
use Koha::Patrons;
Lines 60-67 Koha::Item - Koha Item object class Link Here
60
60
61
    $item->store;
61
    $item->store;
62
62
63
$params can take an optional 'skip_modzebra_update' parameter.
63
$params can take an optional 'skip_record_index' parameter.
64
If set, the reindexation process will not happen (ModZebra not called)
64
If set, the reindexation process will not happen (index_records not called)
65
65
66
NOTE: This is a temporary fix to answer a performance issue when lot of items
66
NOTE: This is a temporary fix to answer a performance issue when lot of items
67
are added (or modified) at the same time.
67
are added (or modified) at the same time.
Lines 176-183 sub store { Link Here
176
    }
176
    }
177
177
178
    my $result = $self->SUPER::store;
178
    my $result = $self->SUPER::store;
179
    C4::Biblio::ModZebra( $self->biblionumber, "specialUpdate", "biblioserver" )
179
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
180
        unless $params->{skip_modzebra_update};
180
    $indexer->index_records( $self->biblionumber, "specialUpdate", "biblioserver" )
181
        unless $params->{skip_record_index};
181
182
182
    return $result;
183
    return $result;
183
}
184
}
Lines 193-200 sub delete { Link Here
193
    # FIXME check the item has no current issues
194
    # FIXME check the item has no current issues
194
    # i.e. raise the appropriate exception
195
    # i.e. raise the appropriate exception
195
196
196
    C4::Biblio::ModZebra( $self->biblionumber, "specialUpdate", "biblioserver" )
197
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
197
        unless $params->{skip_modzebra_update};
198
    $indexer->index_records( $self->biblionumber, "specialUpdate", "biblioserver" )
199
        unless $params->{skip_record_index};
198
200
199
    $self->_after_item_action_hooks({ action => 'delete' });
201
    $self->_after_item_action_hooks({ action => 'delete' });
200
202
(-)a/Koha/SearchEngine/Elasticsearch/Indexer.pm (+46 lines)
Lines 25-30 use base qw(Koha::SearchEngine::Elasticsearch); Link Here
25
use Data::Dumper;
25
use Data::Dumper;
26
26
27
use Koha::Exceptions;
27
use Koha::Exceptions;
28
use Koha::SearchEngine::Zebra::Indexer;
29
use C4::Biblio;
28
use C4::Context;
30
use C4::Context;
29
31
30
=head1 NAME
32
=head1 NAME
Lines 277-282 sub update_index_background { Link Here
277
    $self->update_index(@_);
279
    $self->update_index(@_);
278
}
280
}
279
281
282
=head2 index_records
283
284
This function takes an array of record numbers and fetches the records to send to update_index
285
for actual indexing.
286
287
If $records parameter is provided the records will be used as-is, this is only utilized for authorities
288
at the moment.
289
290
The other variables are used for parity with Zebra indexing calls. Currently the calls are passed through
291
to Zebra as well.
292
293
=cut
294
295
sub index_records {
296
    my ( $self, $record_numbers, $op, $server, $records ) = @_;
297
    $record_numbers = [$record_numbers] if ref $record_numbers ne 'ARRAY' && defined $record_numbers;
298
    $records = [$records] if ref $records ne 'ARRAY' && defined $records;
299
    if ( $op eq 'specialUpdate' ) {
300
        my $index_record_numbers;
301
        unless ($records) {
302
            foreach my $record_number ( @$record_numbers ){
303
                my $record = _get_record( $record_number, $server );
304
                if( $record ){
305
                    push @$records, $record;
306
                    push @$index_record_numbers, $record_number;
307
                }
308
            }
309
        }
310
        $self->update_index_background( $index_record_numbers, $records ) if $index_record_numbers && $records;
311
    }
312
    elsif ( $op eq 'recordDelete' ) {
313
        $self->delete_index_background( $record_numbers );
314
    }
315
    #FIXME Current behaviour is to index Zebra when using ES, at some point we should stop
316
    Koha::SearchEngine::Zebra::Indexer::index_records( $self, $record_numbers, $op, $server, undef );
317
}
318
319
sub _get_record {
320
    my ( $id, $server ) = @_;
321
    return $server eq 'biblioserver'
322
        ? C4::Biblio::GetMarcBiblio({ biblionumber => $id, embed_items  => 1 })
323
        : C4::AuthoritiesMarc::GetAuthority($id);
324
}
325
280
=head2 delete_index($biblionums)
326
=head2 delete_index($biblionums)
281
327
282
C<$biblionums> is an arrayref of biblionumbers to delete from the index.
328
C<$biblionums> is an arrayref of biblionumbers to delete from the index.
(-)a/Koha/SearchEngine/Indexer.pm (+59 lines)
Line 0 Link Here
1
package Koha::SearchEngine::Indexer;
2
3
# This file is part of Koha.
4
#
5
# Copyright 2020 ByWater Solutions
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
# This is a shim that gives you the appropriate search object for your
21
# system preference.
22
23
=head1 NAME
24
25
Koha::SearchEngine::Indexer - instantiate the search object that corresponds to
26
the C<SearchEngine> system preference.
27
28
=head1 DESCRIPTION
29
30
This allows you to be agnostic about what the search engine configuration is
31
and just get whatever indexer object you need.
32
33
=head1 SYNOPSIS
34
35
    use Koha::SearchEngine::Indexer;
36
    my $searcher = Koha::SearchEngine::Indexer->new({ index => Koha::SearchEngine::Index});
37
38
=head1 METHODS
39
40
=head2 new
41
42
Creates a new C<Search> of whatever the relevant type is.
43
44
=cut
45
46
use Modern::Perl;
47
use C4::Context;
48
use C4::Biblio qw//;
49
50
sub new {
51
    my $engine = C4::Context->preference("SearchEngine") // 'Zebra';
52
    my $file = "Koha/SearchEngine/${engine}/Indexer.pm";
53
    my $class = "Koha::SearchEngine::${engine}::Indexer";
54
    require $file;
55
    shift @_;
56
    return $class->new(@_);
57
}
58
59
1;
(-)a/Koha/SearchEngine/Zebra/Indexer.pm (+65 lines)
Line 0 Link Here
1
package Koha::SearchEngine::Zebra::Indexer;
2
3
# Copyright 2020 ByWater Solutions
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
use Modern::Perl;
21
use C4::Biblio qw(ModZebra); # FIXME This is terrible, we should move the indexation code outside of C4::Biblio
22
use base qw(Class::Accessor);
23
24
=head1 NAME
25
26
Koha::SearchEngine::Elasticsearch::Indexer - handles adding new records to the index
27
28
=head1 SYNOPSIS
29
30
    my $indexer = Koha::SearchEngine::Zebra::Indexer->new();
31
    $indexer->index_records( $record_numbers, $op, $server, $records);
32
33
34
=head1 FUNCTIONS
35
36
=head2 new
37
38
    This is a dummy function to create the object. C4::Biblio->ModZebra is doing the real work
39
    now and needed variables are passed to index_records
40
41
=cut
42
43
sub new {
44
    my $class = shift @_;
45
    my $self = $class->SUPER::new(@_);
46
}
47
48
=head2 index_records($record_numbers, $op, $server, $records)
49
50
    This is simply a wrapper to C4::Biblio::ModZebra that takes an array of records and
51
    passes them through individually
52
53
    The final parameter $records is not used in Zebra, it exists for parity with Elasticsearch calls
54
55
=cut
56
57
sub index_records {
58
    my ( $self, $record_numbers, $op, $server, $records ) = @_;
59
    $record_numbers = [$record_numbers] if ref $record_numbers ne 'ARRAY' && defined $record_numbers;
60
    foreach my $record_number ( @$record_numbers ){
61
        ModZebra( $record_number, $op, $server );
62
    }
63
}
64
65
1;
(-)a/cataloguing/additem.pl (-4 / +7 lines)
Lines 35-40 use Koha::Items; Link Here
35
use Koha::ItemTypes;
35
use Koha::ItemTypes;
36
use Koha::Libraries;
36
use Koha::Libraries;
37
use Koha::Patrons;
37
use Koha::Patrons;
38
use Koha::SearchEngine::Indexer;
38
use List::MoreUtils qw/any/;
39
use List::MoreUtils qw/any/;
39
use C4::Search;
40
use C4::Search;
40
use Storable qw(thaw freeze);
41
use Storable qw(thaw freeze);
Lines 596-602 if ($op eq "additem") { Link Here
596
		# Adding the item
597
		# Adding the item
597
        if (!$exist_itemnumber) {
598
        if (!$exist_itemnumber) {
598
            my ( $oldbiblionumber, $oldbibnum, $oldbibitemnum ) =
599
            my ( $oldbiblionumber, $oldbibnum, $oldbibitemnum ) =
599
                AddItemFromMarc( $record, $biblionumber, { skip_modzebra_update => 1 } );
600
                AddItemFromMarc( $record, $biblionumber, { skip_record_index => 1 } );
600
            set_item_default_location($oldbibitemnum);
601
            set_item_default_location($oldbibitemnum);
601
602
602
            # We count the item only if it was really added
603
            # We count the item only if it was really added
Lines 611-617 if ($op eq "additem") { Link Here
611
		$oldbarcode = $barcodevalue;
612
		$oldbarcode = $barcodevalue;
612
	    }
613
	    }
613
614
614
        C4::Biblio::ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
615
        my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
616
        $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
615
617
616
	    undef($itemrecord);
618
	    undef($itemrecord);
617
	}
619
	}
Lines 685-695 if ($op eq "additem") { Link Here
685
#-------------------------------------------------------------------------------
687
#-------------------------------------------------------------------------------
686
    my $items = Koha::Items->search({ biblionumber => $biblionumber });
688
    my $items = Koha::Items->search({ biblionumber => $biblionumber });
687
    while ( my $item = $items->next ) {
689
    while ( my $item = $items->next ) {
688
        $error = $item->safe_delete({ skip_modzebra_update => 1 });
690
        $error = $item->safe_delete({ skip_record_index => 1 });
689
        next if ref $error eq 'Koha::Item'; # Deleted item is returned if deletion successful
691
        next if ref $error eq 'Koha::Item'; # Deleted item is returned if deletion successful
690
        push @errors,$error;
692
        push @errors,$error;
691
    }
693
    }
692
    C4::Biblio::ModZebra( $biblionumber, "specialUpdate", "biblioserver" );
694
    my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
695
    $indexer->index_records( $biblionumber, "specialUpdate", "biblioserver" );
693
    if ( @errors ) {
696
    if ( @errors ) {
694
        $nextop="additem";
697
        $nextop="additem";
695
    } else {
698
    } else {
(-)a/t/db_dependent/Koha/SearchEngine/Elasticsearch/Indexer.t (+2 lines)
Lines 1-3 Link Here
1
#!/usr/bin/perl
2
1
# Copyright 2015 Catalyst IT
3
# Copyright 2015 Catalyst IT
2
#
4
#
3
# This file is part of Koha.
5
# This file is part of Koha.
(-)a/t/db_dependent/Koha/SearchEngine/Indexer.t (-6 / +6 lines)
Lines 113-120 subtest 'Test indexer calls' => sub { Link Here
113
            $item2 = $builder->build_sample_item({biblionumber => $biblio->biblionumber});
113
            $item2 = $builder->build_sample_item({biblionumber => $biblio->biblionumber});
114
        } [$engine,"Koha::Item",$engine,"Koha::Item"], "index_records is called for $engine when adding an item (Item->store)";
114
        } [$engine,"Koha::Item",$engine,"Koha::Item"], "index_records is called for $engine when adding an item (Item->store)";
115
        warnings_are{
115
        warnings_are{
116
            $item->store({ skip_modzebra_update => 1 });
116
            $item->store({ skip_record_index => 1 });
117
        } undef, "index_records is not called for $engine when adding an item (Item->store) if skip_modzebra_update passed";
117
        } undef, "index_records is not called for $engine when adding an item (Item->store) if skip_record_index passed";
118
118
119
        $builder->build({
119
        $builder->build({
120
            source => 'Branchtransfer',
120
            source => 'Branchtransfer',
Lines 132-146 subtest 'Test indexer calls' => sub { Link Here
132
                datearrived => undef}
132
                datearrived => undef}
133
        });
133
        });
134
        warnings_are{
134
        warnings_are{
135
            LostItem( $item->itemnumber, "tests", undef, { skip_modzebra_update => 1 });
135
            LostItem( $item->itemnumber, "tests", undef, { skip_record_index => 1 });
136
        } undef, "index_records is not called for $engine when calling LostItem and transfer exists if skip_modzebra_update";
136
        } undef, "index_records is not called for $engine when calling LostItem and transfer exists if skip_record_index";
137
137
138
        warnings_are{
138
        warnings_are{
139
            $item->delete();
139
            $item->delete();
140
        } [$engine,"Koha::Item"], "index_records is called for $engine when deleting an item (Item->delete)";
140
        } [$engine,"Koha::Item"], "index_records is called for $engine when deleting an item (Item->delete)";
141
        warnings_are{
141
        warnings_are{
142
            $item2->delete({ skip_modzebra_update => 1 });
142
            $item2->delete({ skip_record_index => 1 });
143
        } undef, "index_records is not called for $engine when adding an item (Item->store) if skip_modzebra_update passed";
143
        } undef, "index_records is not called for $engine when adding an item (Item->store) if skip_record_index passed";
144
144
145
        warnings_are{
145
        warnings_are{
146
            DelBiblio( $biblio->biblionumber );
146
            DelBiblio( $biblio->biblionumber );
(-)a/tools/batchMod.pl (-6 / +23 lines)
Lines 44-49 use Koha::DateUtils; Link Here
44
use Koha::Items;
44
use Koha::Items;
45
use Koha::ItemTypes;
45
use Koha::ItemTypes;
46
use Koha::Patrons;
46
use Koha::Patrons;
47
use Koha::SearchEngine::Indexer;
47
48
48
my $input = new CGI;
49
my $input = new CGI;
49
my $dbh = C4::Context->dbh;
50
my $dbh = C4::Context->dbh;
Lines 200-205 if ($op eq "action") { Link Here
200
                            my $return = $item->safe_delete;
201
                            my $return = $item->safe_delete;
201
                            if ( ref( $return ) ) {
202
                            if ( ref( $return ) ) {
202
                                $deleted_items++;
203
                                $deleted_items++;
204
                                push @$upd_biblionumbers, $itemdata->{'biblionumber'};
203
                            }
205
                            }
204
                            else {
206
                            else {
205
                                $not_deleted_items++;
207
                                $not_deleted_items++;
Lines 217-225 if ($op eq "action") { Link Here
217
                            if ($del_records) {
219
                            if ($del_records) {
218
                                my $itemscount = Koha::Biblios->find( $itemdata->{'biblionumber'} )->items->count;
220
                                my $itemscount = Koha::Biblios->find( $itemdata->{'biblionumber'} )->items->count;
219
                                if ( $itemscount == 0 ) {
221
                                if ( $itemscount == 0 ) {
220
                                    my $error = DelBiblio( $itemdata->{'biblionumber'} );
222
                                    my $error = DelBiblio( $itemdata->{'biblionumber'}, { skip_record_index => 1 } );
221
                                    unless ($error) {
223
                                    unless ($error) {
222
                                        $deleted_records++;
224
                                        $deleted_records++;
225
                                        push @$del_biblionumbers, $itemdata->{'biblionumber'};
223
                                        if ( $src eq 'CATALOGUING' ) {
226
                                        if ( $src eq 'CATALOGUING' ) {
224
                                            # We are coming catalogue/detail.pl, there were items from a single bib record
227
                                            # We are coming catalogue/detail.pl, there were items from a single bib record
225
                                            $template->param( biblio_deleted => 1 );
228
                                            $template->param( biblio_deleted => 1 );
Lines 279-293 if ($op eq "action") { Link Here
279
                                            my $item = ModItemFromMarc(
282
                                            my $item = ModItemFromMarc(
280
                                                $localmarcitem,
283
                                                $localmarcitem,
281
                                                $itemdata->{biblionumber},
284
                                                $itemdata->{biblionumber},
282
                                                $itemnumber
285
                                                $itemnumber,
286
                                                { skip_record_index => 1 },
283
                                            )
287
                                            )
284
                                          )
288
                                          )
285
                                        {
289
                                        {
286
                                            LostItem( $itemnumber, 'batchmod' )
290
                                            LostItem(
287
                                              if $item->{itemlost}
291
                                                $itemnumber,
292
                                                'batchmod',
293
                                                undef,
294
                                                { skip_record_index => 1 }
295
                                            ) if $item->{itemlost}
288
                                              and not $itemdata->{itemlost};
296
                                              and not $itemdata->{itemlost};
289
                                        }
297
                                        }
290
                                    };
298
                                    };
299
                                    push @$upd_biblionumbers, $itemdata->{'biblionumber'};
291
                                }
300
                                }
292
                                if ($runinbackground) {
301
                                if ($runinbackground) {
293
                                    $modified_items++ if $modified;
302
                                    $modified_items++ if $modified;
Lines 316-322 if ($op eq "action") { Link Here
316
            }
325
            }
317
            die "Something terrible has happened!"
326
            die "Something terrible has happened!"
318
                if ($_ =~ /Rollback failed/); # Rollback failed
327
                if ($_ =~ /Rollback failed/); # Rollback failed
319
        }
328
        };
329
        $upd_biblionumbers = [ uniq @$upd_biblionumbers ]; # Only update each bib once
330
331
        # Don't send specialUpdate for records we are going to delete
332
        my %del_bib_hash = map{ $_ => undef } @$del_biblionumbers;
333
        @$upd_biblionumbers = grep( ! exists( $del_bib_hash{$_} ), @$upd_biblionumbers );
334
335
        my $indexer = Koha::SearchEngine::Indexer->new({ index => $Koha::SearchEngine::BIBLIOS_INDEX });
336
        $indexer->index_records( $upd_biblionumbers, 'specialUpdate', "biblioserver", undef ) if @$upd_biblionumbers;
337
        $indexer->index_records( $del_biblionumbers, 'recordDelete', "biblioserver", undef ) if @$del_biblionumbers;
320
    }
338
    }
321
}
339
}
322
#
340
#
323
- 

Return to bug 25265