@@ -, +, @@ * OPAC (simple search) * OPAC (advanced search) * OPAC (authorities) * Staff client (header search) * Staff client (advanced search) * Staff client (cataloging search) * Staff client (authorities) * Staff client (importing a batch using a match point) * Staff client (searching for an item for adding to a label) * Staff client (acquisitions) * Staff client (searching for a record to create a serial) * ANYWHERE ELSE I HAVE FORGOTTEN searches you did above. --- C4/AuthoritiesMarc.pm | 40 ++++++- C4/Matcher.pm | 55 +++++----- C4/Search.pm | 112 +++++++++++++------- Koha/SuggestionEngine/Plugin/AuthorityFile.pm | 2 +- cataloguing/addbooks.pl | 10 +- .../value_builder/marc21_linking_section.pl | 10 +- cataloguing/value_builder/unimarc_field_4XX.pl | 10 +- labels/label-item-search.pl | 25 +++-- misc/migration_tools/bulkmarcimport.pl | 22 +++- serials/subscription-bib-search.pl | 18 +++- 10 files changed, 219 insertions(+), 85 deletions(-) --- a/C4/AuthoritiesMarc.pm +++ a/C4/AuthoritiesMarc.pm @@ -201,6 +201,9 @@ sub SearchAuthorities { } } else { my $query; + my $qpquery = ''; + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); my $attr = ''; # the marclist may contain "mainentry". In this case, search the tag_to_report, that depends on # the authtypecode. Then, search on $a of this tag_to_report @@ -219,6 +222,9 @@ sub SearchAuthorities { if ($n>1){ while ($n>1){$query= "\@or ".$query;$n--;} } + if ($QParser) { + $qpquery .= '(authtype:' . join('|| authtype:', @auths) . ')'; + } } my $dosearch; @@ -276,6 +282,9 @@ sub SearchAuthorities { $q2 .=$attr; $dosearch=1; ++$attr_cnt; + if ($QParser) { + $qpquery .= " $tags->[$i]:$value->[$i]"; + } }#if value } ##Add how many queries generated @@ -296,8 +305,21 @@ sub SearchAuthorities { } elsif ($sortby eq 'AuthidDsc') { $orderstring = '@attr 7=2 @attr 4=109 @attr 1=Local-Number 0'; } - $query=($query?$query:"\@attr 1=_ALLRECORDS \@attr 2=103 ''"); - $query="\@or $orderstring $query" if $orderstring; + if ($QParser) { + $qpquery .= ' all:all' unless $value->[0]; + + if ( $value->[0] =~ m/^qp=(.*)$/ ) { + $qpquery = $1; + } + + $qpquery .= " #$sortby"; + + $QParser->parse( $qpquery ); + $query = $QParser->target_syntax('authorityserver'); + } else { + $query=($query?$query:"\@attr 1=_ALLRECORDS \@attr 2=103 ''"); + $query="\@or $orderstring $query" if $orderstring; + } $offset=0 unless $offset; my $counter = $offset; @@ -406,7 +428,7 @@ sub CountUsage { } else { ### ZOOM search here my $query; - $query= "an=".$authid; + $query= "an:".$authid; my ($err,$res,$result) = C4::Search::SimpleSearch($query,0,10); if ($err) { warn "Error: $err from search $query"; @@ -910,11 +932,19 @@ sub FindDuplicateAuthority { $sth->finish; # warn "record :".$record->as_formatted." auth_tag_to_report :$auth_tag_to_report"; # build a request for SearchAuthorities - my $query='at='.$authtypecode.' '; + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + my $op; + if ($QParser) { + $op = '&&'; + } else { + $op = 'and'; + } + my $query='at:'.$authtypecode.' '; my $filtervalues=qr([\001-\040\!\'\"\`\#\$\%\&\*\+,\-\./:;<=>\?\@\(\)\{\[\]\}_\|\~]); if ($record->field($auth_tag_to_report)) { foreach ($record->field($auth_tag_to_report)->subfields()) { - $_->[1]=~s/$filtervalues/ /g; $query.= " and he,wrdl=\"".$_->[1]."\"" if ($_->[0]=~/[A-z]/); + $_->[1]=~s/$filtervalues/ /g; $query.= " $op he:\"".$_->[1]."\"" if ($_->[0]=~/[A-z]/); } } my ($error, $results, $total_hits) = C4::Search::SimpleSearch( $query, 0, 1, [ "authorityserver" ] ); --- a/C4/Matcher.pm +++ a/C4/Matcher.pm @@ -626,6 +626,8 @@ sub get_matches { my %matches = (); + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); foreach my $matchpoint (@{ $self->{'matchpoints'} }) { my @source_keys = _get_match_keys($source_record, $matchpoint); next if scalar(@source_keys) == 0; @@ -634,31 +636,36 @@ sub get_matches { my $error; my $searchresults; my $total_hits; - if ($self->{'record_type'} eq 'biblio') { - $query = join(" or ", map { "$matchpoint->{'index'}=$_" } @source_keys); -# FIXME only searching biblio index at the moment + if ($QParser) { + $query = join(" || ", map { "$matchpoint->{'index'}:$_" } @source_keys); require C4::Search; - ($error, $searchresults, $total_hits) = C4::Search::SimpleSearch($query, 0, $max_matches); - } elsif ($self->{'record_type'} eq 'authority') { - my $authresults; - my @marclist; - my @and_or; - my @excluding = []; - my @operator; - my @value; - foreach my $key (@source_keys) { - push @marclist, $matchpoint->{'index'}; - push @and_or, 'or'; - push @operator, 'exact'; - push @value, $key; - } - require C4::AuthoritiesMarc; - ($authresults, $total_hits) = C4::AuthoritiesMarc::SearchAuthorities( - \@marclist, \@and_or, \@excluding, \@operator, - \@value, 0, 20, undef, 'AuthidAsc', 1 - ); - foreach my $result (@$authresults) { - push @$searchresults, $result->{'authid'}; + ($error, $searchresults, $total_hits) = C4::Search::SimpleSearch($query, 0, $max_matches, [ $self->{'record_type'} . 'server' ] ); + } else { + if ($self->{'record_type'} eq 'biblio') { + $query = join(" or ", map { "$matchpoint->{'index'}=$_" } @source_keys); + require C4::Search; + ($error, $searchresults, $total_hits) = C4::Search::SimpleSearch($query, 0, $max_matches); + } elsif ($self->{'record_type'} eq 'authority') { + my $authresults; + my @marclist; + my @and_or; + my @excluding = []; + my @operator; + my @value; + foreach my $key (@source_keys) { + push @marclist, $matchpoint->{'index'}; + push @and_or, 'or'; + push @operator, 'exact'; + push @value, $key; + } + require C4::AuthoritiesMarc; + ($authresults, $total_hits) = C4::AuthoritiesMarc::SearchAuthorities( + \@marclist, \@and_or, \@excluding, \@operator, + \@value, 0, 20, undef, 'AuthidAsc', 1 + ); + foreach my $result (@$authresults) { + push @$searchresults, $result->{'authid'}; + } } } --- a/C4/Search.pm +++ a/C4/Search.pm @@ -100,9 +100,22 @@ sub FindDuplicate { if ( $result->{isbn} ) { $result->{isbn} =~ s/\(.*$//; $result->{isbn} =~ s/\s+$//; - $query = "isbn=$result->{isbn}"; + $query = "isbn:$result->{isbn}"; } else { + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + my $titleindex; + my $authorindex; + + if ($QParser) { + $titleindex = 'title|exact'; + $authorindex = 'author|exact'; + } else { + $titleindex = 'ti,ext'; + $authorindex = 'au,ext'; + } + $result->{title} =~ s /\\//g; $result->{title} =~ s /\"//g; $result->{title} =~ s /\(//g; @@ -111,8 +124,8 @@ sub FindDuplicate { # FIXME: instead of removing operators, could just do # quotes around the value $result->{title} =~ s/(and|or|not)//g; - $query = "ti,ext=$result->{title}"; - $query .= " and itemtype=$result->{itemtype}" + $query = "$titleindex:\"$result->{title}\""; + $query .= " && itemtype:$result->{itemtype}" if ( $result->{itemtype} ); if ( $result->{author} ) { $result->{author} =~ s /\\//g; @@ -122,7 +135,7 @@ sub FindDuplicate { # remove valid operators $result->{author} =~ s/(and|or|not)//g; - $query .= " and au,ext=$result->{author}"; + $query .= " && $authorindex:\"$result->{author}\""; } } @@ -226,11 +239,21 @@ sub SimpleSearch { my $results = []; my $total_hits = 0; + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser') && ! ($query =~ m/\w,\w|\w=\w/)); + # Initialize & Search Zebra for ( my $i = 0 ; $i < @servers ; $i++ ) { eval { $zconns[$i] = C4::Context->Zconn( $servers[$i], 1 ); - $zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]); + if ($QParser) { + $query =~ s/=/:/g; + $QParser->parse( $query ); + $query = $QParser->target_syntax($servers[$i]); + $zoom_queries[$i] = new ZOOM::Query::PQF( $query, $zconns[$i]); + } else { + $zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]); + } $tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] ); # error handling @@ -1093,7 +1116,9 @@ on authority data). =cut sub _handle_exploding_index { - my ( $index, $term ) = @_; + my ($QParser, $struct, $filter, $params, $negate, $server) = @_; + my $index = $filter; + my $term = join(' ', @$params); return unless ($index =~ m/(su-br|su-na|su-rl)/ && $term); @@ -1101,8 +1126,8 @@ sub _handle_exploding_index { my $codesubfield = $marcflavour eq 'UNIMARC' ? '5' : 'w'; my $wantedcodes = ''; - my @subqueries = ( "(su=\"$term\")"); - my ($error, $results, $total_hits) = SimpleSearch( "Heading,wrdl=$term", undef, undef, [ "authorityserver" ] ); + my @subqueries = ( "su:\"$term\""); + my ($error, $results, $total_hits) = SimpleSearch( "he:$term", undef, undef, [ "authorityserver" ] ); foreach my $auth (@$results) { my $record = MARC::Record->new_from_usmarc($auth); my @references = $record->field('5..'); @@ -1116,11 +1141,13 @@ sub _handle_exploding_index { } foreach my $reference (@references) { my $codes = $reference->subfield($codesubfield); - push @subqueries, '(su="' . $reference->as_string('abcdefghijlmnopqrstuvxyz') . '")' if (($codes && $codes eq $wantedcodes) || !$wantedcodes); + push @subqueries, 'su:"' . $reference->as_string('abcdefghijlmnopqrstuvxyz') . '"' if (($codes && $codes eq $wantedcodes) || !$wantedcodes); } } } - return join(' or ', @subqueries); + my $query = '(' x scalar(@subqueries) . join(') || ', @subqueries) . ')'; + warn $query; + return $query; } =head2 parseQuery @@ -1147,37 +1174,42 @@ sub parseQuery { my $query = $operands[0]; my $index; my $term; + my $query_desc; -# TODO: once we are using QueryParser, all this special case code for -# exploded search indexes will be replaced by a callback to -# _handle_exploding_index - if ( $query =~ m/^(.*)\b(su-br|su-na|su-rl)[:=](\w.*)$/ ) { - $query = $1; - $index = $2; - $term = $3; - } else { + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser') || $query =~ s/^qp=//); + undef $QParser if ($query =~ m/^(ccl=|pqf=|cql=)/ || grep (/\w,\w|\w=\w/, @operands) ); + + if ($QParser) + { $query = ''; - for ( my $i = 0 ; $i <= @operands ; $i++ ) { - if ($operands[$i] && $indexes[$i] =~ m/(su-br|su-na|su-rl)/) { - $index = $indexes[$i]; - $term = $operands[$i]; - } elsif ($operands[$i]) { - $query .= $operators[$i] eq 'or' ? ' or ' : ' and ' if ($query); - $query .= "($indexes[$i]:$operands[$i])"; - } + for ( my $ii = 0 ; $ii <= @operands ; $ii++ ) { + next unless $operands[$ii]; + $query .= $operators[ $ii - 1 ] eq 'or' ? ' || ' : ' && ' + if ($query); + $query .= + ( $operators[$ii] ? "$operators[$ii]:" : '' ) . $operands[$ii]; + } + foreach my $limit (@limits) { + } + foreach my $modifier (@sort_by) { + $query .= " #$modifier"; } - } - if ($index) { - my $queryPart = _handle_exploding_index($index, $term); - if ($queryPart) { - $query .= "($queryPart)"; + $query_desc = $query; + if ( C4::Context->preference("QueryWeightFields") ) { } - $operators = (); - $operands[0] = "ccl=$query"; + $QParser->add_bib1_filter_map( 'biblioserver', 'su-br', { 'callback' => \&_handle_exploding_index }); + $QParser->add_bib1_filter_map( 'biblioserver', 'su-na', { 'callback' => \&_handle_exploding_index }); + $QParser->add_bib1_filter_map( 'biblioserver', 'su-rl', { 'callback' => \&_handle_exploding_index }); + $QParser->parse( $query ); + $operands[0] = "pqf=" . $QParser->target_syntax('biblioserver'); +# TODO: once we are using QueryParser, all this special case code for +# exploded search indexes will be replaced by a callback to +# _handle_exploding_index } - return ( $operators, \@operands, $indexes, $limits, $sort_by, $scan, $lang); + return ( $operators, \@operands, $indexes, $limits, $sort_by, $scan, $lang, $query_desc); } =head2 buildQuery @@ -1201,7 +1233,8 @@ sub buildQuery { warn "---------\nEnter buildQuery\n---------" if $DEBUG; - ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang) = parseQuery($operators, $operands, $indexes, $limits, $sort_by, $scan, $lang); + my $query_desc; + ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang, $query_desc) = parseQuery($operators, $operands, $indexes, $limits, $sort_by, $scan, $lang); # dereference my @operators = $operators ? @$operators : (); @@ -1229,7 +1262,6 @@ sub buildQuery { # initialize the variables we're passing back my $query_cgi; - my $query_desc; my $query_type; my $limit; @@ -1264,7 +1296,13 @@ sub buildQuery { return ( undef, $', $', "q=cql=$'", $', '', '', '', '', 'cql' ); } if ( $query =~ /^pqf=/ ) { - return ( undef, $', $', "q=pqf=$'", $', '', '', '', '', 'pqf' ); + if ($query_desc) { + $query_cgi = "q=$query_desc"; + } else { + $query_desc = $'; + $query_cgi = "q=pqf=$'"; + } + return ( undef, $', $', $query_cgi, $query_desc, '', '', '', '', 'pqf' ); } # pass nested queries directly --- a/Koha/SuggestionEngine/Plugin/AuthorityFile.pm +++ a/Koha/SuggestionEngine/Plugin/AuthorityFile.pm @@ -85,7 +85,7 @@ sub get_suggestions { # FIXME: calling into C4 require C4::AuthoritiesMarc; my ( $searchresults, $count ) = C4::AuthoritiesMarc::SearchAuthorities( - \@marclist, \@and_or, \@excluding, \@operator, + @marclist, @and_or, @excluding, @operator, @value, 0, $param->{'count'}, '', 'Relevance', 0 ); --- a/cataloguing/addbooks.pl +++ a/cataloguing/addbooks.pl @@ -70,10 +70,16 @@ if ($query) { # build query my @operands = $query; - my ( $builterror,$builtquery,$simple_query,$query_cgi,$query_desc,$limit,$limit_cgi,$limit_desc,$stopwords_removed,$query_type) = buildQuery(undef,\@operands); + + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + unless ($QParser) { + my ( $builterror,$builtquery,$simple_query,$query_cgi,$query_desc,$limit,$limit_cgi,$limit_desc,$stopwords_removed,$query_type) = buildQuery(undef,\@operands); + $query = $builtquery; + } # find results - my ( $error, $marcresults, $total_hits ) = SimpleSearch($builtquery, $results_per_page * ($page - 1), $results_per_page); + my ( $error, $marcresults, $total_hits ) = SimpleSearch($query, $results_per_page * ($page - 1), $results_per_page); if ( defined $error ) { $template->param( error => $error ); --- a/cataloguing/value_builder/marc21_linking_section.pl +++ a/cataloguing/value_builder/marc21_linking_section.pl @@ -190,7 +190,15 @@ sub plugin { my $startfrom = $query->param('startfrom'); my $resultsperpage = $query->param('resultsperpage') || 20; my $orderby; - $search = 'kw,wrdl=' . $search . ' and mc-itemtype=' . $itype if $itype; + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + my $op; + if ($QParser) { + $op = '&&'; + } else { + $op = 'and'; + } + $search = 'kw:' . $search . " $op mc-itemtype:" . $itype if $itype; my ( $errors, $results, $total_hits ) = SimpleSearch( $search, $startfrom * $resultsperpage, $resultsperpage ); --- a/cataloguing/value_builder/unimarc_field_4XX.pl +++ a/cataloguing/value_builder/unimarc_field_4XX.pl @@ -341,7 +341,15 @@ sub plugin { my $startfrom = $query->param('startfrom'); my $resultsperpage = $query->param('resultsperpage') || 20; my $orderby; - $search = 'kw,wrdl='.$search.' and mc-itemtype='.$itype if $itype; + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + my $op; + if ($QParser) { + $op = '&&'; + } else { + $op = 'and'; + } + $search = 'kw:'.$search." $op mc-itemtype:".$itype if $itype; my ( $errors, $results, $total_hits ) = SimpleSearch($search, $startfrom * $resultsperpage, $resultsperpage ); if (defined $errors ) { $results = []; --- a/labels/label-item-search.pl +++ a/labels/label-item-search.pl @@ -66,10 +66,12 @@ my $display_columns = [ {_add => {label => "Add Item", link_fi ]; if ( $op eq "do_search" ) { + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); $idx = $query->param('idx'); $ccl_textbox = $query->param('ccl_textbox'); if ( $ccl_textbox && $idx ) { - $ccl_query = "$idx=$ccl_textbox"; + $ccl_query = "$idx:$ccl_textbox"; } $datefrom = $query->param('datefrom'); @@ -77,15 +79,26 @@ if ( $op eq "do_search" ) { if ($datefrom) { $datefrom = C4::Dates->new($datefrom); - $ccl_query .= ' and ' if $ccl_textbox; - $ccl_query .= - "acqdate,st-date-normalized,ge=" . $datefrom->output("iso"); + if ($QParser) { + $ccl_query .= ' && ' if $ccl_textbox; + $ccl_query .= + "acqdate(" . $datefrom->output("iso") . '-)'; + } else { + $ccl_query .= ' and ' if $ccl_textbox; + $ccl_query .= + "acqdate,st-date-normalized,ge=" . $datefrom->output("iso"); + } } if ($dateto) { $dateto = C4::Dates->new($dateto); - $ccl_query .= ' and ' if ( $ccl_textbox || $datefrom ); - $ccl_query .= "acqdate,st-date-normalized,le=" . $dateto->output("iso"); + if ($QParser) { + $ccl_query .= ' && ' if ( $ccl_textbox || $datefrom ); + $ccl_query .= "acqdate(-" . $dateto->output("iso") . ')'; + } else { + $ccl_query .= ' and ' if ( $ccl_textbox || $datefrom ); + $ccl_query .= "acqdate,st-date-normalized,le=" . $datefrom->output("iso"); + } } my $offset = $startfrom > 1 ? $startfrom - 1 : 0; --- a/misc/migration_tools/bulkmarcimport.pl +++ a/misc/migration_tools/bulkmarcimport.pl @@ -432,7 +432,15 @@ sub build_query { my $string = build_simplequery($matchingpoint,$record); push @searchstrings,$string if (length($string)>0); } - return join(" and ",@searchstrings); + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + my $op; + if ($QParser) { + $op = '&&'; + } else { + $op = 'and'; + } + return join(" $op ",@searchstrings); } sub build_simplequery { my $element=shift; @@ -442,10 +450,18 @@ sub build_simplequery { my @searchstrings; foreach my $field ($record->field($tag)){ if (length($field->as_string("$subfields"))>0){ - push @searchstrings,"$index,wrdl=\"".$field->as_string("$subfields")."\""; + push @searchstrings,"$index:\"".$field->as_string("$subfields")."\""; } } - return join(" and ",@searchstrings); + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + my $op; + if ($QParser) { + $op = '&&'; + } else { + $op = 'and'; + } + return join(" $op ",@searchstrings); } sub report_item_errors { my $biblionumber = shift; --- a/serials/subscription-bib-search.pl +++ a/serials/subscription-bib-search.pl @@ -90,11 +90,19 @@ if ($op eq "do_search" && $query) { # add the itemtype limit if applicable my $itemtypelimit = $input->param('itemtypelimit'); if ( $itemtypelimit ) { - if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') { - $query .= " AND $itype_or_itemtype=$itemtypelimit"; - } else { - $query .= " AND $advanced_search_types=$itemtypelimit"; - } + my $QParser; + $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); + my $op; + if ($QParser) { + $op = '&&'; + } else { + $op = 'and'; + } + if (!$advanced_search_types or $advanced_search_types eq 'itemtypes') { + $query .= " $op $itype_or_itemtype:$itemtypelimit"; + } else { + $query .= " $op $advanced_search_types:$itemtypelimit"; + } } $debug && warn $query; $resultsperpage= $input->param('resultsperpage'); --