}
} 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
if ($n>1){
while ($n>1){$query= "\@or ".$query;$n--;}
}
+ if ($QParser) {
+ $qpquery .= '(authtype:' . join('|| authtype:', @auths) . ')';
+ }
}
my $dosearch;
$q2 .=$attr;
$dosearch=1;
++$attr_cnt;
+ if ($QParser) {
+ $qpquery .= " $tags->[$i]:$value->[$i]";
+ }
}#if value
}
##Add how many queries generated
} 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;
} 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";
$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" ] );
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;
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'};
+ }
}
}
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;
+ my $op;
+
+ if ($QParser) {
+ $titleindex = 'title|exact';
+ $authorindex = 'author|exact';
+ $op = '&&';
+ } else {
+ $titleindex = 'ti,ext';
+ $authorindex = 'au,ext';
+ $op = 'and';
+ }
+
$result->{title} =~ s /\\//g;
$result->{title} =~ s /\"//g;
$result->{title} =~ s /\(//g;
# 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 = "$titleindex:\"$result->{title}\"";
if ( $result->{author} ) {
$result->{author} =~ s /\\//g;
$result->{author} =~ s /\"//g;
# remove valid operators
$result->{author} =~ s/(and|or|not)//g;
- $query .= " and au,ext=$result->{author}";
+ $query .= " $op $authorindex:\"$result->{author}\"";
}
}
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
=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);
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..');
}
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
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
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 : ();
# initialize the variables we're passing back
my $query_cgi;
- my $query_desc;
my $query_type;
my $limit;
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
# 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
);
# 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 );
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 );
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 = [];
];
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');
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;
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;
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;
# 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');
my $QueryWeightFields = 0;
my $QueryFuzzy = 0;
my $QueryRemoveStopwords = 0;
+my $UseQueryParser = 0;
my $contextmodule = new Test::MockModule('C4::Context');
$contextmodule->mock('_new_dbh', sub {
my $dbh = DBI->connect( 'DBI:Mock:', '', '' )
return $QueryFuzzy;
} elsif ($pref eq 'QueryRemoveStopwords') {
return $QueryRemoveStopwords;
+ } elsif ($pref eq 'UseQueryParser') {
+ return $UseQueryParser;
} elsif ($pref eq 'maxRecordsForFacets') {
return 20;
} elsif ($pref eq 'FacetLabelTruncationLength') {
);
return \%hash;
});
+$contextmodule->mock('queryparser', sub {
+ my $QParser = Koha::QueryParser::Driver::PQF->new();
+ $QParser->load_config("$datadir/etc/searchengine/queryparser.yaml");
+ return $QParser;
+});
my $context = new C4::Context("$datadir/etc/koha-conf.xml");
$context->set_context();
$searchmodule->mock('SimpleSearch', sub {
my $query = shift;
- is($query, "Heading,wrdl=$term", "Searching for expected term '$term' for exploding") or return '', [], 0;
+ is($query, "he:$term", "Searching for expected term '$term' for exploding") or return '', [], 0;
my $record = MARC::Record->new;
if ($query =~ m/Arizona/) {
return '', [ $record->as_usmarc() ], 1;
});
+$UseQueryParser = 1;
$term = 'Arizona';
( $error, $query, $simple_query, $query_cgi,
$query_desc, $limit, $limit_cgi, $limit_desc,
( $error, $query, $simple_query, $query_cgi,
$query_desc, $limit, $limit_cgi, $limit_desc,
-$stopwords_removed, $query_type ) = buildQuery([], [ "su-br:$term" ], [ ], [ ], [], 0, 'en');
+$stopwords_removed, $query_type ) = buildQuery([], [ "su-br($term)" ], [ ], [ ], [], 0, 'en');
matchesExplodedTerms("Simple search for broader subjects", $query, 'Arizona', 'United States');
( $error, $query, $simple_query, $query_cgi,
$query_desc, $limit, $limit_cgi, $limit_desc,
-$stopwords_removed, $query_type ) = buildQuery([], [ "su-na:$term" ], [ ], [ ], [], 0, 'en');
+$stopwords_removed, $query_type ) = buildQuery([], [ "su-na($term)" ], [ ], [ ], [], 0, 'en');
matchesExplodedTerms("Simple search for narrower subjects", $query, 'Arizona', 'Maricopa County', 'Navajo County', 'Pima County');
( $error, $query, $simple_query, $query_cgi,
$query_desc, $limit, $limit_cgi, $limit_desc,
-$stopwords_removed, $query_type ) = buildQuery([], [ "su-rl:$term" ], [ ], [ ], [], 0, 'en');
+$stopwords_removed, $query_type ) = buildQuery([], [ "su-rl($term)" ], [ ], [ ], [], 0, 'en');
matchesExplodedTerms("Simple search for related subjects", $query, 'Arizona', 'United States', 'Maricopa County', 'Navajo County', 'Pima County');
( $error, $query, $simple_query, $query_cgi,
$query_desc, $limit, $limit_cgi, $limit_desc,
-$stopwords_removed, $query_type ) = buildQuery([], [ "history and su-rl:$term" ], [ ], [ ], [], 0, 'en');
+$stopwords_removed, $query_type ) = buildQuery([], [ "history && su-rl($term)" ], [ ], [ ], [], 0, 'en');
matchesExplodedTerms("Simple search for related subjects and keyword 'history' searches related subjects", $query, 'Arizona', 'United States', 'Maricopa County', 'Navajo County', 'Pima County');
like($query, qr/history/, "Simple search for related subjects and keyword 'history' searches for 'history'");
sub matchesExplodedTerms {
my ($message, $query, @terms) = @_;
- my $match = "(( or )?\\((" . join ('|', map { "su=\"$_\"" } @terms) . ")\\)){" . scalar(@terms) . "}";
+ my $match = '(' . join ('|', map { " \@attr 1=Subject \@attr 4=1 \"$_\"" } @terms) . "){" . scalar(@terms) . "}";
like($query, qr/$match/, $message);
}