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

(-)a/C4/Biblio.pm (-3 / +10 lines)
Lines 554-560 sub LinkBibHeadingsToAuthorities { Link Here
554
                        '', '', "a" => "" . $field->subfield('a') );
554
                        '', '', "a" => "" . $field->subfield('a') );
555
                    map {
555
                    map {
556
                        $authfield->add_subfields( $_->[0] => $_->[1] )
556
                        $authfield->add_subfields( $_->[0] => $_->[1] )
557
                          if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" )
557
                          if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a"
558
                            && C4::Heading::valid_bib_heading_subfield(
559
                                $authority_type->auth_tag_to_report, $_->[0], $frameworkcode )
560
                            );
558
                    } $field->subfields();
561
                    } $field->subfields();
559
                    $marcrecordauth->insert_fields_ordered($authfield);
562
                    $marcrecordauth->insert_fields_ordered($authfield);
560
563
Lines 2680-2686 sub ModZebra { Link Here
2680
2683
2681
        # TODO abstract to a standard API that'll work for whatever
2684
        # TODO abstract to a standard API that'll work for whatever
2682
        require Koha::SearchEngine::Elasticsearch::Indexer;
2685
        require Koha::SearchEngine::Elasticsearch::Indexer;
2683
        my $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new(
2686
        # Use state to speed up repeated calls in batch processes and reuse the indexer.
2687
        # This also avoids creating a massive amount of ES connectors that would 
2688
        # eventually run out of file descriptors.
2689
        state $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new(
2684
            {
2690
            {
2685
                index => $server eq 'biblioserver'
2691
                index => $server eq 'biblioserver'
2686
                ? $Koha::SearchEngine::BIBLIOS_INDEX
2692
                ? $Koha::SearchEngine::BIBLIOS_INDEX
Lines 2702-2707 sub ModZebra { Link Here
2702
        else {
2708
        else {
2703
            croak "ModZebra called with unknown operation: $op";
2709
            croak "ModZebra called with unknown operation: $op";
2704
        }
2710
        }
2711
        return;
2705
    }
2712
    }
2706
2713
2707
    my $dbh = C4::Context->dbh;
2714
    my $dbh = C4::Context->dbh;
Lines 2927-2933 sub _koha_modify_biblio { Link Here
2927
2934
2928
    $sth->execute(
2935
    $sth->execute(
2929
        $frameworkcode,      $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
2936
        $frameworkcode,      $biblio->{'author'},      $biblio->{'title'},         $biblio->{'unititle'}, $biblio->{'notes'},
2930
        $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'}, $biblio->{'abstract'}, $biblio->{'biblionumber'}
2937
        $biblio->{'serial'}, $biblio->{'seriestitle'}, $biblio->{'copyrightdate'} ? int($biblio->{'copyrightdate'}) : undef, $biblio->{'abstract'}, $biblio->{'biblionumber'}
2931
    ) if $biblio->{'biblionumber'};
2938
    ) if $biblio->{'biblionumber'};
2932
2939
2933
    if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
2940
    if ( $dbh->errstr || !$biblio->{'biblionumber'} ) {
(-)a/C4/Heading.pm (-6 / +37 lines)
Lines 17-22 package C4::Heading; Link Here
17
# You should have received a copy of the GNU General Public License
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>.
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
19
20
use Modern::Perl;
21
20
use strict;
22
use strict;
21
use warnings;
23
use warnings;
22
use MARC::Record;
24
use MARC::Record;
Lines 50-56 headings found in bibliographic and authority records. Link Here
50
52
51
  my $heading = C4::Heading->new_from_bib_field($field, $frameworkcode, [, $marc_flavour]);
53
  my $heading = C4::Heading->new_from_bib_field($field, $frameworkcode, [, $marc_flavour]);
52
54
53
Given a C<MARC::Field> object containing a heading from a 
55
Given a C<MARC::Field> object containing a heading from a
54
bib record, create a C<C4::Heading> object.
56
bib record, create a C<C4::Heading> object.
55
57
56
The optional second parameter is the MARC flavour (i.e., MARC21
58
The optional second parameter is the MARC flavour (i.e., MARC21
Lines 141-147 sub search_form { Link Here
141
143
142
  my $authorities = $heading->authorities([$skipmetadata]);
144
  my $authorities = $heading->authorities([$skipmetadata]);
143
145
144
Return a list of authority records for this 
146
Return a list of authority records for this
145
heading. If passed a true value for $skipmetadata,
147
heading. If passed a true value for $skipmetadata,
146
SearchAuthorities will return only authids.
148
SearchAuthorities will return only authids.
147
149
Lines 170-175 sub preferred_authorities { Link Here
170
    return $results;
172
    return $results;
171
}
173
}
172
174
175
=head2 valid_bib_heading_subfield
176
177
    if (C4::Heading::valid_bib_heading_subfield('100', 'e', '')) ...
178
179
=cut
180
181
sub valid_bib_heading_subfield {
182
    my $tag           = shift;
183
    my $subfield      = shift;
184
    my $frameworkcode = shift;
185
    my $marcflavour   = @_ ? shift : C4::Context->preference('marcflavour');
186
187
    my $marc_handler = _marc_format_handler($marcflavour);
188
189
    return $marc_handler->valid_bib_heading_subfield( $tag, $subfield, $frameworkcode );
190
}
191
173
=head1 INTERNAL METHODS
192
=head1 INTERNAL METHODS
174
193
175
=head2 _search
194
=head2 _search
Lines 200-211 sub _search { Link Here
200
    #        push @operator, 'is';
219
    #        push @operator, 'is';
201
    #        push @value, $self->{'thesaurus'};
220
    #        push @value, $self->{'thesaurus'};
202
    #    }
221
    #    }
203
    require C4::AuthoritiesMarc;
222
204
    return C4::AuthoritiesMarc::SearchAuthorities(
223
    require Koha::SearchEngine::QueryBuilder;
224
    require Koha::SearchEngine::Search;
225
226
    # Use state variables to avoid recreating the objects every time.
227
    # With Elasticsearch this also avoids creating a massive amount of
228
    # ES connectors that would eventually run out of file descriptors.
229
    state $builder = Koha::SearchEngine::QueryBuilder->new(
230
        { index => $Koha::SearchEngine::AUTHORITIES_INDEX } );
231
    state $searcher = Koha::SearchEngine::Search->new(
232
        {index => $Koha::SearchEngine::AUTHORITIES_INDEX} );
233
234
    my $search_query = $builder->build_authorities_query_compat(
205
        \@marclist, \@and_or, \@excluding, \@operator,
235
        \@marclist, \@and_or, \@excluding, \@operator,
206
        \@value,    0,        20,          $self->{'auth_type'},
236
        \@value,    $self->{'auth_type'},
207
        'AuthidAsc',         $skipmetadata
237
        'AuthidAsc'
208
    );
238
    );
239
    return $searcher->search_auth_compat( $search_query, 0, 20, $skipmetadata );
209
}
240
}
210
241
211
=head1 INTERNAL FUNCTIONS
242
=head1 INTERNAL FUNCTIONS
(-)a/C4/Heading/MARC21.pm (+16 lines)
Lines 161-166 sub valid_bib_heading_tag { Link Here
161
161
162
}
162
}
163
163
164
=head2 valid_bib_heading_subfield
165
166
=cut
167
168
sub valid_bib_heading_subfield {
169
    my $self          = shift;
170
    my $tag           = shift;
171
    my $subfield      = shift;
172
    my $frameworkcode = shift;
173
174
    if ( exists $bib_heading_fields->{$tag} ) {
175
        return 1 if ($bib_heading_fields->{$tag}->{subfields} =~ /$subfield/);
176
    }
177
    return 0;
178
}
179
164
=head2 parse_heading
180
=head2 parse_heading
165
181
166
=cut
182
=cut
(-)a/C4/Items.pm (-1 / +5 lines)
Lines 18-23 package C4::Items; Link Here
18
# You should have received a copy of the GNU General Public License
18
# You should have received a copy of the GNU General Public License
19
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
# along with Koha; if not, see <http://www.gnu.org/licenses>.
20
20
21
use Modern::Perl;
21
use strict;
22
use strict;
22
#use warnings; FIXME - Bug 2505
23
#use warnings; FIXME - Bug 2505
23
24
Lines 2263-2269 sub GetAnalyticsCount { Link Here
2263
    ### ZOOM search here
2264
    ### ZOOM search here
2264
    my $query;
2265
    my $query;
2265
    $query= "hi=".$itemnumber;
2266
    $query= "hi=".$itemnumber;
2266
    my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
2267
    # Use state variables to avoid recreating the objects every time.
2268
    # With Elasticsearch this also avoids creating a massive amount of
2269
    # ES connectors that would eventually run out of file descriptors.
2270
    state $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
2267
    my ($err,$res,$result) = $searcher->simple_search_compat($query,0,10);
2271
    my ($err,$res,$result) = $searcher->simple_search_compat($query,0,10);
2268
    return ($result);
2272
    return ($result);
2269
}
2273
}
(-)a/C4/Matcher.pm (-3 / +9 lines)
Lines 664-670 sub get_matches { Link Here
664
                    #NOTE: double-quote the values so you don't get a "Embedded truncation not supported" error when a term has a ? in it.
664
                    #NOTE: double-quote the values so you don't get a "Embedded truncation not supported" error when a term has a ? in it.
665
            }
665
            }
666
666
667
            my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
667
            # Use state variables to avoid recreating the objects every time.
668
            # With Elasticsearch this also avoids creating a massive amount of
669
            # ES connectors that would eventually run out of file descriptors.
670
            state $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
668
            ( $error, $searchresults, $total_hits ) =
671
            ( $error, $searchresults, $total_hits ) =
669
              $searcher->simple_search_compat( $query, 0, $max_matches, undef, skip_normalize => 1 );
672
              $searcher->simple_search_compat( $query, 0, $max_matches, undef, skip_normalize => 1 );
670
673
Lines 703-710 sub get_matches { Link Here
703
                push @operator, 'exact';
706
                push @operator, 'exact';
704
                push @value,    $key;
707
                push @value,    $key;
705
            }
708
            }
706
            my $builder  = Koha::SearchEngine::QueryBuilder->new({index => $Koha::SearchEngine::AUTHORITIES_INDEX});
709
            # Use state variables to avoid recreating the objects every time.
707
            my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::AUTHORITIES_INDEX});
710
            # With Elasticsearch this also avoids creating a massive amount of
711
            # ES connectors that would eventually run out of file descriptors.
712
            state $builder  = Koha::SearchEngine::QueryBuilder->new({index => $Koha::SearchEngine::AUTHORITIES_INDEX});
713
            state $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::AUTHORITIES_INDEX});
708
            my $search_query = $builder->build_authorities_query_compat(
714
            my $search_query = $builder->build_authorities_query_compat(
709
                \@marclist, \@and_or, \@excluding, \@operator,
715
                \@marclist, \@and_or, \@excluding, \@operator,
710
                \@value, undef, 'AuthidAsc'
716
                \@value, undef, 'AuthidAsc'
(-)a/Koha/SearchEngine/Elasticsearch/Indexer.pm (-9 / +21 lines)
Lines 58-64 If that's a problem, clone them first. Link Here
58
=cut
58
=cut
59
59
60
sub update_index {
60
sub update_index {
61
    my ($self, $biblionums, $records) = @_;
61
    my ($self, $biblionums, $records, $commit) = @_;
62
62
63
    # TODO should have a separate path for dealing with a large number
63
    # TODO should have a separate path for dealing with a large number
64
    # of records at once where we use the bulk update functions in ES.
64
    # of records at once where we use the bulk update functions in ES.
Lines 80-86 sub update_index { Link Here
80
80
81
    #print Data::Dumper::Dumper( $from->to_array );
81
    #print Data::Dumper::Dumper( $from->to_array );
82
    $self->store->bag->add_many($from);
82
    $self->store->bag->add_many($from);
83
    $self->store->bag->commit;
83
    if ( !defined $commit || $commit ) {
84
        $self->store->bag->commit;
85
    } else {
86
        # TODO: nicer way to do this
87
        $self->store->bag->_bulk->flush;
88
    }
84
    return 1;
89
    return 1;
85
}
90
}
86
91
Lines 98-105 it to be updated by a regular index cron job in the future. Link Here
98
=cut
103
=cut
99
104
100
sub update_index_background {
105
sub update_index_background {
101
    my $self = shift;
106
    my ($self, $biblionums, $records) = @_;
102
    $self->update_index(@_);
107
    $self->update_index($biblionums, $records, 0);
103
}
108
}
104
109
105
=head2 $indexer->delete_index($biblionums)
110
=head2 $indexer->delete_index($biblionums)
Lines 181-187 sub _sanitise_records { Link Here
181
        # tears in rain...
186
        # tears in rain...
182
        if ( $rec ) {
187
        if ( $rec ) {
183
            $rec->delete_fields($rec->field('999'));
188
            $rec->delete_fields($rec->field('999'));
184
            $rec->append_fields(MARC::Field->new('999','','','c' => $bibnum, 'd' => $bibnum));
189
            # Make sure biblionumber is a string. Elasticsearch would consider int and string different IDs.
190
            $rec->append_fields(MARC::Field->new('999','','','c' => "" . $bibnum, 'd' => "" . $bibnum));
185
        }
191
        }
186
    }
192
    }
187
}
193
}
Lines 189-199 sub _sanitise_records { Link Here
189
sub _convert_marc_to_json {
195
sub _convert_marc_to_json {
190
    my $self    = shift;
196
    my $self    = shift;
191
    my $records = shift;
197
    my $records = shift;
198
199
    # Use state to speed up repeated calls in batch processes
200
    state %fixers;
201
    if ( !defined $fixers{$self->index} ) {
202
        $fixers{$self->index} = Catmandu::Fix->new( fixes => $self->get_fixer_rules() );
203
    }
204
192
    my $importer =
205
    my $importer =
193
      Catmandu::Importer::MARC->new( records => $records, id => '999c' );
206
        Catmandu::Importer::MARC->new( records => $records, id => '999c' );
194
    my $fixer = Catmandu::Fix->new( fixes => $self->get_fixer_rules() );
207
195
    $importer = $fixer->fix($importer);
208
    return $fixers{$self->index}->fix($importer);
196
    return $importer;
197
}
209
}
198
210
199
1;
211
1;
(-)a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm (-26 / +35 lines)
Lines 102-111 sub build_query { Link Here
102
              if $d && ( $d ne 'asc' && $d ne 'desc' );
102
              if $d && ( $d ne 'asc' && $d ne 'desc' );
103
            $d = 'asc' unless $d;
103
            $d = 'asc' unless $d;
104
104
105
            # TODO account for fields that don't have a 'phrase' type
106
107
            $f = $self->_sort_field($f);
105
            $f = $self->_sort_field($f);
108
            push @{ $res->{sort} }, { "$f.phrase" => { order => $d } };
106
            push @{ $res->{sort} }, { $f => { order => $d } };
109
        }
107
        }
110
    }
108
    }
111
109
Lines 171-177 sub build_browse_query { Link Here
171
                }
169
                }
172
            }
170
            }
173
        },
171
        },
174
        sort => [ { "$sort.phrase" => { order => "asc" } } ],
172
        sort => [ { $sort => { order => "asc" } } ],
175
    };
173
    };
176
}
174
}
177
175
Lines 294-300 sub build_authorities_query { Link Here
294
    foreach my $s ( @{ $search->{searches} } ) {
292
    foreach my $s ( @{ $search->{searches} } ) {
295
        my ( $wh, $op, $val ) = @{$s}{qw(where operator value)};
293
        my ( $wh, $op, $val ) = @{$s}{qw(where operator value)};
296
        $wh = '_all' if $wh eq '';
294
        $wh = '_all' if $wh eq '';
297
        if ( $op eq 'is' || $op eq '=' ) {
295
        if ( $op eq 'is' || $op eq '='  || $op eq 'exact' ) {
298
296
299
            # look for something that matches completely
297
            # look for something that matches completely
300
            # note, '=' is about numerical vals. May need special handling.
298
            # note, '=' is about numerical vals. May need special handling.
Lines 302-313 sub build_authorities_query { Link Here
302
            # matches. Also, we lowercase our search because the ES
300
            # matches. Also, we lowercase our search because the ES
303
            # index lowercases its values, and term searches don't get the
301
            # index lowercases its values, and term searches don't get the
304
            # search analyzer applied to them.
302
            # search analyzer applied to them.
305
            push @filter_parts, { term => { "$wh.phrase" => lc $val } };
303
            push @query_parts, { match_phrase => { "$wh.phrase" => lc $val } };
306
        }
307
        elsif ( $op eq 'exact' ) {
308
309
            # left and right truncation, otherwise an exact phrase
310
            push @query_parts, { match_phrase => { $wh => $val } };
311
        }
304
        }
312
        elsif ( $op eq 'start' ) {
305
        elsif ( $op eq 'start' ) {
313
306
Lines 316-337 sub build_authorities_query { Link Here
316
        }
309
        }
317
        else {
310
        else {
318
            # regular wordlist stuff
311
            # regular wordlist stuff
319
            push @query_parts, { match => { $wh => $val } };
312
            push @query_parts, { match => { $wh => { query => $val, operator => 'AND' } } };
320
        }
313
        }
321
    }
314
    }
322
315
316
    # Add authtype if specified
317
    if (defined $search->{authtypecode} && $search->{authtypecode}) {
318
        push @query_parts, { match => { authtype => $search->{authtypecode} } };
319
    }
320
323
    # Merge the query and filter parts appropriately
321
    # Merge the query and filter parts appropriately
324
    # 'should' behaves like 'or', if we want 'and', use 'must'
322
    # 'should' behaves like 'or', if we want 'and', use 'must'
325
    my $query_part  = { bool => { should => \@query_parts } };
323
    my $query_part  = { bool => { must => \@query_parts } };
326
    my $filter_part = { bool => { should => \@filter_parts } };
324
    my $filter_part = { bool => { must => \@filter_parts } };
327
325
328
    # We need to add '.phrase' to all the sort headings otherwise it'll sort
329
    # based on the tokenised form.
330
    my %s;
326
    my %s;
331
    if ( exists $search->{sort} ) {
327
    if ( exists $search->{sort} ) {
332
        foreach my $k ( keys %{ $search->{sort} } ) {
328
        foreach my $k ( keys %{ $search->{sort} } ) {
333
            my $f = $self->_sort_field($k);
329
            my $f = $self->_sort_field($k);
334
            $s{"$f.phrase"} = $search->{sort}{$k};
330
            $s{$f} = $search->{sort}{$k};
335
        }
331
        }
336
        $search->{sort} = \%s;
332
        $search->{sort} = \%s;
337
    }
333
    }
Lines 383-391 Also ignored. Link Here
383
379
384
=item operator
380
=item operator
385
381
386
What form of search to do. Options are: is (phrase, no trunction, whole field
382
What form of search to do. Options are: is (phrase, no truncation, whole field
387
must match), = (number exact match), exact (phrase, but with left and right
383
must match), = (number exact match), exact (phrase, no truncation, whole field
388
truncation). If left blank, then word list, right truncted, anywhere is used.
384
must match). If left blank, then word list, right truncated, anywhere is used.
389
385
390
=item value
386
=item value
391
387
Lines 417-423 our $koha_to_index_name = { Link Here
417
    'match-heading' => 'Match-heading',
413
    'match-heading' => 'Match-heading',
418
    'see-from'      => 'Match-heading-see-from',
414
    'see-from'      => 'Match-heading-see-from',
419
    thesaurus       => 'Subject-heading-thesaurus',
415
    thesaurus       => 'Subject-heading-thesaurus',
420
    all              => ''
416
    any             => '',
417
    all             => ''
421
};
418
};
422
419
423
sub build_authorities_query_compat {
420
sub build_authorities_query_compat {
Lines 435-453 sub build_authorities_query_compat { Link Here
435
            unless exists $koha_to_index_name->{$m};
432
            unless exists $koha_to_index_name->{$m};
436
    }
433
    }
437
    for ( my $i = 0 ; $i < @$value ; $i++ ) {
434
    for ( my $i = 0 ; $i < @$value ; $i++ ) {
438
        next unless $value->[$i]; #clean empty form values, ES doesn't like undefined searches
435
        next unless defined $value->[$i] && $value->[$i]; #clean empty form values, ES doesn't like undefined searches
436
        my $escaped_value = $value->[$i];
437
        $escaped_value =~ s/(["()\\])/\\$1/g;
439
        push @searches,
438
        push @searches,
440
          {
439
        {
441
            where    => $koha_to_index_name->{$marclist->[$i]},
440
            where    => $koha_to_index_name->{$marclist->[$i]},
442
            operator => $operator->[$i],
441
            operator => $operator->[$i],
443
            value    => $value->[$i],
442
            value    => $escaped_value,
444
          };
443
        };
445
    }
444
    }
446
445
447
    my %sort;
446
    my %sort;
448
    my $sort_field =
447
    my $sort_field =
449
        ( $orderby =~ /^Heading/ ) ? 'Heading'
448
        ( $orderby =~ /^Heading/ ) ? 'Heading'
450
      : ( $orderby =~ /^Auth/ )    ? 'Local-Number'
449
      : ( $orderby =~ /^Auth/ )    ? 'Local-number'
451
      :                              undef;
450
      :                              undef;
452
    if ($sort_field) {
451
    if ($sort_field) {
453
        my $sort_order = ( $orderby =~ /Asc$/ ) ? 'asc' : 'desc';
452
        my $sort_order = ( $orderby =~ /Asc$/ ) ? 'asc' : 'desc';
Lines 779-786 the end. Maybe it'll be something else in the future, who knows? Link Here
779
778
780
sub _sort_field {
779
sub _sort_field {
781
    my ($self, $f) = @_;
780
    my ($self, $f) = @_;
781
782
    my $mappings = $self->get_elasticsearch_mappings();
783
    my $textField = defined $mappings->{data}{properties}{$f}{type} && $mappings->{data}{properties}{$f}{type} eq 'text';
782
    if ($self->sort_fields()->{$f}) {
784
    if ($self->sort_fields()->{$f}) {
783
        $f .= '__sort';
785
        $f .= '__sort';
786
        # We need to add '.phrase' to text fields, otherwise it'll sort
787
        # based on the tokenised form.
788
        $f .= '.phrase' if $textField;
789
    } else {
790
        # We need to add '.raw' to text fields without a sort field, 
791
        # otherwise it'll sort based on the tokenised form.
792
        $f .= '.raw' if $textField;
784
    }
793
    }
785
    return $f;
794
    return $f;
786
}
795
}
(-)a/Koha/SearchEngine/Elasticsearch/Search.pm (-38 / +45 lines)
Lines 176-182 sub search_compat { Link Here
176
=head2 search_auth_compat
176
=head2 search_auth_compat
177
177
178
    my ( $results, $total ) =
178
    my ( $results, $total ) =
179
      $searcher->search_auth_compat( $query, $page, $count, %options );
179
      $searcher->search_auth_compat( $query, $offset, $count, $skipmetadata, %options );
180
180
181
This has a similar calling convention to L<search>, however it returns its
181
This has a similar calling convention to L<search>, however it returns its
182
results in a form the same as L<C4::AuthoritiesMarc::SearchAuthorities>.
182
results in a form the same as L<C4::AuthoritiesMarc::SearchAuthorities>.
Lines 184-202 results in a form the same as L<C4::AuthoritiesMarc::SearchAuthorities>. Link Here
184
=cut
184
=cut
185
185
186
sub search_auth_compat {
186
sub search_auth_compat {
187
    my $self = shift;
187
    my ($self, $query, $offset, $count, $skipmetadata, %options) = @_;
188
189
    if ( !defined $offset or $offset <= 0 ) {
190
        $offset = 1;
191
    }
192
    # Uh, authority search uses 1-based offset..
193
    $options{offset} = $offset - 1;
188
194
189
    # TODO handle paging
190
    my $database = Koha::Database->new();
195
    my $database = Koha::Database->new();
191
    my $schema   = $database->schema();
196
    my $schema   = $database->schema();
192
    my $res      = $self->search(@_);
197
    my $res      = $self->search($query, undef, $count, %options);
198
193
    my $bib_searcher = Koha::SearchEngine::Elasticsearch::Search->new({index => 'biblios'});
199
    my $bib_searcher = Koha::SearchEngine::Elasticsearch::Search->new({index => 'biblios'});
194
    my @records;
200
    my @records;
195
    $res->each(
201
    $res->each(
196
        sub {
202
        sub {
197
            my %result;
203
            my %result;
198
            my $record    = $_[0];
204
            my $record = $_[0];
199
            my $marc_json = $record->{record};
200
205
201
            # I wonder if these should be real values defined in the mapping
206
            # I wonder if these should be real values defined in the mapping
202
            # rather than hard-coded conversions.
207
            # rather than hard-coded conversions.
Lines 205-244 sub search_auth_compat { Link Here
205
            my $authid = $record->{ 'Local-number' }[0][0];
210
            my $authid = $record->{ 'Local-number' }[0][0];
206
            $result{authid} = $authid;
211
            $result{authid} = $authid;
207
212
208
            # TODO put all this info into the record at index time so we
213
            if (!defined $skipmetadata || !$skipmetadata) {
209
            # don't have to go and sort it all out now.
214
                # TODO put all this info into the record at index time so we
210
            my $authtypecode = $record->{authtype};
215
                # don't have to go and sort it all out now.
211
            my $rs           = $schema->resultset('AuthType')
216
                my $authtypecode = $record->{authtype};
212
              ->search( { authtypecode => $authtypecode } );
217
                my $rs           = $schema->resultset('AuthType')
213
218
                ->search( { authtypecode => $authtypecode } );
214
            # FIXME there's an assumption here that we will get a result.
219
215
            # the original code also makes an assumption that some provided
220
                # FIXME there's an assumption here that we will get a result.
216
            # authtypecode may sometimes be used instead of the one stored
221
                # the original code also makes an assumption that some provided
217
            # with the record. It's not documented why this is the case, so
222
                # authtypecode may sometimes be used instead of the one stored
218
            # it's not reproduced here yet.
223
                # with the record. It's not documented why this is the case, so
219
            my $authtype           = $rs->single;
224
                # it's not reproduced here yet.
220
            my $auth_tag_to_report = $authtype->auth_tag_to_report;
225
                my $authtype           = $rs->single;
221
            my $marc               = $self->json2marc($marc_json);
226
                my $auth_tag_to_report = $authtype->auth_tag_to_report;
222
            my $mainentry          = $marc->field($auth_tag_to_report);
227
                my $marc               = $self->json2marc($record->{record});
223
            my $reported_tag;
228
                my $mainentry          = $marc->field($auth_tag_to_report);
224
            if ($mainentry) {
229
                my $reported_tag;
225
                foreach ( $mainentry->subfields() ) {
230
                if ($mainentry) {
226
                    $reported_tag .= '$' . $_->[0] . $_->[1];
231
                    foreach ( $mainentry->subfields() ) {
232
                        $reported_tag .= '$' . $_->[0] . $_->[1];
233
                    }
227
                }
234
                }
235
                # Turn the resultset into a hash
236
                my %authtype_cols;
237
                foreach my $col ($authtype->result_source->columns) {
238
                    $authtype_cols{$col} = $authtype->get_column($col);
239
                }
240
                $result{authtype}     = $authtype->authtypetext;
241
                $result{reported_tag} = $reported_tag;
242
243
                # Reimplementing BuildSummary is out of scope because it'll be hard
244
                $result{summary} =
245
                C4::AuthoritiesMarc::BuildSummary( $marc, $result{authid},
246
                    $authtypecode );
247
                $result{used} = $self->count_auth_use($bib_searcher, $authid);
228
            }
248
            }
229
            # Turn the resultset into a hash
230
            my %authtype_cols;
231
            foreach my $col ($authtype->result_source->columns) {
232
                $authtype_cols{$col} = $authtype->get_column($col);
233
            }
234
            $result{authtype}     = $authtype->authtypetext;
235
            $result{reported_tag} = $reported_tag;
236
237
            # Reimplementing BuildSummary is out of scope because it'll be hard
238
            $result{summary} =
239
              C4::AuthoritiesMarc::BuildSummary( $marc, $result{authid},
240
                $authtypecode );
241
            $result{used} = $self->count_auth_use($bib_searcher, $authid);
242
            push @records, \%result;
249
            push @records, \%result;
243
        }
250
        }
244
    );
251
    );
(-)a/Koha/SearchEngine/Zebra/Search.pm (-2 / +2 lines)
Lines 100-111 This passes the search query on to C4::AuthoritiesMarc::SearchAuthorities Link Here
100
=cut
100
=cut
101
101
102
sub search_auth_compat {
102
sub search_auth_compat {
103
    my ( $self, $q, $startfrom, $resperpage ) = @_;
103
    my ( $self, $q, $startfrom, $resperpage, $skipmetadata ) = @_;
104
104
105
    my @params = (
105
    my @params = (
106
        @{$q}{ 'marclist', 'and_or', 'excluding', 'operator', 'value' },
106
        @{$q}{ 'marclist', 'and_or', 'excluding', 'operator', 'value' },
107
        $startfrom - 1,
107
        $startfrom - 1,
108
        $resperpage, @{$q}{ 'authtypecode', 'orderby' }
108
        $resperpage, @{$q}{ 'authtypecode', 'orderby' }, $skipmetadata
109
    );
109
    );
110
    C4::AuthoritiesMarc::SearchAuthorities(@params);
110
    C4::AuthoritiesMarc::SearchAuthorities(@params);
111
}
111
}
(-)a/misc/link_bibs_to_authorities.pl (-6 / +10 lines)
Lines 195-203 sub process_bib { Link Here
195
        return;
195
        return;
196
    }
196
    }
197
197
198
    my $frameworkcode = GetFrameworkCode($biblionumber);
199
198
    my ( $headings_changed, $results ) =
200
    my ( $headings_changed, $results ) =
199
      LinkBibHeadingsToAuthorities( $linker, $bib,
201
      LinkBibHeadingsToAuthorities( $linker, $bib, $frameworkcode );
200
        GetFrameworkCode($biblionumber) );
201
    foreach my $key ( keys %{ $results->{'unlinked'} } ) {
202
    foreach my $key ( keys %{ $results->{'unlinked'} } ) {
202
        $unlinked_headings{$key} += $results->{'unlinked'}->{$key};
203
        $unlinked_headings{$key} += $results->{'unlinked'}->{$key};
203
    }
204
    }
Lines 211-221 sub process_bib { Link Here
211
    if ($headings_changed) {
212
    if ($headings_changed) {
212
        if ($verbose) {
213
        if ($verbose) {
213
            my $title = substr( $bib->title, 0, 20 );
214
            my $title = substr( $bib->title, 0, 20 );
214
            print
215
            printf(
215
"Bib $biblionumber ($title): $headings_changed headings changed\n";
216
                "Bib %12d (%-20s): %3d headings changed\n",
217
                $biblionumber,
218
                $title,
219
                $headings_changed
220
            );
216
        }
221
        }
217
        if ( not $test_only ) {
222
        if ( not $test_only ) {
218
            ModBiblio( $bib, $biblionumber, GetFrameworkCode($biblionumber) );
223
            ModBiblio( $bib, $biblionumber, $frameworkcode );
219
            $num_bibs_modified++;
224
            $num_bibs_modified++;
220
        }
225
        }
221
    }
226
    }
222
- 

Return to bug 19365