X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=C4%2FSearch.pm;h=ef81e19e7cb85f7a49f51ea53c2a0254c936e2f5;hb=00cf699c82aeea46ef5a72bf344fd306430c5aba;hp=2583f90458d86a7e3f27741d26bc6bbecf7fb50a;hpb=d019f86f671771a36006f8847712317e0337c02f;p=koha.git diff --git a/C4/Search.pm b/C4/Search.pm index 2583f90458..ef81e19e7c 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -26,13 +26,14 @@ use Koha::Libraries; use Lingua::Stem; use C4::Search::PazPar2; use XML::Simple; -use C4::Members qw(GetHideLostItemsPreference); use C4::XSLT; use C4::Reserves; # GetReserveStatus use C4::Debug; use C4::Charset; use Koha::AuthorisedValues; +use Koha::ItemTypes; use Koha::Libraries; +use Koha::Patrons; use YAML; use URI::Escape; use Business::ISBN; @@ -160,7 +161,7 @@ sub FindDuplicate { =head2 SimpleSearch -( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers] ); +( $error, $results, $total_hits ) = SimpleSearch( $query, $offset, $max_results, [@servers], [%options] ); This function provides a simple search API on the bibliographic catalog @@ -172,6 +173,7 @@ This function provides a simple search API on the bibliographic catalog * @servers is optional. Defaults to biblioserver as found in koha-conf.xml * $offset - If present, represents the number of records at the beginning to omit. Defaults to 0 * $max_results - if present, determines the maximum number of records to fetch. undef is All. defaults to undef. + * %options is optional. (e.g. "skip_normalize" allows you to skip changing : to = ) =item C @@ -221,7 +223,7 @@ $template->param(result=>\@results); =cut sub SimpleSearch { - my ( $query, $offset, $max_results, $servers ) = @_; + my ( $query, $offset, $max_results, $servers, %options ) = @_; return ( 'No query entered', undef, undef ) unless $query; # FIXME hardcoded value. See catalog/search.pl & opac-search.pl too. @@ -243,12 +245,12 @@ sub SimpleSearch { eval { $zconns[$i] = C4::Context->Zconn( $servers[$i], 1 ); if ($QParser) { - $query =~ s/=/:/g; + $query =~ s/=/:/g unless $options{skip_normalize}; $QParser->parse( $query ); $query = $QParser->target_syntax($servers[$i]); $zoom_queries[$i] = new ZOOM::Query::PQF( $query, $zconns[$i]); } else { - $query =~ s/:/=/g; + $query =~ s/:/=/g unless $options{skip_normalize}; $zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]); } $tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] ); @@ -330,6 +332,7 @@ sub getRecords { my @servers = @$servers_ref; my @sort_by = @$sort_by_ref; + $offset = 0 if $offset < 0; # Initialize variables for the ZOOM connection and results object my $zconn; @@ -584,6 +587,13 @@ sub getRecords { $facet_label_value = $av->count ? $av->next->opac_description : ''; } + # also, if it's a collection code, use the name instead of the code + if ( $link_value =~ /ccode/ ) { + # TODO Retrieve all authorised values at once, instead of 1 query per entry + my $av = Koha::AuthorisedValues->search({ category => 'CCODE', authorised_value => $one_facet }); + $facet_label_value = $av->count ? $av->next->opac_description : ''; + } + # but we're down with the whole label being in the link's title. push @this_facets_array, { @@ -644,11 +654,9 @@ sub GetFacets { my $rs = shift; my $facets; - my $indexing_mode = C4::Context->config('zebra_bib_index_mode') // 'dom'; my $use_zebra_facets = C4::Context->config('use_zebra_facets') // 0; - if ( $indexing_mode eq 'dom' && - $use_zebra_facets ) { + if ( $use_zebra_facets ) { $facets = _get_facets_from_zebra( $rs ); } else { $facets = _get_facets_from_records( $rs ); @@ -960,6 +968,9 @@ sub _build_stemmed_operand { require Lingua::Stem::Snowball ; my $stemmed_operand=q{}; + # Stemmer needs language + return $operand unless $lang; + # If operand contains a digit, it is almost certainly an identifier, and should # not be stemmed. This is particularly relevant for ISBNs and ISSNs, which # can contain the letter "X" - for example, _build_stemmend_operand would reduce @@ -1078,6 +1089,8 @@ sub getIndexes{ 'an', 'Any', 'at', + 'arl', + 'arp', 'au', 'aub', 'aud', @@ -1332,7 +1345,7 @@ sub _handle_exploding_index { ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang ) = - buildQuery ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang); + parseQuery ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang); Shim function to ease the transition from buildQuery to a new QueryParser. This function is called at the beginning of buildQuery, and modifies @@ -1476,10 +1489,19 @@ sub buildQuery { # This is needed otherwise ccl= and &limit won't work together, and # this happens when selecting a subject on the opac-detail page @limits = grep {!/^$/} @limits; + my $original_q = $q; # without available part + unless ( grep { /^available$/ } @limits ) { + $q =~ s| and \( \( allrecords,AlwaysMatches:'' not onloan,AlwaysMatches:''\) and \(lost,st-numeric=0\) \)||; + $original_q = $q; + } if ( @limits ) { - $q .= ' and '.join(' and ', @limits); + if ( grep { /^available$/ } @limits ) { + $q .= q| and ( ( allrecords,AlwaysMatches:'' not onloan,AlwaysMatches:'') and (lost,st-numeric=0) )|; + delete $limits['available']; + } + $q .= ' and '.join(' and ', @limits) if @limits; } - return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $q, '', '', '', 'ccl' ); + return ( undef, $q, $q, "q=ccl=".uri_escape_utf8($q), $original_q, '', '', '', 'ccl' ); } if ( $query =~ /^cql=/ ) { return ( undef, $', $', "q=cql=".uri_escape_utf8($'), $', '', '', '', 'cql' ); @@ -1520,7 +1542,7 @@ sub buildQuery { for ( my $i = 0 ; $i <= @operands ; $i++ ) { # COMBINE OPERANDS, INDEXES AND OPERATORS - if ( $operands[$i] ) { + if ( ($operands[$i] // '') ne '' ) { $operands[$i]=~s/^\s+//; # A flag to determine whether or not to add the index to the query @@ -1561,7 +1583,7 @@ sub buildQuery { $stemming = $auto_truncation = $weight_fields = $fuzzy_enabled = 0; } # ISBN,ISSN,Standard Number, don't need special treatment - elsif ( $index eq 'nb' || $index eq 'ns' ) { + elsif ( $index eq 'nb' || $index eq 'ns' || $index eq 'hi' ) { ( $stemming, $auto_truncation, $weight_fields, $fuzzy_enabled @@ -1665,7 +1687,7 @@ sub buildQuery { query_desc => $query_desc, operator => ($operators[ $i - 1 ]) ? $operators[ $i - 1 ] : '', parsed_operand => $operand, - original_operand => ($operands[$i]) ? $operands[$i] : '', + original_operand => $operands[$i] // '', index => $index, index_plus => $index_plus, indexes_set => $indexes_set, @@ -1836,9 +1858,9 @@ sub searchResults { require C4::Items; - $search_context = 'opac' if !$search_context || $search_context ne 'intranet'; + $search_context->{'interface'} = 'opac' if !$search_context->{'interface'} || $search_context->{'interface'} ne 'intranet'; my ($is_opac, $hidelostitems); - if ($search_context eq 'opac') { + if ($search_context->{'interface'} eq 'opac') { $hidelostitems = C4::Context->preference('hidelostitems'); $is_opac = 1; } @@ -1849,14 +1871,16 @@ sub searchResults { # FIXME - We build an authorised values hash here, using the default framework # though it is possible to have different authvals for different fws. - my $shelflocations =GetKohaAuthorisedValues('items.location',''); + my $shelflocations = + { map { $_->{authorised_value} => $_->{lib} } Koha::AuthorisedValues->get_descriptions_by_koha_field( { frameworkcode => '', kohafield => 'items.location' } ) }; # get notforloan authorised value list (see $shelflocations FIXME) - my $av = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.notforloan' }); + my $av = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.notforloan', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] }); my $notforloan_authorised_value = $av->count ? $av->next->authorised_value : undef; #Get itemtype hash - my %itemtypes = %{ GetItemTypes() }; + my $itemtypes = Koha::ItemTypes->search_with_localization; + my %itemtypes = map { $_->{itemtype} => $_ } @{ $itemtypes->unblessed }; #search item field code my ($itemtag, undef) = &GetMarcFromKohaField( "items.itemnumber", "" ); @@ -1886,7 +1910,7 @@ sub searchResults { my ($bibliotag,$bibliosubf)=GetMarcFromKohaField('biblio.biblionumber',''); # set stuff for XSLT processing here once, not later again for every record we retrieved - my $interface = $search_context eq 'opac' ? 'OPAC' : ''; + my $interface = $is_opac ? 'OPAC' : ''; my $xslsyspref = $interface . "XSLTResultsDisplay"; my $xslfile = C4::Context->preference($xslsyspref); my $lang = $xslfile ? C4::Languages::getlanguage() : undef; @@ -1926,6 +1950,7 @@ sub searchResults { # add imageurl to itemtype if there is one $oldbiblio->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $oldbiblio->{itemtype} }->{imageurl} ); + $oldbiblio->{'authorised_value_images'} = ($search_context->{'interface'} eq 'opac' && C4::Context->preference('AuthorisedValueImages')) || ($search_context->{'interface'} eq 'intranet' && C4::Context->preference('StaffAuthorisedValueImages')) ? C4::Items::get_authorised_value_images( C4::Biblio::get_biblio_authorised_values( $oldbiblio->{'biblionumber'}, $marcrecord ) ) : []; $oldbiblio->{normalized_upc} = GetNormalizedUPC( $marcrecord,$marcflavour); $oldbiblio->{normalized_ean} = GetNormalizedEAN( $marcrecord,$marcflavour); $oldbiblio->{normalized_oclc} = GetNormalizedOCLCNumber($marcrecord,$marcflavour); @@ -1983,27 +2008,31 @@ sub searchResults { # Pull out the items fields my @fields = $marcrecord->field($itemtag); my $marcflavor = C4::Context->preference("marcflavour"); + # adding linked items that belong to host records - my $analyticsfield = '773'; - if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') { - $analyticsfield = '773'; - } elsif ($marcflavor eq 'UNIMARC') { - $analyticsfield = '461'; - } - foreach my $hostfield ( $marcrecord->field($analyticsfield)) { - my $hostbiblionumber = $hostfield->subfield("0"); - my $linkeditemnumber = $hostfield->subfield("9"); - if(!$hostbiblionumber eq undef){ - my $hostbiblio = GetMarcBiblio($hostbiblionumber, 1); - my ($itemfield, undef) = GetMarcFromKohaField( 'items.itemnumber', GetFrameworkCode($hostbiblionumber) ); - if(!$hostbiblio eq undef){ - my @hostitems = $hostbiblio->field($itemfield); - foreach my $hostitem (@hostitems){ - if ($hostitem->subfield("9") eq $linkeditemnumber){ - my $linkeditem =$hostitem; - # append linked items if they exist - if (!$linkeditem eq undef){ - push (@fields, $linkeditem);} + if ( C4::Context->preference('EasyAnalyticalRecords') ) { + my $analyticsfield = '773'; + if ($marcflavor eq 'MARC21' || $marcflavor eq 'NORMARC') { + $analyticsfield = '773'; + } elsif ($marcflavor eq 'UNIMARC') { + $analyticsfield = '461'; + } + foreach my $hostfield ( $marcrecord->field($analyticsfield)) { + my $hostbiblionumber = $hostfield->subfield("0"); + my $linkeditemnumber = $hostfield->subfield("9"); + if( $hostbiblionumber ) { + my $hostbiblio = GetMarcBiblio({ + biblionumber => $hostbiblionumber, + embed_items => 1 }); + my ($itemfield, undef) = GetMarcFromKohaField( 'items.itemnumber', GetFrameworkCode($hostbiblionumber) ); + if( $hostbiblio ) { + my @hostitems = $hostbiblio->field($itemfield); + foreach my $hostitem (@hostitems){ + if ($hostitem->subfield("9") eq $linkeditemnumber){ + my $linkeditem =$hostitem; + # append linked items if they exist + push @fields, $linkeditem if $linkeditem; + } } } } @@ -2056,7 +2085,7 @@ sub searchResults { next; } # hidden based on OpacHiddenItems syspref - my @hi = C4::Items::GetHiddenItemnumbers($item); + my @hi = C4::Items::GetHiddenItemnumbers({ items=> [ $item ], borcat => $search_context->{category} }); if (scalar @hi) { push @hiddenitems, @hi; $hideatopac_count++; @@ -2079,18 +2108,20 @@ sub searchResults { # For each grouping of items (onloan, available, unavailable), we build a key to store relevant info about that item my $userenv = C4::Context->userenv; if ( $item->{onloan} - && !( C4::Members::GetHideLostItemsPreference( $userenv->{'number'} ) && $item->{itemlost} ) ) + && $userenv + && $userenv->{number} + && !( Koha::Patrons->find($userenv->{number})->category->hidelostitems && $item->{itemlost} ) ) { $onloan_count++; my $key = $prefix . $item->{onloan} . $item->{barcode}; - $onloan_items->{$key}->{due_date} = output_pref( { dt => dt_from_string( $item->{onloan} ), dateonly => 1 } ); + $onloan_items->{$key}->{due_date} = $item->{onloan}; $onloan_items->{$key}->{count}++ if $item->{$hbranch}; $onloan_items->{$key}->{branchname} = $item->{branchname}; $onloan_items->{$key}->{location} = $shelflocations->{ $item->{location} }; $onloan_items->{$key}->{itemcallnumber} = $item->{itemcallnumber}; $onloan_items->{$key}->{description} = $item->{description}; $onloan_items->{$key}->{imageurl} = - getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); # if something's checked out and lost, mark it as 'long overdue' if ( $item->{itemlost} ) { @@ -2180,21 +2211,21 @@ sub searchResults { $other_items->{$key}->{intransit} = ( $transfertwhen ne '' ) ? 1 : 0; $other_items->{$key}->{onhold} = ($reservestatus) ? 1 : 0; $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value and $item->{notforloan}; - $other_items->{$key}->{count}++ if $item->{$hbranch}; - $other_items->{$key}->{location} = $shelflocations->{ $item->{location} }; - $other_items->{$key}->{description} = $item->{description}; - $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + $other_items->{$key}->{count}++ if $item->{$hbranch}; + $other_items->{$key}->{location} = $shelflocations->{ $item->{location} }; + $other_items->{$key}->{description} = $item->{description}; + $other_items->{$key}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); } # item is available else { $can_place_holds = 1; $available_count++; - $available_items->{$prefix}->{count}++ if $item->{$hbranch}; - foreach (qw(branchname itemcallnumber description)) { - $available_items->{$prefix}->{$_} = $item->{$_}; - } - $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} }; - $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context, $itemtypes{ $item->{itype} }->{imageurl} ); + $available_items->{$prefix}->{count}++ if $item->{$hbranch}; + foreach (qw(branchname itemcallnumber description)) { + $available_items->{$prefix}->{$_} = $item->{$_}; + } + $available_items->{$prefix}->{location} = $shelflocations->{ $item->{location} }; + $available_items->{$prefix}->{imageurl} = getitemtypeimagelocation( $search_context->{'interface'}, $itemtypes{ $item->{itype} }->{imageurl} ); } } } # notforloan, item level and biblioitem level @@ -2222,7 +2253,6 @@ sub searchResults { # we fetched the sysprefs already before the loop through all retrieved record! if (!$scan && $xslfile) { $oldbiblio->{XSLTResultsRecord} = XSLTParse4Display($oldbiblio->{biblionumber}, $marcrecord, $xslsyspref, 1, \@hiddenitems, $sysxml, $xslfile, $lang); - # the last parameter tells Koha to clean up the problematic ampersand entities that Zebra outputs } # if biblio level itypes are used and itemtype is notforloan, it can't be reserved either @@ -2232,7 +2262,6 @@ sub searchResults { } } $oldbiblio->{norequests} = 1 unless $can_place_holds; - $oldbiblio->{itemsplural} = 1 if $items_count > 1; $oldbiblio->{items_count} = $items_count; $oldbiblio->{available_items_loop} = \@available_items_loop; $oldbiblio->{onloan_items_loop} = \@onloan_items_loop; @@ -2489,7 +2518,7 @@ sub new_record_from_zebra { # Set the default indexing modes my $search_engine = C4::Context->preference("SearchEngine"); if ($search_engine eq 'Elasticsearch') { - return $raw_data; + return ref $raw_data eq 'MARC::Record' ? $raw_data : MARC::Record->new_from_xml( $raw_data, 'UTF-8' ); } my $index_mode = ( $server eq 'biblioserver' ) ? C4::Context->config('zebra_bib_index_mode') // 'dom'