|
Lines 42-47
use MARC::File::XML;
Link Here
|
| 42 |
use MIME::Base64; |
42 |
use MIME::Base64; |
| 43 |
use Encode qw(encode); |
43 |
use Encode qw(encode); |
| 44 |
use Business::ISBN; |
44 |
use Business::ISBN; |
|
|
45 |
use Scalar::Util qw(looks_like_number); |
| 45 |
|
46 |
|
| 46 |
__PACKAGE__->mk_ro_accessors(qw( index )); |
47 |
__PACKAGE__->mk_ro_accessors(qw( index )); |
| 47 |
__PACKAGE__->mk_accessors(qw( sort_fields )); |
48 |
__PACKAGE__->mk_accessors(qw( sort_fields )); |
|
Lines 346-352
sub sort_fields {
Link Here
|
| 346 |
return $self->_sort_fields_accessor(); |
347 |
return $self->_sort_fields_accessor(); |
| 347 |
} |
348 |
} |
| 348 |
|
349 |
|
| 349 |
=head2 _process_mappings($mappings, $data, $record_document, $altscript) |
350 |
=head2 _process_mappings($mappings, $data, $record_document, $meta) |
| 350 |
|
351 |
|
| 351 |
$self->_process_mappings($mappings, $marc_field_data, $record_document, 0) |
352 |
$self->_process_mappings($mappings, $marc_field_data, $record_document, 0) |
| 352 |
|
353 |
|
|
Lines 374-396
The source data from a MARC record field.
Link Here
|
| 374 |
Hashref representing the Elasticsearch document on which mappings should be |
375 |
Hashref representing the Elasticsearch document on which mappings should be |
| 375 |
applied. |
376 |
applied. |
| 376 |
|
377 |
|
| 377 |
=item C<$altscript> |
378 |
=item C<$meta> |
| 378 |
|
379 |
|
| 379 |
A boolean value indicating whether an alternate script presentation is being |
380 |
A hashref containing metadata useful for enforcing per mapping rules. For |
|
|
381 |
example for providing extra context for mapping options, or treating mapping |
| 382 |
targets differently depending on type (sort, search, facet etc). Combining |
| 383 |
this metadata with the mapping options and metadata allows us to mutate the |
| 384 |
data per mapping, or even replace it with other data retrieved from the |
| 385 |
metadata context. |
| 386 |
|
| 387 |
Current properties are: |
| 388 |
|
| 389 |
C<altscript>: A boolean value indicating whether an alternate script presentation is being |
| 380 |
processed. |
390 |
processed. |
| 381 |
|
391 |
|
|
|
392 |
C<data_source>: The source of the $<data> argument. Possible values are: 'leader', 'control_field', |
| 393 |
'subfield' or 'subfields_group'. |
| 394 |
|
| 395 |
C<code>: The code of the subfield C<$data> was retrieved, if C<data_source> is 'subfield'. |
| 396 |
|
| 397 |
C<codes>: Subfield codes of the subfields group from which C<$data> was retrieved, if C<data_source> |
| 398 |
is 'subfields_group'. |
| 399 |
|
| 400 |
C<field>: The original C<MARC::Record> object. |
| 401 |
|
| 382 |
=back |
402 |
=back |
| 383 |
|
403 |
|
| 384 |
=cut |
404 |
=cut |
| 385 |
|
405 |
|
| 386 |
sub _process_mappings { |
406 |
sub _process_mappings { |
| 387 |
my ($_self, $mappings, $data, $record_document, $altscript) = @_; |
407 |
my ($_self, $mappings, $data, $record_document, $meta) = @_; |
| 388 |
foreach my $mapping (@{$mappings}) { |
408 |
foreach my $mapping (@{$mappings}) { |
| 389 |
my ($target, $options) = @{$mapping}; |
409 |
my ($target, $options) = @{$mapping}; |
| 390 |
|
410 |
|
| 391 |
# Don't process sort fields for alternate scripts |
411 |
# Don't process sort fields for alternate scripts |
| 392 |
my $sort = $target =~ /__sort$/; |
412 |
my $sort = $target =~ /__sort$/; |
| 393 |
if ($sort && $altscript) { |
413 |
if ($sort && $meta->{altscript}) { |
| 394 |
next; |
414 |
next; |
| 395 |
} |
415 |
} |
| 396 |
|
416 |
|
|
Lines 411-416
sub _process_mappings {
Link Here
|
| 411 |
$options->{property} => $_data |
431 |
$options->{property} => $_data |
| 412 |
} |
432 |
} |
| 413 |
} |
433 |
} |
|
|
434 |
if (defined $options->{nonfiling_characters_indicator}) { |
| 435 |
my $nonfiling_chars = $meta->{field}->indicator($options->{nonfiling_characters_indicator}); |
| 436 |
$nonfiling_chars = looks_like_number($nonfiling_chars) ? int($nonfiling_chars) : 0; |
| 437 |
if ($nonfiling_chars) { |
| 438 |
$_data = substr $_data, $nonfiling_chars; |
| 439 |
} |
| 440 |
} |
| 414 |
push @{$record_document->{$target}}, $_data; |
441 |
push @{$record_document->{$target}}, $_data; |
| 415 |
} |
442 |
} |
| 416 |
} |
443 |
} |
|
Lines 448-460
sub marc_records_to_documents {
Link Here
|
| 448 |
my $record_document = {}; |
475 |
my $record_document = {}; |
| 449 |
my $mappings = $rules->{leader}; |
476 |
my $mappings = $rules->{leader}; |
| 450 |
if ($mappings) { |
477 |
if ($mappings) { |
| 451 |
$self->_process_mappings($mappings, $record->leader(), $record_document, 0); |
478 |
$self->_process_mappings($mappings, $record->leader(), $record_document, { |
|
|
479 |
altscript => 0, |
| 480 |
data_source => 'leader' |
| 481 |
} |
| 482 |
); |
| 452 |
} |
483 |
} |
| 453 |
foreach my $field ($record->fields()) { |
484 |
foreach my $field ($record->fields()) { |
| 454 |
if ($field->is_control_field()) { |
485 |
if ($field->is_control_field()) { |
| 455 |
my $mappings = $control_fields_rules->{$field->tag()}; |
486 |
my $mappings = $control_fields_rules->{$field->tag()}; |
| 456 |
if ($mappings) { |
487 |
if ($mappings) { |
| 457 |
$self->_process_mappings($mappings, $field->data(), $record_document, 0); |
488 |
$self->_process_mappings($mappings, $field->data(), $record_document, { |
|
|
489 |
altscript => 0, |
| 490 |
data_source => 'control_field', |
| 491 |
field => $field |
| 492 |
} |
| 493 |
); |
| 458 |
} |
494 |
} |
| 459 |
} |
495 |
} |
| 460 |
else { |
496 |
else { |
|
Lines 480-486
sub marc_records_to_documents {
Link Here
|
| 480 |
$mappings = [@{$mappings}, @{$wildcard_mappings}]; |
516 |
$mappings = [@{$mappings}, @{$wildcard_mappings}]; |
| 481 |
} |
517 |
} |
| 482 |
if (@{$mappings}) { |
518 |
if (@{$mappings}) { |
| 483 |
$self->_process_mappings($mappings, $data, $record_document, $altscript); |
519 |
$self->_process_mappings($mappings, $data, $record_document, { |
|
|
520 |
altscript => $altscript, |
| 521 |
data_source => 'subfield', |
| 522 |
code => $code, |
| 523 |
field => $field |
| 524 |
} |
| 525 |
); |
| 484 |
} |
526 |
} |
| 485 |
if ( defined @{$mappings}[0] && grep /match-heading/, @{@{$mappings}[0]} ){ |
527 |
if ( defined @{$mappings}[0] && grep /match-heading/, @{@{$mappings}[0]} ){ |
| 486 |
# Used by the authority linker the match-heading field requires a specific syntax |
528 |
# Used by the authority linker the match-heading field requires a specific syntax |
|
Lines 503-509
sub marc_records_to_documents {
Link Here
|
| 503 |
) |
545 |
) |
| 504 |
); |
546 |
); |
| 505 |
if ($data) { |
547 |
if ($data) { |
| 506 |
$self->_process_mappings($subfields_join_mappings->{$subfields_group}, $data, $record_document, $altscript); |
548 |
$self->_process_mappings($subfields_join_mappings->{$subfields_group}, $data, $record_document, { |
|
|
549 |
altscript => $altscript, |
| 550 |
data_source => 'subfields_group', |
| 551 |
codes => $subfields_group, |
| 552 |
field => $field |
| 553 |
} |
| 554 |
); |
| 507 |
} |
555 |
} |
| 508 |
if ( grep { $_->[0] eq 'match-heading' } @{$subfields_join_mappings->{$subfields_group}} ){ |
556 |
if ( grep { $_->[0] eq 'match-heading' } @{$subfields_join_mappings->{$subfields_group}} ){ |
| 509 |
# Used by the authority linker the match-heading field requires a specific syntax |
557 |
# Used by the authority linker the match-heading field requires a specific syntax |
|
Lines 799-805
sub _field_mappings {
Link Here
|
| 799 |
push @{$mapping}, {%{$default_options}, property => 'input'}; |
847 |
push @{$mapping}, {%{$default_options}, property => 'input'}; |
| 800 |
} |
848 |
} |
| 801 |
else { |
849 |
else { |
| 802 |
push @{$mapping}, $default_options; |
850 |
# Important! Make shallow clone, or we end up with the same hashref |
|
|
851 |
# shared by all mappings |
| 852 |
push @{$mapping}, {%{$default_options}}; |
| 803 |
} |
853 |
} |
| 804 |
push @mappings, $mapping; |
854 |
push @mappings, $mapping; |
| 805 |
} |
855 |
} |
|
Lines 934-939
sub _get_marc_mapping_rules {
Link Here
|
| 934 |
); |
984 |
); |
| 935 |
} |
985 |
} |
| 936 |
}); |
986 |
}); |
|
|
987 |
|
| 988 |
# Marc-flavour specific rule tweaks, could/should also provide hook for this |
| 989 |
if ($marcflavour eq 'marc21') { |
| 990 |
# Nonfiling characters processing for sort fields |
| 991 |
my %title_fields; |
| 992 |
if ($self->index eq $Koha::SearchEngine::BIBLIOS_INDEX) { |
| 993 |
# Format is: nonfiling characters indicator => field names list |
| 994 |
%title_fields = ( |
| 995 |
1 => [630, 730, 740], |
| 996 |
2 => [130, 222, 240, 242, 243, 440, 830] |
| 997 |
); |
| 998 |
} |
| 999 |
elsif ($self->index eq $Koha::SearchEngine::AUTHORITIES_INDEX) { |
| 1000 |
%title_fields = ( |
| 1001 |
1 => [730], |
| 1002 |
2 => [130, 430, 530] |
| 1003 |
); |
| 1004 |
} |
| 1005 |
foreach my $indicator (keys %title_fields) { |
| 1006 |
foreach my $field_tag (@{$title_fields{$indicator}}) { |
| 1007 |
my $mappings = $rules->{data_fields}->{$field_tag}->{subfields}->{a} // []; |
| 1008 |
foreach my $mapping (@{$mappings}) { |
| 1009 |
if ($mapping->[0] =~ /__sort$/) { |
| 1010 |
# Mark this as to be processed for nonfiling characters indicator |
| 1011 |
# later on in _process_mappings |
| 1012 |
$mapping->[1]->{nonfiling_characters_indicator} = $indicator; |
| 1013 |
} |
| 1014 |
} |
| 1015 |
} |
| 1016 |
} |
| 1017 |
} |
| 1018 |
|
| 937 |
return $rules; |
1019 |
return $rules; |
| 938 |
} |
1020 |
} |
| 939 |
|
1021 |
|