|
Lines 112-124
sub build_query {
Link Here
|
| 112 |
# See _convert_facets in Search.pm for how these get turned into |
112 |
# See _convert_facets in Search.pm for how these get turned into |
| 113 |
# things that Koha can use. |
113 |
# things that Koha can use. |
| 114 |
$res->{aggregations} = { |
114 |
$res->{aggregations} = { |
| 115 |
author => { terms => { field => "author__facet" } }, |
115 |
author => { terms => { field => "author__facet" } }, |
| 116 |
subject => { terms => { field => "subject__facet" } }, |
116 |
subject => { terms => { field => "subject__facet" } }, |
| 117 |
itype => { terms => { field => "itype__facet" } }, |
117 |
itype => { terms => { field => "itype__facet" } }, |
| 118 |
location => { terms => { field => "location__facet" } }, |
118 |
location => { terms => { field => "location__facet" } }, |
| 119 |
'su-geo' => { terms => { field => "su-geo__facet" } }, |
119 |
'su-geo' => { terms => { field => "su-geo__facet" } }, |
| 120 |
se => { terms => { field => "se__facet" } }, |
120 |
'title-series' => { terms => { field => "title-series__facet" } }, |
| 121 |
ccode => { terms => { field => "ccode__facet" } }, |
121 |
ccode => { terms => { field => "ccode__facet" } }, |
| 122 |
}; |
122 |
}; |
| 123 |
|
123 |
|
| 124 |
my $display_library_facets = C4::Context->preference('DisplayLibraryFacets'); |
124 |
my $display_library_facets = C4::Context->preference('DisplayLibraryFacets'); |
|
Lines 411-422
appropriate search object.
Link Here
|
| 411 |
=cut |
411 |
=cut |
| 412 |
|
412 |
|
| 413 |
our $koha_to_index_name = { |
413 |
our $koha_to_index_name = { |
| 414 |
mainmainentry => 'Heading-Main', |
414 |
mainmainentry => 'heading-main', |
| 415 |
mainentry => 'Heading', |
415 |
mainentry => 'heading', |
| 416 |
match => 'Match', |
416 |
match => 'match', |
| 417 |
'match-heading' => 'Match-heading', |
417 |
'match-heading' => 'match-heading', |
| 418 |
'see-from' => 'Match-heading-see-from', |
418 |
'see-from' => 'match-heading-see-from', |
| 419 |
thesaurus => 'Subject-heading-thesaurus', |
419 |
thesaurus => 'subject-heading-thesaurus', |
| 420 |
all => '' |
420 |
all => '' |
| 421 |
}; |
421 |
}; |
| 422 |
|
422 |
|
|
Lines 429-434
sub build_authorities_query_compat {
Link Here
|
| 429 |
# extensible hash form that is understood by L<build_authorities_query>. |
429 |
# extensible hash form that is understood by L<build_authorities_query>. |
| 430 |
my @searches; |
430 |
my @searches; |
| 431 |
|
431 |
|
|
|
432 |
# Convert to lower case |
| 433 |
$marclist = [map(lc, @{$marclist})]; |
| 434 |
|
| 432 |
# Make sure everything exists |
435 |
# Make sure everything exists |
| 433 |
foreach my $m (@$marclist) { |
436 |
foreach my $m (@$marclist) { |
| 434 |
Koha::Exceptions::WrongParameter->throw("Invalid marclist field provided: $m") |
437 |
Koha::Exceptions::WrongParameter->throw("Invalid marclist field provided: $m") |
|
Lines 445-452
sub build_authorities_query_compat {
Link Here
|
| 445 |
|
448 |
|
| 446 |
my %sort; |
449 |
my %sort; |
| 447 |
my $sort_field = |
450 |
my $sort_field = |
| 448 |
( $orderby =~ /^Heading/ ) ? 'Heading' |
451 |
( $orderby =~ /^heading/ ) ? 'heading' |
| 449 |
: ( $orderby =~ /^Auth/ ) ? 'Local-Number' |
452 |
: ( $orderby =~ /^auth/ ) ? 'local-number' |
| 450 |
: undef; |
453 |
: undef; |
| 451 |
if ($sort_field) { |
454 |
if ($sort_field) { |
| 452 |
my $sort_order = ( $orderby =~ /Asc$/ ) ? 'asc' : 'desc'; |
455 |
my $sort_order = ( $orderby =~ /Asc$/ ) ? 'asc' : 'desc'; |
|
Lines 477-492
sub _convert_sort_fields {
Link Here
|
| 477 |
|
480 |
|
| 478 |
# Turn the sorting into something we care about. |
481 |
# Turn the sorting into something we care about. |
| 479 |
my %sort_field_convert = ( |
482 |
my %sort_field_convert = ( |
| 480 |
acqdate => 'acqdate', |
483 |
acqdate => 'date-of-acquisition', |
| 481 |
author => 'author', |
484 |
author => 'author', |
| 482 |
call_number => 'callnum', |
485 |
call_number => 'local-classification', |
| 483 |
popularity => 'issues', |
486 |
popularity => 'issues', |
| 484 |
relevance => undef, # default |
487 |
relevance => undef, # default |
| 485 |
title => 'title', |
488 |
title => 'title', |
| 486 |
pubdate => 'pubdate', |
489 |
pubdate => 'date-of-publication', |
|
|
490 |
); |
| 491 |
my %sort_order_convert = ( |
| 492 |
dsc => 'desc', |
| 493 |
asc => 'asc', |
| 494 |
az => 'asc', |
| 495 |
za => 'desc', |
| 487 |
); |
496 |
); |
| 488 |
my %sort_order_convert = |
|
|
| 489 |
( qw( dsc desc ), qw( asc asc ), qw( az asc ), qw( za desc ) ); |
| 490 |
|
497 |
|
| 491 |
# Convert the fields and orders, drop anything we don't know about. |
498 |
# Convert the fields and orders, drop anything we don't know about. |
| 492 |
grep { $_->{field} } map { |
499 |
grep { $_->{field} } map { |
|
Lines 513-533
types.
Link Here
|
| 513 |
=cut |
520 |
=cut |
| 514 |
|
521 |
|
| 515 |
our %index_field_convert = ( |
522 |
our %index_field_convert = ( |
| 516 |
'kw' => '_all', |
523 |
'kw' => '_all', |
| 517 |
'ti' => 'title', |
524 |
'ab' => 'abstract', |
| 518 |
'au' => 'author', |
525 |
'au' => 'author', |
| 519 |
'su' => 'subject', |
526 |
'lcn' => 'local-classification', |
| 520 |
'nb' => 'isbn', |
527 |
'callnum' => 'local-classification', |
| 521 |
'se' => 'title-series', |
528 |
'record-type' => 'rtype', |
| 522 |
'callnum' => 'callnum', |
529 |
'mc-rtype' => 'rtype', |
| 523 |
'itype' => 'itype', |
530 |
'mus' => 'rtype', |
| 524 |
'ln' => 'ln', |
531 |
'lc-card' => 'lc-card-number', |
| 525 |
'branch' => 'homebranch', |
532 |
'sn' => 'local-number', |
| 526 |
'fic' => 'lf', |
533 |
'yr' => 'date-of-publication', |
| 527 |
'mus' => 'rtype', |
534 |
'pubdate' => 'date-of-publication', |
| 528 |
'aud' => 'ta', |
535 |
'acqdate' => 'date-of-acquisition', |
| 529 |
'hi' => 'Host-Item-Number', |
536 |
'date/time-last-modified' => 'date-time-last-modified', |
|
|
537 |
'dtlm' => 'date/time-last-modified', |
| 538 |
'diss' => 'dissertation-information', |
| 539 |
'nb' => 'isbn', |
| 540 |
'ns' => 'issn', |
| 541 |
'music-number' => 'identifier-publisher-for-music', |
| 542 |
'number-music-publisher' => 'identifier-publisher-for-music', |
| 543 |
'music' => 'identifier-publisher-for-music', |
| 544 |
'ident' => 'identifier-standard', |
| 545 |
'cpn' => 'corporate-name', |
| 546 |
'cfn' => 'conference-name', |
| 547 |
'pn' => 'personal-name', |
| 548 |
'pb' => 'publisher', |
| 549 |
'pv' => 'provider', |
| 550 |
'nt' => 'note', |
| 551 |
'notes' => 'note', |
| 552 |
'rcn' => 'record-control-number', |
| 553 |
'su' => 'subject', |
| 554 |
'su-to' => 'subject', |
| 555 |
#'su-geo' => 'subject', |
| 556 |
'su-ut' => 'subject', |
| 557 |
'ti' => 'title', |
| 558 |
'se' => 'title-series', |
| 559 |
'ut' => 'title-uniform', |
| 560 |
'an' => 'authority-number', |
| 561 |
'koha-auth-number' => 'authority-number', |
| 562 |
'at' => 'authtype', |
| 563 |
'he' => 'heading', |
| 564 |
'rank' => 'relevance', |
| 565 |
'phr' => 'st-phrase', |
| 566 |
'wrdl' => 'st-word-list', |
| 567 |
'rt' => 'right-truncation', |
| 568 |
'rtrn' => 'right-truncation', |
| 569 |
'ltrn' => 'left-truncation', |
| 570 |
'rltrn' => 'left-and-right', |
| 571 |
'mc-itemtype' => 'itemtype', |
| 572 |
'mc-ccode' => 'ccode', |
| 573 |
'branch' => 'homebranch', |
| 574 |
'mc-loc' => 'location', |
| 575 |
'stocknumber' => 'number-local-acquisition', |
| 576 |
'inv' => 'number-local-acquisition', |
| 577 |
'bc' => 'barcode', |
| 578 |
'mc-itype' => 'itype', |
| 579 |
'aub' => 'author-personal-bibliography', |
| 580 |
'auo' => 'author-in-order', |
| 581 |
'ff8-22' => 'ta', |
| 582 |
'aud' => 'ta', |
| 583 |
'audience' => 'ta', |
| 584 |
'frequency-code' => 'ff8-18', |
| 585 |
'illustration-code' => 'ff8-18-21', |
| 586 |
'regularity-code' => 'ff8-19', |
| 587 |
'type-of-serial' => 'ff8-21', |
| 588 |
'format' => 'ff8-23', |
| 589 |
'conference-code' => 'ff8-29', |
| 590 |
'festschrift-indicator' => 'ff8-30', |
| 591 |
'index-indicator' => 'ff8-31', |
| 592 |
'fiction' => 'lf', |
| 593 |
'fic' => 'lf', |
| 594 |
'literature-code' => 'lf', |
| 595 |
'biography' => 'bio', |
| 596 |
'ff8-34' => 'bio', |
| 597 |
'biography-code' => 'bio', |
| 598 |
'l-format' => 'ff7-01-02', |
| 599 |
'lex' => 'lexile-number', |
| 600 |
'hi' => 'host-item-number', |
| 601 |
'itu' => 'index-term-uncontrolled', |
| 602 |
'itg' => 'index-term-genre', |
| 530 |
); |
603 |
); |
|
|
604 |
my $field_name_pattern = '[\w\-]+'; |
| 605 |
my $multi_field_pattern = "(?:\\.$field_name_pattern)*"; |
| 531 |
|
606 |
|
| 532 |
sub _convert_index_fields { |
607 |
sub _convert_index_fields { |
| 533 |
my ( $self, @indexes ) = @_; |
608 |
my ( $self, @indexes ) = @_; |
|
Lines 540-553
sub _convert_index_fields {
Link Here
|
| 540 |
# when joining things, to indicate we make it an 'OR' join. |
615 |
# when joining things, to indicate we make it an 'OR' join. |
| 541 |
# (Sorry, this got a bit ugly after special cases were found.) |
616 |
# (Sorry, this got a bit ugly after special cases were found.) |
| 542 |
grep { $_->{field} } map { |
617 |
grep { $_->{field} } map { |
| 543 |
my ( $f, $t ) = split /,/; |
618 |
# Lower case all field names |
|
|
619 |
my ( $f, $t ) = map(lc, split /,/); |
| 544 |
my $mc = ''; |
620 |
my $mc = ''; |
| 545 |
if ($f =~ /^mc-/) { |
621 |
if ($f =~ /^mc-/) { |
| 546 |
$mc = 'mc-'; |
622 |
$mc = 'mc-'; |
| 547 |
$f =~ s/^mc-//; |
623 |
$f =~ s/^mc-//; |
| 548 |
} |
624 |
} |
| 549 |
my $r = { |
625 |
my $r = { |
| 550 |
field => $index_field_convert{$f}, |
626 |
field => exists $index_field_convert{$f} ? $index_field_convert{$f} : $f, |
| 551 |
type => $index_type_convert{ $t // '__default' } |
627 |
type => $index_type_convert{ $t // '__default' } |
| 552 |
}; |
628 |
}; |
| 553 |
$r->{field} = ($mc . $r->{field}) if $mc && $r->{field}; |
629 |
$r->{field} = ($mc . $r->{field}) if $mc && $r->{field}; |
|
Lines 599-610
second part is stripped off as we can't yet handle that. Making it work
Link Here
|
| 599 |
will have to wait for a real query parser. |
675 |
will have to wait for a real query parser. |
| 600 |
|
676 |
|
| 601 |
=cut |
677 |
=cut |
| 602 |
|
|
|
| 603 |
sub _convert_index_strings_freeform { |
678 |
sub _convert_index_strings_freeform { |
| 604 |
my ( $self, $search ) = @_; |
679 |
my ( $self, $search ) = @_; |
| 605 |
while ( my ( $zeb, $es ) = each %index_field_convert ) { |
680 |
# @TODO: Currenty will alter also fields contained within quotes: |
| 606 |
$search =~ s/\b$zeb(?:,[\w\-]*)?:/$es:/g; |
681 |
# `searching for "stuff cn:123"` for example will become |
| 607 |
} |
682 |
# `searching for "stuff local-number:123" |
|
|
683 |
# |
| 684 |
# Fixing this is tricky, one possibility: |
| 685 |
# https://stackoverflow.com/questions/19193876/perl-regex-to-match-a-string-that-is-not-enclosed-in-quotes |
| 686 |
# Still not perfect, and will not handle escaped quotes within quotes and assumes balanced quotes. |
| 687 |
# |
| 688 |
# Another, not so elegant, solution could be to replace all quoted content with placeholders, and put |
| 689 |
# them back when processing is done. |
| 690 |
|
| 691 |
# Lower case field names |
| 692 |
$search =~ s/($field_name_pattern)(?:,[\w-]*)?($multi_field_pattern):/\L$1\E$2:/og; |
| 693 |
# Resolve possible field aliases |
| 694 |
$search =~ s/($field_name_pattern)($multi_field_pattern):/(exists $index_field_convert{$1} ? $index_field_convert{$1} : $1)."$2:"/oge; |
| 608 |
return $search; |
695 |
return $search; |
| 609 |
} |
696 |
} |
| 610 |
|
697 |
|
|
Lines 793-804
operands and double quoted strings.
Link Here
|
| 793 |
|
880 |
|
| 794 |
=cut |
881 |
=cut |
| 795 |
|
882 |
|
|
|
883 |
my $tokenize_split_re = qr/((?:${field_name_pattern}${multi_field_pattern}:)?"[^"]+"|\s+)/; |
| 796 |
sub _truncate_terms { |
884 |
sub _truncate_terms { |
| 797 |
my ( $self, $query ) = @_; |
885 |
my ( $self, $query ) = @_; |
| 798 |
|
886 |
|
| 799 |
# '"donald duck" title:"the mouse" and peter" get split into |
887 |
# '"donald duck" title:"the mouse" and peter" get split into |
| 800 |
# ['', '"donald duck"', '', ' ', '', 'title:"the mouse"', '', ' ', 'and', ' ', 'pete'] |
888 |
# ['', '"donald duck"', '', ' ', '', 'title:"the mouse"', '', ' ', 'and', ' ', 'pete'] |
| 801 |
my @tokens = split /((?:[\w\-.]+:)?"[^"]+"|\s+)/, $query; |
889 |
my @tokens = split $tokenize_split_re, $query; |
| 802 |
|
890 |
|
| 803 |
# Filter out empty tokens |
891 |
# Filter out empty tokens |
| 804 |
my @words = grep { $_ !~ /^\s*$/ } @tokens; |
892 |
my @words = grep { $_ !~ /^\s*$/ } @tokens; |