Bugzilla – Attachment 99811 Details for
Bug 24735
Remove QueryParser-related code
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 24735: Remove QueryParser-related code
Bug-24735-Remove-QueryParser-related-code.patch (text/plain), 209.64 KB, created by
Katrin Fischer
on 2020-02-29 11:13:15 UTC
(
hide
)
Description:
Bug 24735: Remove QueryParser-related code
Filename:
MIME Type:
Creator:
Katrin Fischer
Created:
2020-02-29 11:13:15 UTC
Size:
209.64 KB
patch
obsolete
>From 1f28210f99df4f880e57877805ba4e417f8ba80e Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Wed, 26 Feb 2020 13:28:27 +0100 >Subject: [PATCH] Bug 24735: Remove QueryParser-related code > >At the last development meeting we have voted to remove the >QueryParser-related code >https://wiki.koha-community.org/wiki/Development_IRC_meeting_19_February_2020 > >Hea tells us that it has not been adopted, and the code/bug tracker that >it is not really usable as it. As nobody is willing to work on it, we >decided to remove it instead. > >Test plan: > % prove t/db_dependent/Search.t >must return green > >See commits from bug 9239 and confirm that the code is removed in this >patch. > >Also play with the search on the UI and confirm that you do not see >obvious regressions > >Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> > >Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de> >--- > C4/AuthoritiesMarc.pm | 34 +- > C4/Context.pm | 47 - > C4/Matcher.pm | 28 +- > C4/Search.pm | 174 +- > C4/UsageStats.pm | 1 - > Koha/QueryParser/Driver/PQF.pm | 925 -------- > Koha/QueryParser/Driver/PQF/Util.pm | 54 - > Koha/QueryParser/Driver/PQF/query_plan.pm | 70 - > Koha/QueryParser/Driver/PQF/query_plan/facet.pm | 46 - > Koha/QueryParser/Driver/PQF/query_plan/filter.pm | 51 - > Koha/QueryParser/Driver/PQF/query_plan/modifier.pm | 50 - > Koha/QueryParser/Driver/PQF/query_plan/node.pm | 106 - > .../QueryParser/Driver/PQF/query_plan/node/atom.pm | 49 - > OpenILS/QueryParser.pm | 2242 -------------------- > about.pl | 29 - > cataloguing/addbooks.pl | 11 +- > .../value_builder/marc21_linking_section.pl | 9 +- > cataloguing/value_builder/unimarc_field_4XX.pl | 9 +- > debian/templates/koha-conf-site.xml.in | 1 - > etc/koha-conf.xml | 1 - > etc/searchengine/queryparser.yaml | 1647 -------------- > installer/data/mysql/atomicupdate/bug_xxxxx.perl | 9 + > installer/data/mysql/sysprefs.sql | 1 - > koha-tmpl/intranet-tmpl/prog/en/modules/about.tt | 23 +- > .../en/modules/admin/preferences/cataloguing.pref | 4 +- > .../en/modules/admin/preferences/searching.pref | 9 +- > labels/label-item-search.pl | 21 +- > misc/migration_tools/bulkmarcimport.pl | 18 +- > serials/subscription-bib-search.pl | 2 +- > t/QueryParser.t | 136 -- > t/db_dependent/QueryParser.t | 70 - > t/db_dependent/Search.t | 110 +- > t/db_dependent/UsageStats.t | 1 - > t/db_dependent/default_search_class.pl | 37 - > t/db_dependent/zebra_config.pl | 1 - > 35 files changed, 44 insertions(+), 5982 deletions(-) > delete mode 100644 Koha/QueryParser/Driver/PQF.pm > delete mode 100644 Koha/QueryParser/Driver/PQF/Util.pm > delete mode 100644 Koha/QueryParser/Driver/PQF/query_plan.pm > delete mode 100644 Koha/QueryParser/Driver/PQF/query_plan/facet.pm > delete mode 100644 Koha/QueryParser/Driver/PQF/query_plan/filter.pm > delete mode 100644 Koha/QueryParser/Driver/PQF/query_plan/modifier.pm > delete mode 100644 Koha/QueryParser/Driver/PQF/query_plan/node.pm > delete mode 100644 Koha/QueryParser/Driver/PQF/query_plan/node/atom.pm > delete mode 100644 OpenILS/QueryParser.pm > delete mode 100644 etc/searchengine/queryparser.yaml > create mode 100644 installer/data/mysql/atomicupdate/bug_xxxxx.perl > delete mode 100644 t/QueryParser.t > delete mode 100644 t/db_dependent/QueryParser.t > delete mode 100755 t/db_dependent/default_search_class.pl > >diff --git a/C4/AuthoritiesMarc.pm b/C4/AuthoritiesMarc.pm >index c5b2131408..9d059a0266 100644 >--- a/C4/AuthoritiesMarc.pm >+++ b/C4/AuthoritiesMarc.pm >@@ -113,8 +113,6 @@ sub SearchAuthorities { > $sortby="" unless $sortby; > 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 >@@ -133,9 +131,6 @@ sub SearchAuthorities { > if ($n>1){ > while ($n>1){$query= "\@or ".$query;$n--;} > } >- if ($QParser) { >- $qpquery .= '(authtype:' . join('|| authtype:', @auths) . ')'; >- } > } > > my $dosearch; >@@ -203,9 +198,6 @@ sub SearchAuthorities { > $q2 .= $attr; > $dosearch = 1; > ++$attr_cnt; >- if ($QParser) { >- $qpquery .= " $tags->[$i]:\"$value->[$i]\""; >- } > } #if value > } > ##Add how many queries generated >@@ -226,21 +218,8 @@ sub SearchAuthorities { > } elsif ($sortby eq 'AuthidDsc') { > $orderstring = '@attr 7=2 @attr 4=109 @attr 1=Local-Number 0'; > } >- if ($QParser) { >- $qpquery .= ' all:all' unless $value->[0]; >- >- if ( $value->[0] =~ m/^qp=(.*)$/ ) { >- $qpquery = $1; >- } >- >- $qpquery .= " #$sortby" unless $sortby eq ''; >- >- $QParser->parse( $qpquery ); >- $query = $QParser->target_syntax('authorityserver'); >- } else { >- $query=($query?$query:"\@attr 1=_ALLRECORDS \@attr 2=103 ''"); >- $query="\@or $orderstring $query" if $orderstring; >- } >+ $query=($query?$query:"\@attr 1=_ALLRECORDS \@attr 2=103 ''"); >+ $query="\@or $orderstring $query" if $orderstring; > > $offset = 0 if not defined $offset or $offset < 0; > my $counter = $offset; >@@ -758,14 +737,7 @@ sub FindDuplicateAuthority { > my $auth_tag_to_report = Koha::Authority::Types->find($authtypecode)->auth_tag_to_report; > # warn "record :".$record->as_formatted." auth_tag_to_report :$auth_tag_to_report"; > # build a request for SearchAuthorities >- my $QParser; >- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); >- my $op; >- if ($QParser) { >- $op = '&&'; >- } else { >- $op = 'AND'; >- } >+ my $op = 'AND'; > $authtypecode =~ s#/#\\/#; # GENRE/FORM contains forward slash which is a reserved character > my $query='at:'.$authtypecode.' '; > my $filtervalues=qr([\001-\040\Q!'"`#$%&*+,-./:;<=>?@(){[}_|~\E\]]); >diff --git a/C4/Context.pm b/C4/Context.pm >index 0fe571da2a..36d68b195c 100644 >--- a/C4/Context.pm >+++ b/C4/Context.pm >@@ -783,53 +783,6 @@ sub restore_dbh > # return something, then this function should, too. > } > >-=head2 queryparser >- >- $queryparser = C4::Context->queryparser >- >-Returns a handle to an initialized Koha::QueryParser::Driver::PQF object. >- >-=cut >- >-sub queryparser { >- my $self = shift; >- unless (defined $context->{"queryparser"}) { >- $context->{"queryparser"} = &_new_queryparser(); >- } >- >- return >- defined( $context->{"queryparser"} ) >- ? $context->{"queryparser"}->new >- : undef; >-} >- >-=head2 _new_queryparser >- >-Internal helper function to create a new QueryParser object. QueryParser >-is loaded dynamically so as to keep the lack of the QueryParser library from >-getting in anyone's way. >- >-=cut >- >-sub _new_queryparser { >- my $qpmodules = { >- 'OpenILS::QueryParser' => undef, >- 'Koha::QueryParser::Driver::PQF' => undef >- }; >- if ( can_load( 'modules' => $qpmodules ) ) { >- my $QParser = Koha::QueryParser::Driver::PQF->new(); >- my $config_file = $context->config('queryparser_config'); >- $config_file ||= '/etc/koha/searchengine/queryparser.yaml'; >- if ( $QParser->load_config($config_file) ) { >- # Set 'keyword' as the default search class >- $QParser->default_search_class('keyword'); >- # TODO: allow indexes to be configured in the database >- return $QParser; >- } >- } >- return; >-} >- > =head2 userenv > > C4::Context->userenv; >diff --git a/C4/Matcher.pm b/C4/Matcher.pm >index 949920c7e1..b7389d7bc6 100644 >--- a/C4/Matcher.pm >+++ b/C4/Matcher.pm >@@ -622,26 +622,18 @@ 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; > >- # FIXME - because of a bug in QueryParser, an expression ofthe >- # format 'isbn:"isbn1" || isbn:"isbn2" || isbn"isbn3"...' >- # does not get parsed correctly, so we will not >- # do AggressiveMatchOnISBN if UseQueryParser is on > @source_keys = C4::Koha::GetVariationsOfISBNs(@source_keys) > if ( $matchpoint->{index} =~ /^isbn$/i >- && C4::Context->preference('AggressiveMatchOnISBN') ) >- && !C4::Context->preference('UseQueryParser'); >+ && C4::Context->preference('AggressiveMatchOnISBN') ); > > @source_keys = C4::Koha::GetVariationsOfISSNs(@source_keys) > if ( $matchpoint->{index} =~ /^issn$/i >- && C4::Context->preference('AggressiveMatchOnISSN') ) >- && !C4::Context->preference('UseQueryParser'); >+ && C4::Context->preference('AggressiveMatchOnISSN') ); > > # build query > my $query; >@@ -650,18 +642,10 @@ sub get_matches { > my $total_hits; > if ( $self->{'record_type'} eq 'biblio' ) { > >- #NOTE: The QueryParser can't handle the CCL syntax of 'qualifier','qualifier', so fallback to non-QueryParser. >- #NOTE: You can see this in C4::Search::SimpleSearch() as well in a different way. >- if ($QParser && $matchpoint->{'index'} !~ m/\w,\w/) { >- $query = join( " || ", >- map { "$matchpoint->{'index'}:$_" } @source_keys ); >- } >- else { >- my $phr = ( C4::Context->preference('AggressiveMatchOnISBN') || C4::Context->preference('AggressiveMatchOnISSN') ) ? ',phr' : q{}; >- $query = join( " OR ", >- map { "$matchpoint->{'index'}$phr=\"$_\"" } @source_keys ); >- #NOTE: double-quote the values so you don't get a "Embedded truncation not supported" error when a term has a ? in it. >- } >+ my $phr = ( C4::Context->preference('AggressiveMatchOnISBN') || C4::Context->preference('AggressiveMatchOnISSN') ) ? ',phr' : q{}; >+ $query = join( " OR ", >+ map { "$matchpoint->{'index'}$phr=\"$_\"" } @source_keys ); >+ #NOTE: double-quote the values so you don't get a "Embedded truncation not supported" error when a term has a ? in it. > > # Use state variables to avoid recreating the objects every time. > # With Elasticsearch this also avoids creating a massive amount of >diff --git a/C4/Search.pm b/C4/Search.pm >index 8d5ca863e9..b4868d1bff 100644 >--- a/C4/Search.pm >+++ b/C4/Search.pm >@@ -99,22 +99,10 @@ sub FindDuplicate { > $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 = '&&'; >- $QParser->custom_data->{'QueryAutoTruncate'} = C4::Context->preference('QueryAutoTruncate'); >- } else { >- $titleindex = 'ti,ext'; >- $authorindex = 'au,ext'; >- $op = 'and'; >- } >+ >+ my $titleindex = 'ti,ext'; >+ my $authorindex = 'au,ext'; >+ my $op = 'and'; > > $result->{title} =~ s /\\//g; > $result->{title} =~ s /\"//g; >@@ -233,25 +221,12 @@ 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/)); >- if ($QParser) { >- $QParser->custom_data->{'QueryAutoTruncate'} = C4::Context->preference('QueryAutoTruncate'); >- } >- > # Initialize & Search Zebra > for ( my $i = 0 ; $i < @servers ; $i++ ) { > eval { > $zconns[$i] = C4::Context->Zconn( $servers[$i], 1 ); >- if ($QParser) { >- $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 unless $options{skip_normalize}; >- $zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]); >- } >+ $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] ); > > # error handling >@@ -1277,134 +1252,6 @@ sub getIndexes{ > return \@indexes; > } > >-=head2 _handle_exploding_index >- >- my $query = _handle_exploding_index($index, $term) >- >-Callback routine to generate the search for "exploding" indexes (i.e. >-those indexes which are turned into multiple or-connected searches based >-on authority data). >- >-=cut >- >-sub _handle_exploding_index { >- my ($QParser, $filter, $params, $negate, $server) = @_; >- my $index = $filter; >- my $term = join(' ', @$params); >- >- return unless ($index =~ m/(su-br|su-na|su-rl)/ && $term); >- >- my $marcflavour = C4::Context->preference('marcflavour'); >- >- my $codesubfield = $marcflavour eq 'UNIMARC' ? '5' : 'w'; >- my $wantedcodes = ''; >- my @subqueries = ( "\@attr 1=Subject \@attr 4=1 \"$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..'); >- if (@references) { >- if ($index eq 'su-br') { >- $wantedcodes = 'g'; >- } elsif ($index eq 'su-na') { >- $wantedcodes = 'h'; >- } elsif ($index eq 'su-rl') { >- $wantedcodes = ''; >- } >- foreach my $reference (@references) { >- my $codes = $reference->subfield($codesubfield); >- push @subqueries, '@attr 1=Subject @attr 4=1 "' . $reference->as_string('abcdefghijlmnopqrstuvxyz') . '"' if (($codes && $codes eq $wantedcodes) || !$wantedcodes); >- } >- } >- } >- my $query = ' @or ' x (scalar(@subqueries) - 1) . join(' ', @subqueries); >- return $query; >-} >- >-=head2 parseQuery >- >- ( $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 >-buildQuery's input. If it can handle the input, it returns a query that >-buildQuery will not try to parse. >- >-=cut >- >-sub parseQuery { >- my ( $operators, $operands, $indexes, $limits, $sort_by, $scan, $lang) = @_; >- >- my @operators = $operators ? @$operators : (); >- my @indexes = $indexes ? @$indexes : (); >- my @operands = $operands ? @$operands : (); >- my @limits = $limits ? @$limits : (); >- my @sort_by = $sort_by ? @$sort_by : (); >- >- my $query = $operands[0]; >- my $index; >- my $term; >- my $query_desc; >- >- 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, @indexes) ); >- undef $QParser if (scalar @limits > 0); >- >- if ($QParser) >- { >- $QParser->custom_data->{'QueryAutoTruncate'} = C4::Context->preference('QueryAutoTruncate'); >- $query = ''; >- for ( my $ii = 0 ; $ii <= @operands ; $ii++ ) { >- next unless $operands[$ii]; >- $query .= $operators[$ii-1] && $operators[ $ii - 1 ] eq 'or' ? ' || ' : ' && ' >- if ($query); >- if ( $operands[$ii] =~ /^[^"]\W*[-|_\w]*:\w.*[^"]$/ ) { >- $query .= $operands[$ii]; >- } >- elsif ( $indexes[$ii] && $indexes[$ii] =~ m/su-/ ) { >- $query .= $indexes[$ii] . '(' . $operands[$ii] . ')'; >- } >- else { >- $query .= >- ( $indexes[$ii] ? "$indexes[$ii]:" : '' ) . $operands[$ii]; >- } >- } >- foreach my $limit (@limits) { >- } >- if ( scalar(@sort_by) > 0 ) { >- my $modifier_re = >- '#(' . join( '|', @{ $QParser->modifiers } ) . ')'; >- $query =~ s/$modifier_re//g; >- foreach my $modifier (@sort_by) { >- $query .= " #$modifier"; >- } >- } >- >- $query_desc = $query; >- $query_desc =~ s/\s+/ /g; >- if ( C4::Context->preference("QueryWeightFields") ) { >- } >- $QParser->add_bib1_filter_map( 'su-br' => 'biblioserver' => >- { 'target_syntax_callback' => \&_handle_exploding_index } ); >- $QParser->add_bib1_filter_map( 'su-na' => 'biblioserver' => >- { 'target_syntax_callback' => \&_handle_exploding_index } ); >- $QParser->add_bib1_filter_map( 'su-rl' => 'biblioserver' => >- { 'target_syntax_callback' => \&_handle_exploding_index } ); >- $QParser->parse($query); >- $operands[0] = "pqf=" . $QParser->target_syntax('biblioserver'); >- } >- else { >- require Koha::QueryParser::Driver::PQF; >- my $modifier_re = '#(' . join( '|', @{Koha::QueryParser::Driver::PQF->modifiers}) . ')'; >- s/$modifier_re//g for @operands; >- } >- >- return ( $operators, \@operands, $indexes, $limits, $sort_by, $scan, $lang, $query_desc); >-} >- > =head2 buildQuery > > ( $error, $query, >@@ -1427,7 +1274,6 @@ sub buildQuery { > warn "---------\nEnter buildQuery\n---------" if $DEBUG; > > 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 : (); >@@ -1487,12 +1333,8 @@ sub buildQuery { > return ( undef, $', $', "q=cql=".uri_escape_utf8($'), $', '', '', '', 'cql' ); > } > if ( $query =~ /^pqf=/ ) { >- if ($query_desc) { >- $query_cgi = "q=".uri_escape_utf8($query_desc); >- } else { >- $query_desc = $'; >- $query_cgi = "q=pqf=".uri_escape_utf8($'); >- } >+ $query_desc = $'; >+ $query_cgi = "q=pqf=".uri_escape_utf8($'); > return ( undef, $', $', $query_cgi, $query_desc, '', '', '', 'pqf' ); > } > >diff --git a/C4/UsageStats.pm b/C4/UsageStats.pm >index 0d864af01c..7b3b8ea668 100644 >--- a/C4/UsageStats.pm >+++ b/C4/UsageStats.pm >@@ -304,7 +304,6 @@ sub BuildReport { > TraceCompleteSubfields > TraceSubjectSubdivisions > UseICU >- UseQueryParser > defaultSortField > displayFacetCount > OPACdefaultSortField >diff --git a/Koha/QueryParser/Driver/PQF.pm b/Koha/QueryParser/Driver/PQF.pm >deleted file mode 100644 >index 01a9674d6d..0000000000 >--- a/Koha/QueryParser/Driver/PQF.pm >+++ /dev/null >@@ -1,925 +0,0 @@ >-package Koha::QueryParser::Driver::PQF; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use base qw(OpenILS::QueryParser Class::Accessor); >- >-use strict; >-use warnings; >- >-use Module::Load::Conditional qw(can_load); >-use Koha::QueryParser::Driver::PQF::Util; >-use Koha::QueryParser::Driver::PQF::query_plan; >-use Koha::QueryParser::Driver::PQF::query_plan::facet; >-use Koha::QueryParser::Driver::PQF::query_plan::filter; >-use Koha::QueryParser::Driver::PQF::query_plan::modifier; >-use Koha::QueryParser::Driver::PQF::query_plan::node; >-use Koha::QueryParser::Driver::PQF::query_plan::node::atom; >-use Koha::QueryParser::Driver::PQF::query_plan::node::atom; >- >- >-=head1 NAME >- >-Koha::QueryParser::Driver::PQF - QueryParser driver for PQF >- >-=head1 SYNOPSIS >- >- use Koha::QueryParser::Driver::PQF; >- my $QParser = Koha::QueryParser::Driver::PQF->new(%args); >- >-=head1 DESCRIPTION >- >-Main entrypoint into the QueryParser PQF driver. PQF is the Prefix Query >-Language, the syntax used to serialize Z39.50 queries. >- >-=head1 ACCESSORS >- >-In order to simplify Bib-1 attribute mapping, this driver uses Class::Accessor >-for accessing the following maps: >- >-=over 4 >- >-=item B<bib1_field_map> - search class/field Bib-1 mappings >- >-=item B<bib1_modifier_map> - search modifier mappings >- >-=item B<bib1_filter_map> - search filter mappings >- >-=item B<bib1_relevance_bump_map> - relevance bump mappings >- >-=back >- >-=cut >- >-__PACKAGE__->mk_accessors(qw(bib1_field_map bib1_modifier_map bib1_filter_map bib1_relevance_bump_map)); >- >-=head1 FUNCTIONS >- >-=cut >- >-=head2 get >- >-Overridden accessor method for Class::Accessor. (Do not call directly) >- >-=cut >- >-sub get { >- my $self = shift; >- return $self->_map(@_); >-} >- >-=head2 set >- >-Overridden mutator method for Class::Accessor. (Do not call directly) >- >-=cut >- >-sub set { >- my $self = shift; >- return $self->_map(@_); >-} >- >-=head2 add_bib1_field_map >- >- $QParser->add_bib1_field_map($class => $field => $server => \%attributes); >- >- $QParser->add_bib1_field_map('author' => 'personal' => 'biblioserver' => >- { '1' => '1003' }); >- >-Adds a search field<->bib1 attribute mapping for the specified server. The >-%attributes hash contains maps Bib-1 Attributes to the appropropriate >-values. Not all attributes must be specified. >- >-=cut >- >-sub add_bib1_field_map { >- my ($self, $class, $field, $server, $attributes) = @_; >- >- $self->add_search_field( $class => $field ); >- return $self->_add_field_mapping($self->bib1_field_map, $class, $field, $server, $attributes); >-} >- >-=head2 add_bib1_modifier_map >- >- $QParser->add_bib1_modifier_map($name => $server => \%attributes); >- >- $QParser->add_bib1_modifier_map('ascending' => 'biblioserver' => >- { '7' => '1' }); >- >-Adds a search modifier<->bib1 attribute mapping for the specified server. The >-%attributes hash contains maps Bib-1 Attributes to the appropropriate >-values. Not all attributes must be specified. >- >-=cut >- >-sub add_bib1_modifier_map { >- my ($self, $name, $server, $attributes) = @_; >- >- $self->add_search_modifier( $name ); >- >- return $self->_add_mapping($self->bib1_modifier_map, $name, $server, $attributes); >-} >- >-=head2 add_bib1_filter_map >- >- $QParser->add_bib1_filter_map($name => $server => \%attributes); >- >- $QParser->add_bib1_filter_map('date' => 'biblioserver' => >- { 'callback' => &_my_callback }); >- >-Adds a search filter<->bib1 attribute mapping for the specified server. The >-%attributes hash maps Bib-1 Attributes to the appropropriate values and >-provides a callback for the filter. Not all attributes must be specified. >- >-=cut >- >-sub add_bib1_filter_map { >- my ($self, $name, $server, $attributes) = @_; >- >- $self->add_search_filter( $name, $attributes->{'callback'} ); >- >- return $self->_add_mapping($self->bib1_filter_map, $name, $server, $attributes); >-} >- >-=head2 add_relevance_bump >- >- $QParser->add_relevance_bump($class, $field, $server, $multiplier, $active); >- $QParser->add_relevance_bump('title' => 'exact' => 'biblioserver' => 34, 1); >- >-Add a relevance bump to the specified field. When searching for a class without >-any fields, all the relevance bumps for the specified class will be 'OR'ed >-together. >- >-=cut >- >-sub add_relevance_bump { >- my ($self, $class, $field, $server, $multiplier, $active) = @_; >- my $attributes = { '9' => $multiplier, '2' => '102', 'active' => $active }; >- >- $self->add_search_field( $class => $field ); >- return $self->_add_field_mapping($self->bib1_relevance_bump_map, $class, $field, $server, $attributes); >-} >- >- >-=head2 target_syntax >- >- my $pqf = $QParser->target_syntax($server, [$query]); >- my $pqf = $QParser->target_syntax('biblioserver', 'author|personal:smith'); >- print $pqf; # assuming all the indexes are configured, >- # prints '@attr 1=1003 @attr 4=6 "smith"' >- >-Transforms the current or specified query into a PQF query string for the >-specified server. >- >-=cut >- >-sub target_syntax { >- my ($self, $server, $query) = @_; >- my $pqf = ''; >- $self->parse($query) if $query; >- warn "QP query for $server: " . $self->query . "\n" if $self->debug; >- $pqf = $self->parse_tree->target_syntax($server); >- warn "PQF query: $pqf\n" if $self->debug; >- $pqf =~ s/ +/ /g; >- $pqf =~ s/^ //; >- $pqf =~ s/ $//; >- return $pqf; >-} >- >-=head2 date_filter_target_callback >- >- $QParser->add_bib1_filter_map($server, { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => 'pubdate' }); >- >-Callback for date filters. Note that although the first argument is the QParser >-object, this is technically not an object-oriented routine. This has no >-real-world implications. >- >-=cut >- >-sub date_filter_target_callback { >- my ($QParser, $filter, $params, $negate, $server) = @_; >- my $attr_string = $QParser->bib1_mapping_by_name( 'filter', $filter, $server )->{'attr_string'}; >- my $pqf = ''; >- foreach my $datespec (@$params) { >- my $datepqf = ' '; >- if ($datespec) { >- if ($datespec =~ m/(.*)-(.*)/) { >- if ($1) { >- $datepqf .= $attr_string . ' @attr 2=4 "' . $1 . '"'; >- } >- if ($2) { >- $datepqf .= $attr_string . ' @attr 2=2 "' . $2 . '"'; >- $datepqf = ' @and ' . $datepqf if $1; >- } >- } else { >- $datepqf .= $attr_string . ' "' . $datespec . '"'; >- } >- } >- $pqf = ' @or ' . ($negate ? '@not @attr 1=_ALLRECORDS @attr 2=103 "" ' : '') . $pqf if $pqf; >- $pqf .= $datepqf; >- } >- return $pqf; >-} >- >-=head2 _map >- >- return $self->_map('bib1_field_map', $map); >- >-Retrieves or sets a map. >- >-=cut >- >-sub _map { >- my ($self, $name, $map) = @_; >- $self->custom_data->{$name} ||= {}; >- $self->custom_data->{$name} = $map if ($map); >- return $self->custom_data->{$name}; >-} >- >-=head2 _add_mapping >- >- return $self->_add_mapping($map, $name, $server, $attributes) >- >-Adds a mapping. Note that this is not used for mappings relating to fields. >- >-=cut >- >-sub _add_mapping { >- my ($self, $map, $name, $server, $attributes) = @_; >- >- my $attr_string = Koha::QueryParser::Driver::PQF::Util::attributes_to_attr_string($attributes); >- $attributes->{'attr_string'} = $attr_string; >- >- $map->{'by_name'}{$name}{$server} = $attributes; >- $map->{'by_attr'}{$server}{$attr_string} = { 'name' => $name, %$attributes }; >- >- return $map; >-} >- >-=head2 _add_field_mapping >- >- return $self->_add_field_mapping($map, $class, $field, $server, $attributes) >- >-Adds a mapping for field-related data. >- >-=cut >- >-sub _add_field_mapping { >- my ($self, $map, $class, $field, $server, $attributes) = @_; >- my $attr_string = Koha::QueryParser::Driver::PQF::Util::attributes_to_attr_string($attributes); >- $attributes->{'attr_string'} = $attr_string; >- >- $map->{'by_name'}{$class}{$field}{$server} = $attributes; >- $map->{'by_attr'}{$server}{$attr_string} = { 'classname' => $class, 'field' => $field, %$attributes }; >- return $map; >-} >- >- >-=head2 bib1_mapping_by_name >- >- my $attributes = $QParser->bib1_mapping_by_name($type, $name[, $subname], $server); >- my $attributes = $QParser->bib1_mapping_by_name('field', 'author', 'personal', 'biblioserver'); >- my $attributes = $QParser->bib1_mapping_by_name('filter', 'pubdate', 'biblioserver'); >- >-Retrieve the Bib-1 attribute set associated with the specified mapping. >-=cut >- >-sub bib1_mapping_by_name { >- my $server = pop; >- my ($self, $type, $name, $field) = @_; >- >- return unless ($server && $name); >- return unless ($type eq 'field' || $type eq 'modifier' || $type eq 'filter' || $type eq 'relevance_bump'); >- if ($type eq 'field' || $type eq 'relevance_bump') { >- # Unfortunately field is a special case thanks to the class->field hierarchy >- return $self->_map('bib1_' . $type . '_map')->{'by_name'}{$name}{$field}{$server} if $field; >- return $self->_map('bib1_' . $type . '_map')->{'by_name'}{$name}; >- } else { >- return $self->_map('bib1_' . $type . '_map')->{'by_name'}{$name}{$server}; >- } >-} >- >-=head2 bib1_mapping_by_attr >- >- my $field = $QParser->bib1_mapping_by_attr($type, $server, \%attr); >- my $field = $QParser->bib1_mapping_by_attr('field', 'biblioserver', {'1' => '1004'}); >- print $field->{'classname'}; # prints "author" >- print $field->{'field'}; # prints "personal" >- >-Retrieve the search field/modifier/filter used for the specified Bib-1 attribute set. >- >-=cut >- >-sub bib1_mapping_by_attr { >- my ($self, $type, $server, $attributes) = @_; >- return unless ($server && $attributes); >- >- my $attr_string = Koha::QueryParser::Driver::PQF::Util::attributes_to_attr_string($attributes); >- >- return $self->bib1_mapping_by_attr_string($type, $server, $attr_string); >-} >- >-=head2 bib1_mapping_by_attr_string >- >- my $field = $QParser->bib1_mapping_by_attr_string($type, $server, $attr_string); >- my $field = $QParser->bib1_mapping_by_attr_string('field', 'biblioserver', '@attr 1=1004'); >- print $field->{'classname'}; # prints "author" >- print $field->{'field'}; # prints "personal" >- >-Retrieve the search field/modifier/filter used for the specified Bib-1 attribute string >-(i.e. PQF snippet). >- >-=cut >- >-sub bib1_mapping_by_attr_string { >- my ($self, $type, $server, $attr_string) = @_; >- return unless ($server && $attr_string); >- return unless ($type eq 'field' || $type eq 'modifier' || $type eq 'filter' || $type eq 'relevance_bump'); >- >- return $self->_map('bib1_' . $type . '_map')->{'by_attr'}{$server}{$attr_string}; >-} >- >-=head2 clear_all_configuration >- >- $QParser->clear_all_configuration >- >-Clear all configuration. This is a highly destructive method. You may >-not want to use it. >- >-=cut >- >-sub clear_all_configuration { >- my ($self) = @_; >- %OpenILS::QueryParser::parser_config = ( >- 'OpenILS::QueryParser' => { >- filters => [], >- modifiers => [], >- operators => { >- 'and' => '&&', >- 'or' => '||', >- float_start => '{{', >- float_end => '}}', >- group_start => '(', >- group_end => ')', >- required => '+', >- disallowed => '-', >- modifier => '#', >- negated => '!' >- } >- } >- ); >- return $self; >-} >- >-=head2 clear_all_mappings >- >- $QParser->clear_all_mappings >- >-Clear all bib-1 mappings. >- >-=cut >- >-sub clear_all_mappings { >- my ($self) = @_; >- >- foreach my $name (qw(field modifier filter relevance_bump)) { >- $self->custom_data->{'bib1_' . $name . '_map'} = { }; >- } >- return $self; >-} >- >- >-=head2 _canonicalize_field_map >- >-Convert a field map into its canonical form for serialization. Used only for >-fields and relevance bumps. >- >-=cut >- >-sub _canonicalize_field_map { >- my ( $map, $aliases ) = @_; >- my $canonical_map = {}; >- >- foreach my $class ( keys %{ $map->{'by_name'} } ) { >- $canonical_map->{$class} ||= {}; >- foreach my $field ( keys %{ $map->{'by_name'}->{$class} } ) { >- my $field_map = { >- 'index' => $field, >- 'label' => ucfirst($field), >- 'enabled' => '1', >- }; >- foreach >- my $server ( keys %{ $map->{'by_name'}->{$class}->{$field} } ) >- { >- $field_map->{'bib1_mapping'} ||= {}; >- $field_map->{'bib1_mapping'}->{$server} = >- $map->{'by_name'}->{$class}->{$field}->{$server}; >- delete $field_map->{'bib1_mapping'}->{$server}->{'attr_string'} >- if defined( >- $field_map->{'bib1_mapping'}->{$server} >- ->{'attr_string'} >- ); >- } >- if ($aliases) { >- $field_map->{'aliases'} = []; >- foreach my $alias ( @{ $aliases->{$class}->{$field} } ) { >- push @{ $field_map->{'aliases'} }, >- $alias; >- } >- } >- $canonical_map->{$class}->{$field} = $field_map; >- } >- } >- return $canonical_map; >-} >- >-=head2 _canonicalize_map >- >-Convert a map into its canonical form for serialization. Not used for fields. >- >-=cut >- >-sub _canonicalize_map { >- my ($map) = @_; >- my $canonical_map = {}; >- >- foreach my $name ( keys %{ $map->{'by_name'} } ) { >- $canonical_map->{$name} = { >- 'label' => ucfirst($name), >- 'enabled' => 1, >- 'bib1_mapping' => {} >- }; >- foreach my $server ( keys %{ $map->{'by_name'}->{$name} } ) { >- $canonical_map->{$name}->{'bib1_mapping'}->{$server} = >- $map->{'by_name'}->{$name}->{$server}; >- delete $canonical_map->{$name}->{'bib1_mapping'}->{$server} >- ->{'attr_string'} >- if defined( >- $canonical_map->{$name}->{'bib1_mapping'}->{$server} >- ->{'attr_string'} >- ); >- } >- } >- return $canonical_map; >-} >- >-=head2 serialize_mappings >- >- my $yaml = $QParser->serialize_mappings; >- my $json = $QParser->serialize_mappings('json'); >- >-Serialize Bib-1 mappings to YAML or JSON. >- >-=cut >- >-sub serialize_mappings { >- my ( $self, $format ) = @_; >- $format ||= 'yaml'; >- my $config; >- >- $config->{'field_mappings'} = >- _canonicalize_field_map( $self->bib1_field_map, >- $self->search_field_aliases ); >- $config->{'modifier_mappings'} = >- _canonicalize_map( $self->bib1_modifier_map ); >- $config->{'filter_mappings'} = _canonicalize_map( $self->bib1_filter_map ); >- $config->{'relevance_bumps'} = >- _canonicalize_field_map( $self->bib1_relevance_bump_map ); >- >- if ( $format eq 'json' && can_load( modules => { 'JSON' => undef } ) ) { >- return JSON::to_json($config); >- } >- elsif ( can_load( modules => { 'YAML::Any' => undef } ) ) { >- return YAML::Any::Dump($config); >- } >- return; >-} >- >-=head2 initialize >- >- $QParser->initialize( { 'bib1_field_mappings' => \%bib1_field_mappings, >- 'search_field_alias_mappings' => \%search_field_alias_mappings, >- 'bib1_modifier_mappings' => \%bib1_modifier_mappings, >- 'bib1_filter_mappings' => \%bib1_filter_mappings, >- 'relevance_bumps' => \%relevance_bumps }); >- >-Initialize the QueryParser mapping tables based on the provided configuration. >-This method was written to play nice with YAML configuration files loaded by load_config. >- >-=cut >- >-sub initialize { >- my ( $self, $args ) = @_; >- >- my $field_mappings = $args->{'field_mappings'}; >- my $modifier_mappings = $args->{'modifier_mappings'}; >- my $filter_mappings = $args->{'filter_mappings'}; >- my $relbumps = $args->{'relevance_bumps'}; >- my ( $server, $bib1_mapping ); >- foreach my $class ( keys %$field_mappings ) { >- foreach my $field ( keys %{ $field_mappings->{$class} } ) { >- if ( $field_mappings->{$class}->{$field}->{'enabled'} ) { >- while ( ( $server, $bib1_mapping ) = >- each >- %{ $field_mappings->{$class}->{$field}->{'bib1_mapping'} } ) >- { >- $self->add_bib1_field_map( >- $class => $field => $server => $bib1_mapping ); >- } >- foreach my $alias ( >- @{ $field_mappings->{$class}->{$field}->{'aliases'} } ) >- { >- $self->add_search_field_alias( $class => $field => $alias ); >- } >- } >- } >- } >- foreach my $modifier ( keys %$modifier_mappings ) { >- if ( $modifier_mappings->{$modifier}->{'enabled'} ) { >- while ( ( $server, $bib1_mapping ) = >- each %{ $modifier_mappings->{$modifier}->{'bib1_mapping'} } ) >- { >- $self->add_bib1_modifier_map( >- $modifier => $server => $bib1_mapping ); >- } >- } >- } >- foreach my $filter ( keys %$filter_mappings ) { >- if ( $filter_mappings->{$filter}->{'enabled'} ) { >- while ( ( $server, $bib1_mapping ) = >- each %{ $filter_mappings->{$filter}->{'bib1_mapping'} } ) >- { >- if ( $bib1_mapping->{'target_syntax_callback'} eq >- 'date_filter_target_callback' ) >- { >- $bib1_mapping->{'target_syntax_callback'} = >- \&Koha::QueryParser::Driver::PQF::date_filter_target_callback; >- } >- $self->add_bib1_filter_map( >- $filter => $server => $bib1_mapping ); >- } >- } >- } >- foreach my $class ( keys %$relbumps ) { >- foreach my $field ( keys %{ $relbumps->{$class} } ) { >- if ( $relbumps->{$class}->{$field}->{'enabled'} ) { >- while ( ( $server, $bib1_mapping ) = >- each %{ $relbumps->{$class}->{$field}->{'bib1_mapping'} } ) >- { >- $self->add_relevance_bump( >- $class => $field => $server => $bib1_mapping, >- 1 >- ); >- } >- } >- } >- } >- return $self; >-} >- >-=head2 load_config >- >- $QParser->load_config($file_name); >- >-Load a YAML file with a parser configuration. The YAML file should match the following format: >- >- --- >- field_mappings: >- author: >- "": >- aliases: >- - au >- bib1_mapping: >- biblioserver: >- 1: 1003 >- enabled: 1 >- index: '' >- label: '' >- conference: >- aliases: >- - conference >- - cfn >- bib1_mapping: >- biblioserver: >- 1: 1006 >- enabled: 1 >- index: conference >- label: Conference >- filter_mappings: >- acqdate: >- bib1_mapping: >- biblioserver: >- 1: Date-of-acquisition >- 4: 4 >- target_syntax_callback: date_filter_target_callback >- enabled: 1 >- label: Acqdate >- modifier_mappings: >- AuthidAsc: >- bib1_mapping: >- authorityserver: >- "": 0 >- 1: Local-Number >- 7: 1 >- op: "@or" >- enabled: 1 >- label: AuthidAsc >- ... >- >-=cut >- >-sub load_config { >- my ($self, $file) = @_; >- require YAML::Any; >- return unless ($file && -f $file); >- my $config = YAML::Any::LoadFile($file); >- return unless ($config); >- $self->initialize($config); >- return 1; >-} >- >-=head2 TEST_SETUP >- >- $QParser->TEST_SETUP >- >-This routine initializes the QueryParser driver with a reasonable set of >-defaults. This is intended only for testing. Although such test stubs are >-generally not included in Koha, this type of test stub is used by other >-QueryParser implementations, and it seems sensible to maintain consistency >-as much as possible. >- >-=cut >- >-sub TEST_SETUP { >- my ($self) = @_; >- >- $self->default_search_class( 'keyword' ); >- >- $self->add_bib1_field_map('keyword' => 'abstract' => 'biblioserver' => { '1' => '62' } ); >- $self->add_search_field_alias( 'keyword' => 'abstract' => 'ab' ); >- $self->add_bib1_field_map('keyword' => '' => 'biblioserver' => { '1' => '1016' } ); >- $self->add_search_field_alias( 'keyword' => '' => 'kw' ); >- $self->add_bib1_field_map('author' => '' => 'biblioserver' => { '1' => '1003' } ); >- $self->add_search_field_alias( 'author' => '' => 'au' ); >- $self->add_bib1_field_map('author' => 'personal' => 'biblioserver' => { '1' => '1004' } ); >- $self->add_bib1_field_map('author' => 'corporate' => 'biblioserver' => { '1' => '1005' } ); >- $self->add_search_field_alias( 'author' => 'corporate' => 'cpn' ); >- $self->add_bib1_field_map('author' => 'conference' => 'biblioserver' => { '1' => '1006' } ); >- $self->add_search_field_alias( 'author' => 'conference' => 'cfn' ); >- $self->add_bib1_field_map('keyword' => 'local-classification' => 'biblioserver' => { '1' => '20' } ); >- $self->add_search_field_alias( 'keyword' => 'local-classification' => 'lcn' ); >- $self->add_search_field_alias( 'keyword' => 'local-classification' => 'callnum' ); >- $self->add_bib1_field_map('keyword' => 'bib-level' => 'biblioserver' => { '1' => '1021' } ); >- $self->add_bib1_field_map('keyword' => 'code-institution' => 'biblioserver' => { '1' => '56' } ); >- $self->add_bib1_field_map('keyword' => 'language' => 'biblioserver' => { '1' => '54' } ); >- $self->add_search_field_alias( 'keyword' => 'language' => 'ln' ); >- $self->add_bib1_field_map('keyword' => 'record-type' => 'biblioserver' => { '1' => '1001' } ); >- $self->add_search_field_alias( 'keyword' => 'record-type' => 'rtype' ); >- $self->add_search_field_alias( 'keyword' => 'record-type' => 'mc-rtype' ); >- $self->add_search_field_alias( 'keyword' => 'record-type' => 'mus' ); >- $self->add_bib1_field_map('keyword' => 'content-type' => 'biblioserver' => { '1' => '1034' } ); >- $self->add_search_field_alias( 'keyword' => 'content-type' => 'ctype' ); >- $self->add_bib1_field_map('keyword' => 'lc-card-number' => 'biblioserver' => { '1' => '9' } ); >- $self->add_search_field_alias( 'keyword' => 'lc-card-number' => 'lc-card' ); >- $self->add_bib1_field_map('keyword' => 'local-number' => 'biblioserver' => { '1' => '12' } ); >- $self->add_search_field_alias( 'keyword' => 'local-number' => 'sn' ); >- $self->add_bib1_filter_map( 'biblioserver', 'copydate', { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => '30', '4' => '4' }); >- $self->add_bib1_filter_map( 'biblioserver', 'pubdate', { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => 'pubdate', '4' => '4' }); >- $self->add_bib1_filter_map( 'biblioserver', 'acqdate', { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => 'Date-of-acquisition', '4' => '4' }); >- $self->add_bib1_field_map('keyword' => 'isbn' => 'biblioserver' => { '1' => '7' } ); >- $self->add_search_field_alias( 'keyword' => 'isbn' => 'nb' ); >- $self->add_bib1_field_map('keyword' => 'issn' => 'biblioserver' => { '1' => '8' } ); >- $self->add_search_field_alias( 'keyword' => 'issn' => 'ns' ); >- $self->add_bib1_field_map('keyword' => 'identifier-standard' => 'biblioserver' => { '1' => '1007' } ); >- $self->add_search_field_alias( 'keyword' => 'identifier-standard' => 'ident' ); >- $self->add_bib1_field_map('keyword' => 'upc' => 'biblioserver' => { '1' => 'UPC' } ); >- $self->add_search_field_alias( 'keyword' => 'upc' => 'upc' ); >- $self->add_bib1_field_map('keyword' => 'ean' => 'biblioserver' => { '1' => 'EAN' } ); >- $self->add_search_field_alias( 'keyword' => 'ean' => 'ean' ); >- $self->add_bib1_field_map('keyword' => 'music' => 'biblioserver' => { '1' => 'Music-number' } ); >- $self->add_search_field_alias( 'keyword' => 'music' => 'music' ); >- $self->add_bib1_field_map('keyword' => 'stock-number' => 'biblioserver' => { '1' => '1028' } ); >- $self->add_search_field_alias( 'keyword' => 'stock-number' => 'stock-number' ); >- $self->add_bib1_field_map('keyword' => 'material-type' => 'biblioserver' => { '1' => '1031' } ); >- $self->add_search_field_alias( 'keyword' => 'material-type' => 'material-type' ); >- $self->add_bib1_field_map('keyword' => 'place-publication' => 'biblioserver' => { '1' => '59' } ); >- $self->add_search_field_alias( 'keyword' => 'place-publication' => 'pl' ); >- $self->add_bib1_field_map('keyword' => 'personal-name' => 'biblioserver' => { '1' => 'Personal-name' } ); >- $self->add_search_field_alias( 'keyword' => 'personal-name' => 'pn' ); >- $self->add_bib1_field_map('keyword' => 'publisher' => 'biblioserver' => { '1' => '1018' } ); >- $self->add_search_field_alias( 'keyword' => 'publisher' => 'pb' ); >- $self->add_bib1_field_map('keyword' => 'note' => 'biblioserver' => { '1' => '63' } ); >- $self->add_search_field_alias( 'keyword' => 'note' => 'nt' ); >- $self->add_bib1_field_map('keyword' => 'record-control-number' => 'biblioserver' => { '1' => '1045' } ); >- $self->add_search_field_alias( 'keyword' => 'record-control-number' => 'rcn' ); >- $self->add_bib1_field_map('subject' => '' => 'biblioserver' => { '1' => '21' } ); >- $self->add_search_field_alias( 'subject' => '' => 'su' ); >- $self->add_search_field_alias( 'subject' => '' => 'su-to' ); >- $self->add_search_field_alias( 'subject' => '' => 'su-geo' ); >- $self->add_search_field_alias( 'subject' => '' => 'su-ut' ); >- $self->add_bib1_field_map('subject' => 'name-personal' => 'biblioserver' => { '1' => '1009' } ); >- $self->add_search_field_alias( 'subject' => 'name-personal' => 'su-na' ); >- $self->add_bib1_field_map('title' => '' => 'biblioserver' => { '1' => '4' } ); >- $self->add_search_field_alias( 'title' => '' => 'ti' ); >- $self->add_bib1_field_map('title' => 'cover' => 'biblioserver' => { '1' => '36' } ); >- $self->add_search_field_alias( 'title' => 'cover' => 'title-cover' ); >- $self->add_bib1_field_map('keyword' => 'host-item' => 'biblioserver' => { '1' => '1033' } ); >- $self->add_bib1_field_map('keyword' => 'video-mt' => 'biblioserver' => { '1' => 'Video-mt' } ); >- $self->add_bib1_field_map('keyword' => 'graphics-type' => 'biblioserver' => { '1' => 'Graphic-type' } ); >- $self->add_bib1_field_map('keyword' => 'graphics-support' => 'biblioserver' => { '1' => 'Graphic-support' } ); >- $self->add_bib1_field_map('keyword' => 'type-of-serial' => 'biblioserver' => { '1' => 'Type-Of-Serial' } ); >- $self->add_bib1_field_map('keyword' => 'regularity-code' => 'biblioserver' => { '1' => 'Regularity-code' } ); >- $self->add_bib1_field_map('keyword' => 'material-type' => 'biblioserver' => { '1' => 'Material-type' } ); >- $self->add_bib1_field_map('keyword' => 'literature-code' => 'biblioserver' => { '1' => 'Literature-Code' } ); >- $self->add_bib1_field_map('keyword' => 'biography-code' => 'biblioserver' => { '1' => 'Biography-code' } ); >- $self->add_bib1_field_map('keyword' => 'illustration-code' => 'biblioserver' => { '1' => 'Illustration-code' } ); >- $self->add_bib1_field_map('title' => 'series' => 'biblioserver' => { '1' => '5' } ); >- $self->add_search_field_alias( 'title' => 'series' => 'title-series' ); >- $self->add_search_field_alias( 'title' => 'series' => 'se' ); >- $self->add_bib1_field_map('title' => 'uniform' => 'biblioserver' => { '1' => 'Title-uniform' } ); >- $self->add_search_field_alias( 'title' => 'uniform' => 'title-uniform' ); >- $self->add_bib1_field_map('subject' => 'authority-number' => 'biblioserver' => { '1' => 'Koha-Auth-Number' } ); >- $self->add_search_field_alias( 'subject' => 'authority-number' => 'an' ); >- $self->add_bib1_field_map('keyword' => 'control-number' => 'biblioserver' => { '1' => '9001' } ); >- $self->add_bib1_field_map('keyword' => 'biblionumber' => 'biblioserver' => { '1' => '9002', '5' => '100' } ); >- $self->add_bib1_field_map('keyword' => 'totalissues' => 'biblioserver' => { '1' => '9003' } ); >- $self->add_bib1_field_map('keyword' => 'cn-bib-source' => 'biblioserver' => { '1' => '9004' } ); >- $self->add_bib1_field_map('keyword' => 'cn-bib-sort' => 'biblioserver' => { '1' => '9005' } ); >- $self->add_bib1_field_map('keyword' => 'itemtype' => 'biblioserver' => { '1' => '9006' } ); >- $self->add_search_field_alias( 'keyword' => 'itemtype' => 'mc-itemtype' ); >- $self->add_bib1_field_map('keyword' => 'cn-class' => 'biblioserver' => { '1' => '9007' } ); >- $self->add_bib1_field_map('keyword' => 'cn-item' => 'biblioserver' => { '1' => '9008' } ); >- $self->add_bib1_field_map('keyword' => 'cn-prefix' => 'biblioserver' => { '1' => '9009' } ); >- $self->add_bib1_field_map('keyword' => 'cn-suffix' => 'biblioserver' => { '1' => '9010' } ); >- $self->add_bib1_field_map('keyword' => 'suppress' => 'biblioserver' => { '1' => '9011' } ); >- $self->add_bib1_field_map('keyword' => 'id-other' => 'biblioserver' => { '1' => '9012' } ); >- $self->add_bib1_field_map('keyword' => 'date-entered-on-file' => 'biblioserver' => { '1' => 'date-entered-on-file' } ); >- $self->add_bib1_field_map('keyword' => 'extent' => 'biblioserver' => { '1' => 'Extent' } ); >- $self->add_bib1_field_map('keyword' => 'llength' => 'biblioserver' => { '1' => 'llength' } ); >- $self->add_bib1_field_map('keyword' => 'summary' => 'biblioserver' => { '1' => 'Summary' } ); >- $self->add_bib1_field_map('keyword' => 'withdrawn' => 'biblioserver' => { '1' => '8001' } ); >- $self->add_bib1_field_map('keyword' => 'lost' => 'biblioserver' => { '1' => '8002' } ); >- $self->add_bib1_field_map('keyword' => 'classification-source' => 'biblioserver' => { '1' => '8003' } ); >- $self->add_bib1_field_map('keyword' => 'materials-specified' => 'biblioserver' => { '1' => '8004' } ); >- $self->add_bib1_field_map('keyword' => 'damaged' => 'biblioserver' => { '1' => '8005' } ); >- $self->add_bib1_field_map('keyword' => 'restricted' => 'biblioserver' => { '1' => '8006' } ); >- $self->add_bib1_field_map('keyword' => 'cn-sort' => 'biblioserver' => { '1' => '8007' } ); >- $self->add_bib1_field_map('keyword' => 'notforloan' => 'biblioserver' => { '1' => '8008', '4' => '109' } ); >- $self->add_bib1_field_map('keyword' => 'ccode' => 'biblioserver' => { '1' => '8009' } ); >- $self->add_search_field_alias( 'keyword' => 'ccode' => 'mc-ccode' ); >- $self->add_bib1_field_map('keyword' => 'itemnumber' => 'biblioserver' => { '1' => '8010' } ); >- $self->add_bib1_field_map('keyword' => 'homebranch' => 'biblioserver' => { '1' => 'homebranch' } ); >- $self->add_search_field_alias( 'keyword' => 'homebranch' => 'branch' ); >- $self->add_bib1_field_map('keyword' => 'holdingbranch' => 'biblioserver' => { '1' => '8012' } ); >- $self->add_bib1_field_map('keyword' => 'location' => 'biblioserver' => { '1' => '8013' } ); >- $self->add_search_field_alias( 'keyword' => 'location' => 'mc-loc' ); >- $self->add_bib1_field_map('keyword' => 'acqsource' => 'biblioserver' => { '1' => '8015' } ); >- $self->add_bib1_field_map('keyword' => 'coded-location-qualifier' => 'biblioserver' => { '1' => '8016' } ); >- $self->add_bib1_field_map('keyword' => 'price' => 'biblioserver' => { '1' => '8017' } ); >- $self->add_bib1_field_map('keyword' => 'stocknumber' => 'biblioserver' => { '1' => '1062' } ); >- $self->add_search_field_alias( 'keyword' => 'stocknumber' => 'inv' ); >- $self->add_bib1_field_map('keyword' => 'stack' => 'biblioserver' => { '1' => '8018' } ); >- $self->add_bib1_field_map('keyword' => 'issues' => 'biblioserver' => { '1' => '8019' } ); >- $self->add_bib1_field_map('keyword' => 'renewals' => 'biblioserver' => { '1' => '8020' } ); >- $self->add_bib1_field_map('keyword' => 'reserves' => 'biblioserver' => { '1' => '8021' } ); >- $self->add_bib1_field_map('keyword' => 'local-classification' => 'biblioserver' => { '1' => '8022' } ); >- $self->add_bib1_field_map('keyword' => 'barcode' => 'biblioserver' => { '1' => '8023' } ); >- $self->add_search_field_alias( 'keyword' => 'barcode' => 'bc' ); >- $self->add_bib1_field_map('keyword' => 'onloan' => 'biblioserver' => { '1' => '8024' } ); >- $self->add_bib1_field_map('keyword' => 'datelastseen' => 'biblioserver' => { '1' => '8025' } ); >- $self->add_bib1_field_map('keyword' => 'datelastborrowed' => 'biblioserver' => { '1' => '8026' } ); >- $self->add_bib1_field_map('keyword' => 'copynumber' => 'biblioserver' => { '1' => '8027' } ); >- $self->add_bib1_field_map('keyword' => 'uri' => 'biblioserver' => { '1' => '8028' } ); >- $self->add_bib1_field_map('keyword' => 'replacementprice' => 'biblioserver' => { '1' => '8029' } ); >- $self->add_bib1_field_map('keyword' => 'replacementpricedate' => 'biblioserver' => { '1' => '8030' } ); >- $self->add_bib1_field_map('keyword' => 'itype' => 'biblioserver' => { '1' => '8031' } ); >- $self->add_search_field_alias( 'keyword' => 'itype' => 'mc-itype' ); >- $self->add_bib1_field_map('keyword' => 'ff8-22' => 'biblioserver' => { '1' => '8822' } ); >- $self->add_bib1_field_map('keyword' => 'ff8-23' => 'biblioserver' => { '1' => '8823' } ); >- $self->add_bib1_field_map('keyword' => 'ff8-34' => 'biblioserver' => { '1' => '8834' } ); >-# Audience >- $self->add_bib1_field_map('keyword' => 'audience' => 'biblioserver' => { '1' => '8822' } ); >- $self->add_search_field_alias( 'keyword' => 'audience' => 'aud' ); >- >-# Content and Literary form >- $self->add_bib1_field_map('keyword' => 'fiction' => 'biblioserver' => { '1' => '8833' } ); >- $self->add_search_field_alias( 'keyword' => 'fiction' => 'fic' ); >- $self->add_bib1_field_map('keyword' => 'biography' => 'biblioserver' => { '1' => '8834' } ); >- $self->add_search_field_alias( 'keyword' => 'biography' => 'bio' ); >- >-# Format >- $self->add_bib1_field_map('keyword' => 'format' => 'biblioserver' => { '1' => '8823' } ); >-# format used as a limit FIXME: needed? >- $self->add_bib1_field_map('keyword' => 'l-format' => 'biblioserver' => { '1' => '8703' } ); >- >- $self->add_bib1_field_map('keyword' => 'illustration-code' => 'biblioserver' => { '1' => 'Illustration-code ' } ); >- >-# Lexile Number >- $self->add_bib1_field_map('keyword' => 'lex' => 'biblioserver' => { '1' => '9903 r=r' } ); >- >-#Accelerated Reader Level >- $self->add_bib1_field_map('keyword' => 'arl' => 'biblioserver' => { '1' => '9904 r=r' } ); >- >-#Accelerated Reader Point >- $self->add_bib1_field_map('keyword' => 'arp' => 'biblioserver' => { '1' => '9014 r=r' } ); >- >-# Curriculum >- $self->add_bib1_field_map('keyword' => 'curriculum' => 'biblioserver' => { '1' => '9658' } ); >- >-## Statuses >- $self->add_bib1_field_map('keyword' => 'popularity' => 'biblioserver' => { '1' => 'issues' } ); >- >-## Type Limits >- $self->add_bib1_field_map('keyword' => 'dt-bks' => 'biblioserver' => { '1' => '8700' } ); >- $self->add_bib1_field_map('keyword' => 'dt-vis' => 'biblioserver' => { '1' => '8700' } ); >- $self->add_bib1_field_map('keyword' => 'dt-sr' => 'biblioserver' => { '1' => '8700' } ); >- $self->add_bib1_field_map('keyword' => 'dt-cf' => 'biblioserver' => { '1' => '8700' } ); >- $self->add_bib1_field_map('keyword' => 'dt-map' => 'biblioserver' => { '1' => '8700' } ); >- >- $self->add_bib1_field_map('keyword' => 'name' => 'biblioserver' => { '1' => '1002' } ); >- $self->add_bib1_field_map('keyword' => 'item' => 'biblioserver' => { '1' => '9520' } ); >- $self->add_bib1_field_map('keyword' => 'host-item-number' => 'biblioserver' => { '1' => '8911' } ); >- $self->add_search_field_alias( 'keyword' => 'host-item-number' => 'hi' ); >- >- $self->add_bib1_field_map('keyword' => 'alwaysmatch' => 'biblioserver' => { '1' => '_ALLRECORDS', '2' => '103' } ); >- $self->add_bib1_field_map('subject' => 'complete' => 'biblioserver' => { '1' => '21', '3' => '1', '4' => '1', '5' => '100', '6' => '3' } ); >- >- $self->add_bib1_modifier_map('relevance' => 'biblioserver' => { '2' => '102' } ); >- $self->add_bib1_modifier_map('title-sort-za' => 'biblioserver' => { '7' => '2', '1' => '36', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('title-sort-az' => 'biblioserver' => { '7' => '1', '1' => '36', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('relevance_dsc' => 'biblioserver' => { '2' => '102' } ); >- $self->add_bib1_modifier_map('title_dsc' => 'biblioserver' => { '7' => '2', '1' => '4', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('title_asc' => 'biblioserver' => { '7' => '1', '1' => '4', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('author_asc' => 'biblioserver' => { '7' => '2', '1' => '1003', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('author_dsc' => 'biblioserver' => { '7' => '1', '1' => '1003', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('popularity_asc' => 'biblioserver' => { '7' => '2', '1' => '9003', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('popularity_dsc' => 'biblioserver' => { '7' => '1', '1' => '9003', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('call_number_asc' => 'biblioserver' => { '7' => '2', '1' => '8007', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('call_number_dsc' => 'biblioserver' => { '7' => '1', '1' => '8007', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('pubdate_asc' => 'biblioserver' => { '7' => '2', '1' => '31', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('pubdate_dsc' => 'biblioserver' => { '7' => '1', '1' => '31', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('acqdate_asc' => 'biblioserver' => { '7' => '2', '1' => '32', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('acqdate_dsc' => 'biblioserver' => { '7' => '1', '1' => '32', '' => '0', 'op' => '@or' } ); >- >- $self->add_bib1_modifier_map('title_za' => 'biblioserver' => { '7' => '2', '1' => '4', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('title_az' => 'biblioserver' => { '7' => '1', '1' => '4', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('author_za' => 'biblioserver' => { '7' => '2', '1' => '1003', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('author_az' => 'biblioserver' => { '7' => '1', '1' => '1003', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('ascending' => 'biblioserver' => { '7' => '1' } ); >- $self->add_bib1_modifier_map('descending' => 'biblioserver' => { '7' => '2' } ); >- >- $self->add_bib1_field_map('title' => 'exacttitle' => 'biblioserver' => { '1' => '4', '4' => '1', '6' => '3' } ); >- $self->add_search_field_alias( 'title' => 'exacttitle' => 'ti,ext' ); >- $self->add_bib1_field_map('author' => 'exactauthor' => 'biblioserver' => { '1' => '1003', '4' => '1', '6' => '3' } ); >- $self->add_search_field_alias( 'author' => 'exactauthor' => 'au,ext' ); >- >- $self->add_bib1_field_map('subject' => 'headingmain' => 'authorityserver' => { '1' => 'Heading-Main' } ); >- $self->add_bib1_field_map('subject' => 'heading' => 'authorityserver' => { '1' => 'Heading' } ); >- $self->add_bib1_field_map('subject' => 'matchheading' => 'authorityserver' => { '1' => 'Match-heading' } ); >- $self->add_bib1_field_map('subject' => 'seefrom' => 'authorityserver' => { '1' => 'Match-heading-see-from' } ); >- $self->add_bib1_field_map('subject' => '' => 'authorityserver' => { '1' => 'Match-heading' } ); >- $self->add_bib1_field_map('keyword' => 'alwaysmatch' => 'authorityserver' => { '1' => '_ALLRECORDS', '2' => '103' } ); >- $self->add_bib1_field_map('keyword' => 'match' => 'authorityserver' => { '1' => 'Match' } ); >- $self->add_bib1_field_map('keyword' => 'thesaurus' => 'authorityserver' => { '1' => 'Subject-heading-thesaurus' } ); >- $self->add_bib1_field_map('keyword' => 'authtype' => 'authorityserver' => { '1' => 'authtype', '5' => '100' } ); >- $self->add_bib1_field_map('keyword' => '' => 'authorityserver' => { '1' => 'Any' } ); >- $self->add_search_field_alias( 'subject' => 'headingmain' => 'mainmainentry' ); >- $self->add_search_field_alias( 'subject' => 'heading' => 'mainentry' ); >- $self->add_search_field_alias( 'subject' => 'heading' => 'he' ); >- $self->add_search_field_alias( 'subject' => 'matchheading' => 'match-heading' ); >- $self->add_search_field_alias( 'keyword' => '' => 'any' ); >- $self->add_search_field_alias( 'keyword' => 'match' => 'match' ); >- $self->add_search_field_alias( 'subject' => 'seefrom' => 'see-from' ); >- $self->add_search_field_alias( 'keyword' => 'thesaurus' => 'thesaurus' ); >- $self->add_search_field_alias( 'keyword' => 'alwaysmatch' => 'all' ); >- $self->add_search_field_alias( 'keyword' => 'authtype' => 'authtype' ); >- $self->add_search_field_alias( 'keyword' => 'authtype' => 'at' ); >- >- $self->add_bib1_field_map('subject' => 'start' => 'authorityserver' => { '3' => '2', '4' => '1', '5' => '1' } ); >- $self->add_bib1_field_map('subject' => 'exact' => 'authorityserver' => { '4' => '1', '5' => '100', '6' => '3' } ); >- >- $self->add_bib1_modifier_map('HeadingAsc' => 'authorityserver' => { '7' => '1', '1' => 'Heading', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('HeadingDsc' => 'authorityserver' => { '7' => '2', '1' => 'Heading', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('AuthidAsc' => 'authorityserver' => { '7' => '1', '1' => 'Local-Number', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('AuthidDsc' => 'authorityserver' => { '7' => '2', '1' => 'Local-Number', '' => '0', 'op' => '@or' } ); >- $self->add_bib1_modifier_map('Relevance' => 'authorityserver' => { '2' => '102' } ); >- >- return $self; >-} >- >-1; >diff --git a/Koha/QueryParser/Driver/PQF/Util.pm b/Koha/QueryParser/Driver/PQF/Util.pm >deleted file mode 100644 >index 99e36f0a60..0000000000 >--- a/Koha/QueryParser/Driver/PQF/Util.pm >+++ /dev/null >@@ -1,54 +0,0 @@ >-package Koha::QueryParser::Driver::PQF::Util; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use Scalar::Util qw(looks_like_number); >- >-use strict; >-use warnings; >- >-=head1 NAME >- >- Koha::QueryParser::Driver::PQF::Util - Utility module for PQF QueryParser driver >- >-=head1 FUNCTIONS >- >-=head2 attributes_to_attr_string >- >- Koha::QueryParser::Driver::PQF::Util(%attributes); >- >- Koha::QueryParser::Driver::PQF::Util({ '1' => '1003', '4' => '6' }); >- >-Convert a hashref with a Bib-1 mapping into its PQF string representation. >- >-=cut >- >-sub attributes_to_attr_string { >- my ($attributes) = @_; >- my $attr_string = ''; >- foreach my $key ( sort keys %{$attributes} ) { >- next unless looks_like_number($key); >- $attr_string .= ' @attr ' . $key . '=' . $attributes->{ $key } . ' '; >- } >- $attr_string =~ s/^\s*//; >- $attr_string =~ s/\s*$//; >- $attr_string .= ' ' . $attributes->{''} if defined $attributes->{''}; >- return $attr_string; >-} >- >-1; >diff --git a/Koha/QueryParser/Driver/PQF/query_plan.pm b/Koha/QueryParser/Driver/PQF/query_plan.pm >deleted file mode 100644 >index 320410ed2e..0000000000 >--- a/Koha/QueryParser/Driver/PQF/query_plan.pm >+++ /dev/null >@@ -1,70 +0,0 @@ >-package Koha::QueryParser::Driver::PQF::query_plan; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use base 'OpenILS::QueryParser::query_plan'; >- >-use strict; >-use warnings; >- >-=head1 NAME >- >-Koha::QueryParser::Driver::PQF::query_plan - query_plan subclass for PQF driver >- >-=head1 FUNCTIONS >- >-=head2 Koha::QueryParser::Driver::PQF::query_plan::target_syntax >- >- my $pqf = $query_plan->target_syntax($server); >- >-Transforms an OpenILS::QueryParser::query_plan object into PQF. Do not use directly. >- >-=cut >- >-sub target_syntax { >- my ($self, $server) = @_; >- my $pqf = ''; >- my $node_pqf; >- my $node_count = 0; >- >- for my $node ( @{$self->query_nodes} ) { >- >- if (ref($node)) { >- $node_pqf = $node->target_syntax($server); >- $node_count++ if $node_pqf; >- $pqf .= $node_pqf; >- } >- } >- $pqf = ($self->joiner eq '|' ? ' @or ' : ' @and ') x ($node_count - 1) . $pqf if $node_count > 1; >- $node_count = ($node_count ? '1' : '0'); >- for my $node ( @{$self->filters} ) { >- if (ref($node)) { >- $node_pqf = $node->target_syntax($server); >- $node_count++ if $node_pqf; >- $pqf .= $node_pqf; >- } >- } >- $pqf = ($self->joiner eq '|' ? ' @or ' : ' @and ') x ($node_count - 1) . $pqf if $node_count > 1; >- foreach my $modifier ( @{$self->modifiers} ) { >- my $modifierpqf = $modifier->target_syntax($server, $self); >- $pqf = $modifierpqf . ' ' . $pqf if $modifierpqf; >- } >- return ($self->negate ? '@not @attr 1=_ALLRECORDS @attr 2=103 "" ' : '') . $pqf; >-} >- >-1; >diff --git a/Koha/QueryParser/Driver/PQF/query_plan/facet.pm b/Koha/QueryParser/Driver/PQF/query_plan/facet.pm >deleted file mode 100644 >index 0a8b169207..0000000000 >--- a/Koha/QueryParser/Driver/PQF/query_plan/facet.pm >+++ /dev/null >@@ -1,46 +0,0 @@ >-package Koha::QueryParser::Driver::PQF::query_plan::facet; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use base 'OpenILS::QueryParser::query_plan::facet'; >- >-use strict; >-use warnings; >- >-=head1 NAME >- >-Koha::QueryParser::Driver::PQF::query_plan::facet - facet subclass for PQF driver >- >-=head1 FUNCTIONS >- >-=head2 Koha::QueryParser::Driver::PQF::query_plan::facet::target_syntax >- >- my $pqf = $facet->target_syntax($server); >- >-Transforms an OpenILS::QueryParser::query_plan::facet object into PQF. Do not use >-directly. >- >-=cut >- >-sub target_syntax { >- my ($self, $server) = @_; >- >- return ''; >-} >- >-1; >diff --git a/Koha/QueryParser/Driver/PQF/query_plan/filter.pm b/Koha/QueryParser/Driver/PQF/query_plan/filter.pm >deleted file mode 100644 >index ab6358e4af..0000000000 >--- a/Koha/QueryParser/Driver/PQF/query_plan/filter.pm >+++ /dev/null >@@ -1,51 +0,0 @@ >-package Koha::QueryParser::Driver::PQF::query_plan::filter; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use base 'OpenILS::QueryParser::query_plan::filter'; >- >-use strict; >-use warnings; >- >-=head1 NAME >- >-Koha::QueryParser::Driver::PQF::query_plan::filter - filter subclass for PQF driver >- >-=head1 FUNCTIONS >- >-=head2 Koha::QueryParser::Driver::PQF::query_plan::filter::target_syntax >- >- my $pqf = $filter->target_syntax($server); >- >-Transforms an OpenILS::QueryParser::query_plan::filter object into PQF. Do not use >-directly. >- >-=cut >- >-sub target_syntax { >- my ($self, $server) = @_; >- my $attributes = $self->plan->QueryParser->bib1_mapping_by_name( 'filter', $self->name, $server ); >- >- if ($attributes->{'target_syntax_callback'}) { >- return $attributes->{'target_syntax_callback'}->($self->plan->QueryParser, $self->name, $self->args, $self->negate, $server); >- } else { >- return ''; >- } >-} >- >-1; >diff --git a/Koha/QueryParser/Driver/PQF/query_plan/modifier.pm b/Koha/QueryParser/Driver/PQF/query_plan/modifier.pm >deleted file mode 100644 >index bf34e72cb4..0000000000 >--- a/Koha/QueryParser/Driver/PQF/query_plan/modifier.pm >+++ /dev/null >@@ -1,50 +0,0 @@ >-package Koha::QueryParser::Driver::PQF::query_plan::modifier; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use base 'OpenILS::QueryParser::query_plan::modifier'; >- >-use strict; >-use warnings; >- >-=head1 NAME >- >-Koha::QueryParser::Driver::PQF::query_plan::modifer - modifier subclass for PQF driver >- >-=head1 FUNCTIONS >- >-=head2 Koha::QueryParser::Driver::PQF::query_plan::modifier::target_syntax >- >- my $pqf = $modifier->target_syntax($server, $query_plan); >- >-Transforms an OpenILS::QueryParser::query_plan::modifier object into PQF. Do not use >-directly. The second argument points ot the query_plan, since modifiers do >-not have a reference to their parent query_plan. >- >-=cut >- >-sub target_syntax { >- my ($self, $server, $query_plan) = @_; >- my $pqf = ''; >- >- my $attributes = $query_plan->QueryParser->bib1_mapping_by_name('modifier', $self->name, $server); >- $pqf = ($attributes->{'op'} ? $attributes->{'op'} . ' ' : '') . ($self->negate ? '@not @attr 1=_ALLRECORDS @attr 2=103 "" ' : '') . $attributes->{'attr_string'}; >- return $pqf; >-} >- >-1; >diff --git a/Koha/QueryParser/Driver/PQF/query_plan/node.pm b/Koha/QueryParser/Driver/PQF/query_plan/node.pm >deleted file mode 100644 >index 9b77ba7ed0..0000000000 >--- a/Koha/QueryParser/Driver/PQF/query_plan/node.pm >+++ /dev/null >@@ -1,106 +0,0 @@ >-package Koha::QueryParser::Driver::PQF::query_plan::node; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use base 'OpenILS::QueryParser::query_plan::node'; >- >-use strict; >-use warnings; >- >-=head1 NAME >- >-Koha::QueryParser::Driver::PQF::query_plan::node - node subclass for PQF driver >- >-=head1 FUNCTIONS >- >-=head2 Koha::QueryParser::Driver::PQF::query_plan::node::target_syntax >- >- my $pqf = $node->target_syntax($server); >- >-Transforms an OpenILS::QueryParser::query_plan::node object into PQF. Do not use directly. >- >-=cut >- >-sub target_syntax { >- my ($self, $server) = @_; >- my $pqf = ''; >- my $atom_content; >- my $atom_count = 0; >- my @fields = (); >- my $fieldobj; >- my $relbump; >- >- if (scalar(@{$self->fields})) { >- foreach my $field (@{$self->fields}) { >- $fieldobj = $self->plan->QueryParser->bib1_mapping_by_name('field', $self->classname, $field, $server); >- $relbump = $self->plan->QueryParser->bib1_mapping_by_name('relevance_bump', $self->classname, $field, $server); >- if ($relbump && defined $relbump->{'attr_string'}) { >- $fieldobj->{'attr_string'} .= ' ' . $relbump->{'attr_string'}; >- } >- push @fields, $fieldobj unless (!defined($fieldobj) || ($field eq $self->classname && @{$self->fields} > 1)); >- } >- } else { >- $fieldobj = $self->plan->QueryParser->bib1_mapping_by_name('field', $self->classname, $self->classname, $server); >- my $relbumps = $self->plan->QueryParser->bib1_mapping_by_name('relevance_bump', $self->classname, '', $server); >- push @fields, $fieldobj; >- if ($relbumps) { >- foreach my $field (keys %$relbumps) { >- $relbump = $relbumps->{$field}; >- $fieldobj = $self->plan->QueryParser->bib1_mapping_by_name('field', $relbump->{'classname'}, $relbump->{'field'}, $server); >- $fieldobj->{'attr_string'} ||= ''; >- $fieldobj->{'attr_string'} .= ' ' . $relbump->{$server}{'attr_string'} if $relbump->{$server}{'attr_string'}; >- push @fields, $fieldobj; >- } >- } >- } >- >- if (@{$self->phrases}) { >- foreach my $phrase (@{$self->phrases}) { >- if ($phrase) { >- $phrase =~ s/"/\\"/g; >- $pqf .= ' @or ' x (scalar(@fields) - 1); >- foreach my $attributes (@fields) { >- $attributes->{'attr_string'} ||= ''; >- $pqf .= $attributes->{'attr_string'} . ($attributes->{'4'} ? '' : ' @attr 4=1') . ' "' . $phrase . '" '; >- } >- $atom_count++; >- } >- } >- } else { >- foreach my $atom (@{$self->query_atoms}) { >- if (ref($atom)) { >- $atom_content = $atom->target_syntax($server); >- if ($atom_content) { >- $pqf .= ' @or ' x (scalar(@fields) - 1); >- foreach my $attributes (@fields) { >- $attributes->{'attr_string'} ||= ''; >- if ($self->plan->QueryParser->custom_data->{'QueryAutoTruncate'} || $atom->suffix eq '*') { >- $attributes->{'attr_string'} .= ($attributes->{'5'} ? '' : ' @attr 5=1 '); >- } >- $pqf .= $attributes->{'attr_string'} . ($attributes->{'4'} ? '' : ' @attr 4=6 ') . $atom_content . ' '; >- } >- $atom_count++; >- } >- } >- } >- } >- $pqf = (OpenILS::QueryParser::_util::default_joiner eq '|' ? ' @or ' : ' @and ') x ($atom_count - 1) . $pqf if $atom_count > 1; >- return ($self->negate ? '@not @attr 1=_ALLRECORDS @attr 2=103 "" ' : '') . $pqf; >-} >- >-1; >diff --git a/Koha/QueryParser/Driver/PQF/query_plan/node/atom.pm b/Koha/QueryParser/Driver/PQF/query_plan/node/atom.pm >deleted file mode 100644 >index 909fb0d1d6..0000000000 >--- a/Koha/QueryParser/Driver/PQF/query_plan/node/atom.pm >+++ /dev/null >@@ -1,49 +0,0 @@ >-package Koha::QueryParser::Driver::PQF::query_plan::node::atom; >- >-# This file is part of Koha. >-# >-# Copyright 2012 C & P Bibliography Services >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use base 'OpenILS::QueryParser::query_plan::node::atom'; >- >-use strict; >-use warnings; >- >-=head1 NAME >- >-Koha::QueryParser::Driver::PQF::query_plan::node::atom - atom subclass for PQF driver >- >-=head1 FUNCTIONS >- >-=head2 Koha::QueryParser::Driver::PQF::query_plan::node::atom::target_syntax >- >- my $pqf = $atom->target_syntax($server); >- >-Transforms an OpenILS::QueryParser::query_plan::node::atom object into PQF. Do not use >-directly. >- >-=cut >- >-sub target_syntax { >- my ($self, $server) = @_; >- >- my $content = $self->content; >- $content =~ s/"/\\"/g; >- >- return ' "' . $content . '" '; >-} >- >-1; >diff --git a/OpenILS/QueryParser.pm b/OpenILS/QueryParser.pm >deleted file mode 100644 >index f0051f76de..0000000000 >--- a/OpenILS/QueryParser.pm >+++ /dev/null >@@ -1,2242 +0,0 @@ >-use strict; >-use warnings; >- >-package OpenILS::QueryParser; >-use JSON; >- >-=head1 NAME >- >-OpenILS::QueryParser - basic QueryParser class >- >-=head1 SYNOPSIS >- >-use OpenILS::QueryParser; >-my $QParser = OpenILS::QueryParser->new(%args); >- >-=head1 DESCRIPTION >- >-Main entrypoint into the QueryParser functionality. >- >-=head1 FUNCTIONS >- >-=cut >- >-# Note that the first key must match the name of the package. >-our %parser_config = ( >- 'OpenILS::QueryParser' => { >- filters => [], >- modifiers => [], >- operators => { >- 'and' => '&&', >- 'or' => '||', >- float_start => '{{', >- float_end => '}}', >- group_start => '(', >- group_end => ')', >- required => '+', >- disallowed => '-', >- modifier => '#', >- negated => '!' >- } >- } >-); >- >-sub canonicalize { >- my $self = shift; >- return OpenILS::QueryParser::Canonicalize::abstract_query2str_impl( >- $self->parse_tree->to_abstract_query(@_) >- ); >-} >- >- >-=head2 facet_class_count >- >- $count = $QParser->facet_class_count(); >-=cut >- >-sub facet_class_count { >- my $self = shift; >- return @{$self->facet_classes}; >-} >- >-=head2 search_class_count >- >- $count = $QParser->search_class_count(); >-=cut >- >-sub search_class_count { >- my $self = shift; >- return @{$self->search_classes}; >-} >- >-=head2 filter_count >- >- $count = $QParser->filter_count(); >-=cut >- >-sub filter_count { >- my $self = shift; >- return @{$self->filters}; >-} >- >-=head2 modifier_count >- >- $count = $QParser->modifier_count(); >-=cut >- >-sub modifier_count { >- my $self = shift; >- return @{$self->modifiers}; >-} >- >-=head2 custom_data >- >- $data = $QParser->custom_data($class); >-=cut >- >-sub custom_data { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{custom_data} ||= {}; >- return $parser_config{$class}{custom_data}; >-} >- >-=head2 operators >- >- $operators = $QParser->operators(); >- >-Returns hashref of the configured operators. >-=cut >- >-sub operators { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{operators} ||= {}; >- return $parser_config{$class}{operators}; >-} >- >-sub allow_nested_modifiers { >- my $class = shift; >- my $v = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{allow_nested_modifiers} = $v if (defined $v); >- return $parser_config{$class}{allow_nested_modifiers}; >-} >- >-=head2 filters >- >- $filters = $QParser->filters(); >- >-Returns arrayref of the configured filters. >-=cut >- >-sub filters { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{filters} ||= []; >- return $parser_config{$class}{filters}; >-} >- >-=head2 filter_callbacks >- >- $filter_callbacks = $QParser->filter_callbacks(); >- >-Returns hashref of the configured filter callbacks. >-=cut >- >-sub filter_callbacks { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{filter_callbacks} ||= {}; >- return $parser_config{$class}{filter_callbacks}; >-} >- >-=head2 modifiers >- >- $modifiers = $QParser->modifiers(); >- >-Returns arrayref of the configured modifiers. >-=cut >- >-sub modifiers { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{modifiers} ||= []; >- return $parser_config{$class}{modifiers}; >-} >- >-=head2 new >- >- $QParser = OpenILS::QueryParser->new(%args); >- >-Creates a new QueryParser object. >-=cut >- >-sub new { >- my $class = shift; >- $class = ref($class) || $class; >- >- my %opts = @_; >- >- my $self = bless {} => $class; >- >- for my $o (keys %{OpenILS::QueryParser->operators}) { >- $class->operator($o => OpenILS::QueryParser->operator($o)) unless ($class->operator($o)); >- } >- >- for my $opt ( keys %opts) { >- $self->$opt( $opts{$opt} ) if ($self->can($opt)); >- } >- >- return $self; >-} >- >-=head2 new_plan >- >- $query_plan = $QParser->new_plan(); >- >-Create a new query plan. >-=cut >- >-sub new_plan { >- my $self = shift; >- my $pkg = ref($self) || $self; >- return do{$pkg.'::query_plan'}->new( QueryParser => $self, @_ ); >-} >- >-=head2 add_search_filter >- >- $QParser->add_search_filter($filter, [$callback]); >- >-Adds a filter with the specified name and an optional callback to the >-QueryParser configuration. >-=cut >- >-sub add_search_filter { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $filter = shift; >- my $callback = shift; >- >- return $filter if (grep { $_ eq $filter } @{$pkg->filters}); >- push @{$pkg->filters}, $filter; >- $pkg->filter_callbacks->{$filter} = $callback if ($callback); >- return $filter; >-} >- >-=head2 add_search_modifier >- >- $QParser->add_search_modifier($modifier); >- >-Adds a modifier with the specified name to the QueryParser configuration. >-=cut >- >-sub add_search_modifier { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $modifier = shift; >- >- return $modifier if (grep { $_ eq $modifier } @{$pkg->modifiers}); >- push @{$pkg->modifiers}, $modifier; >- return $modifier; >-} >- >-=head2 add_facet_class >- >- $QParser->add_facet_class($facet_class); >- >-Adds a facet class with the specified name to the QueryParser configuration. >-=cut >- >-sub add_facet_class { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- >- return $class if (grep { $_ eq $class } @{$pkg->facet_classes}); >- >- push @{$pkg->facet_classes}, $class; >- $pkg->facet_fields->{$class} = []; >- >- return $class; >-} >- >-=head2 add_search_class >- >- $QParser->add_search_class($class); >- >-Adds a search class with the specified name to the QueryParser configuration. >-=cut >- >-sub add_search_class { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- >- return $class if (grep { $_ eq $class } @{$pkg->search_classes}); >- >- push @{$pkg->search_classes}, $class; >- $pkg->search_fields->{$class} = []; >- $pkg->default_search_class( $pkg->search_classes->[0] ) if (@{$pkg->search_classes} == 1); >- >- return $class; >-} >- >-=head2 add_search_modifier >- >- $op = $QParser->operator($operator, [$newvalue]); >- >-Retrieves or sets value for the specified operator. Valid operators and >-their defaults are as follows: >- >-=over 4 >- >-=item * and => && >- >-=item * or => || >- >-=item * group_start => ( >- >-=item * group_end => ) >- >-=item * required => + >- >-=item * disallowed => - >- >-=item * modifier => # >- >-=back >- >-=cut >- >-sub operator { >- my $class = shift; >- $class = ref($class) || $class; >- my $opname = shift; >- my $op = shift; >- >- return unless ($opname); >- >- $parser_config{$class}{operators} ||= {}; >- $parser_config{$class}{operators}{$opname} = $op if ($op); >- >- return $parser_config{$class}{operators}{$opname}; >-} >- >-=head2 facet_classes >- >- $classes = $QParser->facet_classes([\@newclasses]); >- >-Returns arrayref of all configured facet classes after optionally >-replacing configuration. >-=cut >- >-sub facet_classes { >- my $class = shift; >- $class = ref($class) || $class; >- my $classes = shift; >- >- $parser_config{$class}{facet_classes} ||= []; >- $parser_config{$class}{facet_classes} = $classes if (ref($classes) && @$classes); >- return $parser_config{$class}{facet_classes}; >-} >- >-=head2 search_classes >- >- $classes = $QParser->search_classes([\@newclasses]); >- >-Returns arrayref of all configured search classes after optionally >-replacing the previous configuration. >-=cut >- >-sub search_classes { >- my $class = shift; >- $class = ref($class) || $class; >- my $classes = shift; >- >- $parser_config{$class}{classes} ||= []; >- $parser_config{$class}{classes} = $classes if (ref($classes) && @$classes); >- return $parser_config{$class}{classes}; >-} >- >-=head2 add_query_normalizer >- >- $function = $QParser->add_query_normalizer($class, $field, $func, [\@params]); >- >-=cut >- >-sub add_query_normalizer { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $field = shift; >- my $func = shift; >- my $params = shift || []; >- >- # do not add if function AND params are identical to existing member >- return $func if (grep { >- $_->{function} eq $func and >- to_json($_->{params}) eq to_json($params) >- } @{$pkg->query_normalizers->{$class}->{$field}}); >- >- push(@{$pkg->query_normalizers->{$class}->{$field}}, { function => $func, params => $params }); >- >- return $func; >-} >- >-=head2 query_normalizers >- >- $normalizers = $QParser->query_normalizers($class, $field); >- >-Returns a list of normalizers associated with the specified search class >-and field >-=cut >- >-sub query_normalizers { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- >- my $class = shift; >- my $field = shift; >- >- $parser_config{$pkg}{normalizers} ||= {}; >- if ($class) { >- if ($field) { >- $parser_config{$pkg}{normalizers}{$class}{$field} ||= []; >- return $parser_config{$pkg}{normalizers}{$class}{$field}; >- } else { >- return $parser_config{$pkg}{normalizers}{$class}; >- } >- } >- >- return $parser_config{$pkg}{normalizers}; >-} >- >-=head2 add_filter_normalizer >- >- $normalizer = $QParser->add_filter_normalizer($filter, $func, [\@params]); >- >-Adds a normalizer function to the specified filter. >-=cut >- >-sub add_filter_normalizer { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $filter = shift; >- my $func = shift; >- my $params = shift || []; >- >- return $func if (grep { $_ eq $func } @{$pkg->filter_normalizers->{$filter}}); >- >- push(@{$pkg->filter_normalizers->{$filter}}, { function => $func, params => $params }); >- >- return $func; >-} >- >-=head2 filter_normalizers >- >- $normalizers = $QParser->filter_normalizers($filter); >- >-Return arrayref of normalizer functions associated with the specified filter. >-=cut >- >-sub filter_normalizers { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- >- my $filter = shift; >- >- $parser_config{$pkg}{filter_normalizers} ||= {}; >- if ($filter) { >- $parser_config{$pkg}{filter_normalizers}{$filter} ||= []; >- return $parser_config{$pkg}{filter_normalizers}{$filter}; >- } >- >- return $parser_config{$pkg}{filter_normalizers}; >-} >- >-=head2 default_search_class >- >- $default_class = $QParser->default_search_class([$class]); >- >-Set or return the default search class. >-=cut >- >-sub default_search_class { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- $OpenILS::QueryParser::parser_config{$pkg}{default_class} = $pkg->add_search_class( $class ) if $class; >- >- return $OpenILS::QueryParser::parser_config{$pkg}{default_class}; >-} >- >-=head2 remove_facet_class >- >- $QParser->remove_facet_class($class); >- >-Remove the specified facet class from the configuration. >-=cut >- >-sub remove_facet_class { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- >- return $class if (!grep { $_ eq $class } @{$pkg->facet_classes}); >- >- $pkg->facet_classes( [ grep { $_ ne $class } @{$pkg->facet_classes} ] ); >- delete $OpenILS::QueryParser::parser_config{$pkg}{facet_fields}{$class}; >- >- return $class; >-} >- >-=head2 remove_search_class >- >- $QParser->remove_search_class($class); >- >-Remove the specified search class from the configuration. >-=cut >- >-sub remove_search_class { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- >- return $class if (!grep { $_ eq $class } @{$pkg->search_classes}); >- >- $pkg->search_classes( [ grep { $_ ne $class } @{$pkg->search_classes} ] ); >- delete $OpenILS::QueryParser::parser_config{$pkg}{fields}{$class}; >- >- return $class; >-} >- >-=head2 add_facet_field >- >- $QParser->add_facet_field($class, $field); >- >-Adds the specified field (and facet class if it doesn't already exist) >-to the configuration. >-=cut >- >-sub add_facet_field { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $field = shift; >- >- $pkg->add_facet_class( $class ); >- >- return { $class => $field } if (grep { $_ eq $field } @{$pkg->facet_fields->{$class}}); >- >- push @{$pkg->facet_fields->{$class}}, $field; >- >- return { $class => $field }; >-} >- >-=head2 facet_fields >- >- $fields = $QParser->facet_fields($class); >- >-Returns arrayref with list of fields for specified facet class. >-=cut >- >-sub facet_fields { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{facet_fields} ||= {}; >- return $parser_config{$class}{facet_fields}; >-} >- >-=head2 add_search_field >- >- $QParser->add_search_field($class, $field); >- >-Adds the specified field (and facet class if it doesn't already exist) >-to the configuration. >-=cut >- >-sub add_search_field { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $field = shift; >- >- $pkg->add_search_class( $class ); >- >- return { $class => $field } if (grep { $_ eq $field } @{$pkg->search_fields->{$class}}); >- >- push @{$pkg->search_fields->{$class}}, $field; >- >- return { $class => $field }; >-} >- >-=head2 search_fields >- >- $fields = $QParser->search_fields(); >- >-Returns arrayref with list of configured search fields. >-=cut >- >-sub search_fields { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{fields} ||= {}; >- return $parser_config{$class}{fields}; >-} >- >-=head2 add_search_class_alias >- >- $QParser->add_search_class_alias($class, $alias); >-=cut >- >-sub add_search_class_alias { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $alias = shift; >- >- $pkg->add_search_class( $class ); >- >- return { $class => $alias } if (grep { $_ eq $alias } @{$pkg->search_class_aliases->{$class}}); >- >- push @{$pkg->search_class_aliases->{$class}}, $alias; >- >- return { $class => $alias }; >-} >- >-=head2 search_class_aliases >- >- $aliases = $QParser->search_class_aliases($class); >-=cut >- >-sub search_class_aliases { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{class_map} ||= {}; >- return $parser_config{$class}{class_map}; >-} >- >-=head2 add_search_field_alias >- >- $QParser->add_search_field_alias($class, $field, $alias); >-=cut >- >-sub add_search_field_alias { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $field = shift; >- my $alias = shift; >- >- return { $class => { $field => $alias } } if (grep { $_ eq $alias } @{$pkg->search_field_aliases->{$class}{$field}}); >- >- push @{$pkg->search_field_aliases->{$class}{$field}}, $alias; >- >- return { $class => { $field => $alias } }; >-} >- >-=head2 search_field_aliases >- >- $aliases = $QParser->search_field_aliases(); >-=cut >- >-sub search_field_aliases { >- my $class = shift; >- $class = ref($class) || $class; >- >- $parser_config{$class}{field_alias_map} ||= {}; >- return $parser_config{$class}{field_alias_map}; >-} >- >-=head2 remove_facet_field >- >- $QParser->remove_facet_field($class, $field); >-=cut >- >-sub remove_facet_field { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $field = shift; >- >- return { $class => $field } if (!$pkg->facet_fields->{$class} || !grep { $_ eq $field } @{$pkg->facet_fields->{$class}}); >- >- $pkg->facet_fields->{$class} = [ grep { $_ ne $field } @{$pkg->facet_fields->{$class}} ]; >- >- return { $class => $field }; >-} >- >-=head2 remove_search_field >- >- $QParser->remove_search_field($class, $field); >-=cut >- >-sub remove_search_field { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $field = shift; >- >- return { $class => $field } if (!$pkg->search_fields->{$class} || !grep { $_ eq $field } @{$pkg->search_fields->{$class}}); >- >- $pkg->search_fields->{$class} = [ grep { $_ ne $field } @{$pkg->search_fields->{$class}} ]; >- >- return { $class => $field }; >-} >- >-=head2 remove_search_field_alias >- >- $QParser->remove_search_field_alias($class, $field, $alias); >-=cut >- >-sub remove_search_field_alias { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $field = shift; >- my $alias = shift; >- >- return { $class => { $field => $alias } } if (!$pkg->search_field_aliases->{$class}{$field} || !grep { $_ eq $alias } @{$pkg->search_field_aliases->{$class}{$field}}); >- >- $pkg->search_field_aliases->{$class}{$field} = [ grep { $_ ne $alias } @{$pkg->search_field_aliases->{$class}{$field}} ]; >- >- return { $class => { $field => $alias } }; >-} >- >-=head2 remove_search_class_alias >- >- $QParser->remove_search_class_alias($class, $alias); >-=cut >- >-sub remove_search_class_alias { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $class = shift; >- my $alias = shift; >- >- return { $class => $alias } if (!$pkg->search_class_aliases->{$class} || !grep { $_ eq $alias } @{$pkg->search_class_aliases->{$class}}); >- >- $pkg->search_class_aliases->{$class} = [ grep { $_ ne $alias } @{$pkg->search_class_aliases->{$class}} ]; >- >- return { $class => $alias }; >-} >- >-=head2 debug >- >- $debug = $QParser->debug([$debug]); >- >-Return or set whether debugging output is enabled. >-=cut >- >-sub debug { >- my $self = shift; >- my $q = shift; >- $self->{_debug} = $q if (defined $q); >- return $self->{_debug}; >-} >- >-=head2 query >- >- $query = $QParser->query([$query]); >- >-Return or set the query. >-=cut >- >-sub query { >- my $self = shift; >- my $q = shift; >- $self->{_query} = " $q " if (defined $q); >- return $self->{_query}; >-} >- >-=head2 parse_tree >- >- $parse_tree = $QParser->parse_tree([$parse_tree]); >- >-Return or set the parse tree associated with the QueryParser. >-=cut >- >-sub parse_tree { >- my $self = shift; >- my $q = shift; >- $self->{_parse_tree} = $q if (defined $q); >- return $self->{_parse_tree}; >-} >- >-sub floating_plan { >- my $self = shift; >- my $q = shift; >- $self->{_top} = $q if (defined $q); >- return $self->{_top}; >-} >- >-=head2 parse >- >- $QParser->parse([$query]); >- >-Parse the specified query, or the query already associated with the QueryParser >-object. >-=cut >- >-sub parse { >- my $self = shift; >- my $pkg = ref($self) || $self; >- warn " ** parse package is $pkg\n" if $self->debug; >-# $self->parse_tree( >-# $self->decompose( >-# $self->query( shift() ) >-# ) >-# ); >- >- undef $self->{_parse_tree}; >- >- $self->decompose( $self->query( shift() ) ); >- >- if ($self->floating_plan) { >- $self->floating_plan->add_node( $self->parse_tree ); >- $self->parse_tree( $self->floating_plan ); >- } >- >- $self->parse_tree->plan_level(0); >- >- return $self; >-} >- >-=head2 decompose >- >- ($struct, $remainder) = $QParser->decompose($querystring, [$current_class], [$recursing], [$phrase_helper]); >- >-This routine does the heavy work of parsing the query string recursively. >-Returns the top level query plan, or the query plan from a lower level plus >-the portion of the query string that needs to be processed at a higher level. >-=cut >- >-our $last_class = ''; >-our $last_type = ''; >-our $floating = 0; >-our $fstart; >- >-sub decompose { >- my $self = shift; >- my $pkg = ref($self) || $self; >- >- >- $_ = shift; >- my $current_class = shift || $self->default_search_class; >- >- my $recursing = shift || 0; >- my $phrase_helper = shift || 0; >- >- # Build the search class+field uber-regexp >- my $search_class_re = '^\s*('; >- my $first_class = 1; >- >- warn ' 'x$recursing." ** decompose package is $pkg\n" if $self->debug; >- >- my %seen_classes; >- for my $class ( keys %{$pkg->search_field_aliases} ) { >- warn ' 'x$recursing." *** ... Looking for search fields in $class\n" if $self->debug; >- >- for my $field ( keys %{$pkg->search_field_aliases->{$class}} ) { >- warn ' 'x$recursing." *** ... Looking for aliases of $field\n" if $self->debug; >- >- for my $alias ( @{$pkg->search_field_aliases->{$class}{$field}} ) { >- next unless ($alias); >- my $aliasr = qr/$alias/; >- s/(^|\s+)$aliasr\|/$1$class\|$field#$alias\|/g; >- s/(^|\s+)$aliasr[:=]/$1$class\|$field#$alias:/g; >- warn ' 'x$recursing." *** Rewriting: $alias ($aliasr) as $class\|$field\n" if $self->debug; >- } >- } >- >- $search_class_re .= '|' unless ($first_class); >- $first_class = 0; >- $search_class_re .= $class . '(?:[|#][^:|]+)*'; >- $seen_classes{$class} = 1; >- } >- >- for my $class ( keys %{$pkg->search_class_aliases} ) { >- >- for my $alias ( @{$pkg->search_class_aliases->{$class}} ) { >- next unless ($alias); >- my $aliasr = qr/$alias/; >- s/(^|[^|])\b$aliasr\|/$1$class#$alias\|/g; >- s/(^|[^|])\b$aliasr[:=]/$1$class#$alias:/g; >- warn ' 'x$recursing." *** Rewriting: $alias ($aliasr) as $class\n" if $self->debug; >- } >- >- if (!$seen_classes{$class}) { >- $search_class_re .= '|' unless ($first_class); >- $first_class = 0; >- >- $search_class_re .= $class . '(?:[|#][^:|]+)*'; >- $seen_classes{$class} = 1; >- } >- } >- $search_class_re .= '):'; >- >- warn ' 'x$recursing." ** Rewritten query: $_\n" if $self->debug; >- warn ' 'x$recursing." ** Search class RE: $search_class_re\n" if $self->debug; >- >- my $required_op = $pkg->operator('required'); >- my $required_re = qr/\Q$required_op\E/; >- >- my $disallowed_op = $pkg->operator('disallowed'); >- my $disallowed_re = qr/\Q$disallowed_op\E/; >- >- my $negated_op = $pkg->operator('negated'); >- my $negated_re = qr/\Q$negated_op\E/; >- >- my $and_op = $pkg->operator('and'); >- my $and_re = qr/^\s*\Q$and_op\E/; >- >- my $or_op = $pkg->operator('or'); >- my $or_re = qr/^\s*\Q$or_op\E/; >- >- my $group_start = $pkg->operator('group_start'); >- my $group_start_re = qr/^\s*($negated_re|$disallowed_re)?\Q$group_start\E/; >- >- my $group_end = $pkg->operator('group_end'); >- my $group_end_re = qr/^\s*\Q$group_end\E/; >- >- my $float_start = $pkg->operator('float_start'); >- my $float_start_re = qr/^\s*\Q$float_start\E/; >- >- my $float_end = $pkg->operator('float_end'); >- my $float_end_re = qr/^\s*\Q$float_end\E/; >- >- my $modifier_tag = $pkg->operator('modifier'); >- my $modifier_tag_re = qr/^\s*\Q$modifier_tag\E/; >- >- # Group start/end normally are ( and ), but can be overridden. >- # We thus include ( and ) specifically due to filters, as well as : for classes. >- my $phrase_cleanup_re = qr/\s*(\Q$required_op\E|\Q$disallowed_op\E|\Q$and_op\E|\Q$or_op\E|\Q$group_start\E|\Q$group_end\E|\Q$float_start\E|\Q$float_end\E|\Q$modifier_tag\E|\Q$negated_op\E|:|\(|\))/; >- >- # Build the filter and modifier uber-regexps >- my $facet_re = '^\s*(-?)((?:' . join( '|', @{$pkg->facet_classes}) . ')(?:\|\w+)*)\[(.+?)\]'; >- warn ' 'x$recursing." ** Facet RE: $facet_re\n" if $self->debug; >- >- my $filter_re = '^\s*(-?)(' . join( '|', @{$pkg->filters}) . ')\(([^()]+)\)'; >- my $filter_as_class_re = '^\s*(-?)(' . join( '|', @{$pkg->filters}) . '):\s*(\S+)'; >- >- my $modifier_re = '^\s*'.$modifier_tag_re.'(' . join( '|', @{$pkg->modifiers}) . ')\b'; >- my $modifier_as_class_re = '^\s*(' . join( '|', @{$pkg->modifiers}) . '):\s*(\S+)'; >- >- my $struct = shift || $self->new_plan( level => $recursing ); >- $self->parse_tree( $struct ) if (!$self->parse_tree); >- >- my $remainder = ''; >- >- while (!$remainder) { >- warn ' 'x$recursing."Start of the loop. last_type: $last_type, joiner: ".$struct->joiner.", struct: $struct\n" if $self->debug; >- if ($last_type eq 'FEND' and $fstart and $fstart != $struct) { # fall back further >- $remainder = $_; >- last; >- } elsif ($last_type eq 'FEND') { >- $fstart = undef; >- $last_type = ''; >- } >- >- if (/^\s*$/) { # end of an explicit group >- local $last_type = ''; >- last; >- } elsif (/$float_end_re/) { # end of an explicit group >- warn ' 'x$recursing."Encountered explicit float end, remainder: $'\n" if $self->debug; >- >- $remainder = $'; >- $_ = ''; >- >- $floating = 0; >- $last_type = 'FEND'; >- last; >- } elsif (/$group_end_re/) { # end of an explicit group >- warn ' 'x$recursing."Encountered explicit group end, remainder: $'\n" if $self->debug; >- >- $remainder = $'; >- $_ = ''; >- >- local $last_type = ''; >- } elsif ($self->filter_count && /$filter_re/) { # found a filter >- warn ' 'x$recursing."Encountered search filter: $1$2 set to $3\n" if $self->debug; >- >- my $negate = ($1 eq $pkg->operator('disallowed')) ? 1 : 0; >- $_ = $'; >- >- my $filter = $2; >- my $params = [ split '[,]+', $3 ]; >- >- if ($pkg->filter_callbacks->{$filter}) { >- my $replacement = $pkg->filter_callbacks->{$filter}->($self, $struct, $filter, $params, $negate); >- $_ = "$replacement $_" if ($replacement); >- } else { >- $struct->new_filter( $filter => $params, $negate ); >- } >- >- >- local $last_type = ''; >- } elsif ($self->filter_count && /$filter_as_class_re/) { # found a filter >- warn ' 'x$recursing."Encountered search filter: $1$2 set to $3\n" if $self->debug; >- >- my $negate = ($1 eq $pkg->operator('disallowed')) ? 1 : 0; >- $_ = $'; >- >- my $filter = $2; >- my $params = [ split '[,]+', $3 ]; >- >- if ($pkg->filter_callbacks->{$filter}) { >- my $replacement = $pkg->filter_callbacks->{$filter}->($self, $struct, $filter, $params, $negate); >- $_ = "$replacement $_" if ($replacement); >- } else { >- $struct->new_filter( $filter => $params, $negate ); >- } >- >- local $last_type = ''; >- } elsif ($self->modifier_count && /$modifier_re/) { # found a modifier >- warn ' 'x$recursing."Encountered search modifier: $1\n" if $self->debug; >- >- $_ = $'; >- if (!($struct->top_plan || $parser_config{$pkg}->{allow_nested_modifiers})) { >- warn ' 'x$recursing." Search modifiers only allowed at the top level of the query\n" if $self->debug; >- } else { >- $struct->new_modifier($1); >- } >- >- local $last_type = ''; >- } elsif ($self->modifier_count && /$modifier_as_class_re/) { # found a modifier >- warn ' 'x$recursing."Encountered search modifier: $1\n" if $self->debug; >- >- my $mod = $1; >- >- $_ = $'; >- if (!($struct->top_plan || $parser_config{$pkg}->{allow_nested_modifiers})) { >- warn ' 'x$recursing." Search modifiers only allowed at the top level of the query\n" if $self->debug; >- } elsif ($2 =~ /^[ty1]/i) { >- $struct->new_modifier($mod); >- } >- >- local $last_type = ''; >- } elsif (/$float_start_re/) { # start of an explicit float >- warn ' 'x$recursing."Encountered explicit float start\n" if $self->debug; >- $floating = 1; >- $fstart = $struct; >- >- $last_class = $current_class; >- $current_class = undef; >- >- $self->floating_plan( $self->new_plan( floating => 1 ) ) if (!$self->floating_plan); >- >- # pass the floating_plan struct to be modified by the float'ed chunk >- my ($floating_plan, $subremainder) = $self->new( debug => $self->debug )->decompose( $', undef, undef, undef, $self->floating_plan); >- $_ = $subremainder; >- warn ' 'x$recursing."Remainder after explicit float: $_\n" if $self->debug; >- >- $current_class = $last_class; >- >- $last_type = ''; >- } elsif (/$group_start_re/) { # start of an explicit group >- warn ' 'x$recursing."Encountered explicit group start\n" if $self->debug; >- my $negate = $1; >- my ($substruct, $subremainder) = $self->decompose( $', $current_class, $recursing + 1 ); >- $substruct->negate(1) if ($substruct && $negate); >- $struct->add_node( $substruct ) if ($substruct); >- $_ = $subremainder; >- warn ' 'x$recursing."Query remainder after bool group: $_\n" if $self->debug; >- >- local $last_type = ''; >- >- } elsif (/$and_re/) { # ANDed expression >- $_ = $'; >- warn ' 'x$recursing."Encountered AND\n" if $self->debug; >- do {warn ' 'x$recursing."!!! Already doing the bool dance for AND\n" if $self->debug; next} if ($last_type eq 'AND'); >- do {warn ' 'x$recursing."!!! Already doing the bool dance for OR\n" if $self->debug; next} if ($last_type eq 'OR'); >- local $last_type = 'AND'; >- >- warn ' 'x$recursing."Saving LHS, building RHS\n" if $self->debug; >- my $LHS = $struct; >- #my ($RHS, $subremainder) = $self->decompose( "$group_start $_ $group_end", $current_class, $recursing + 1 ); >- my ($RHS, $subremainder) = $self->decompose( $_, $current_class, $recursing + 1 ); >- $_ = $subremainder; >- >- warn ' 'x$recursing."RHS built\n" if $self->debug; >- warn ' 'x$recursing."Post-AND remainder: $subremainder\n" if $self->debug; >- >- my $wrapper = $self->new_plan( level => $recursing + 1 ); >- >- if ($LHS->floating) { >- $wrapper->{query} = $LHS->{query}; >- my $outer_wrapper = $self->new_plan( level => $recursing + 1 ); >- $outer_wrapper->add_node($_) for ($wrapper,$RHS); >- $LHS->{query} = [$outer_wrapper]; >- $struct = $LHS; >- } else { >- $wrapper->add_node($_) for ($LHS, $RHS); >- $wrapper->plan_level($wrapper->plan_level); # reset levels all the way down >- $struct = $self->new_plan( level => $recursing ); >- $struct->add_node($wrapper); >- } >- >- $self->parse_tree( $struct ) if ($self->parse_tree == $LHS); >- >- local $last_type = ''; >- } elsif (/$or_re/) { # ORed expression >- $_ = $'; >- warn ' 'x$recursing."Encountered OR\n" if $self->debug; >- do {warn ' 'x$recursing."!!! Already doing the bool dance for AND\n" if $self->debug; next} if ($last_type eq 'AND'); >- do {warn ' 'x$recursing."!!! Already doing the bool dance for OR\n" if $self->debug; next} if ($last_type eq 'OR'); >- local $last_type = 'OR'; >- >- warn ' 'x$recursing."Saving LHS, building RHS\n" if $self->debug; >- my $LHS = $struct; >- #my ($RHS, $subremainder) = $self->decompose( "$group_start $_ $group_end", $current_class, $recursing + 1 ); >- my ($RHS, $subremainder) = $self->decompose( $_, $current_class, $recursing + 2 ); >- $_ = $subremainder; >- >- warn ' 'x$recursing."RHS built\n" if $self->debug; >- warn ' 'x$recursing."Post-OR remainder: $subremainder\n" if $self->debug; >- >- my $wrapper = $self->new_plan( level => $recursing + 1, joiner => '|' ); >- >- if ($LHS->floating) { >- $wrapper->{query} = $LHS->{query}; >- my $outer_wrapper = $self->new_plan( level => $recursing + 1, joiner => '|' ); >- $outer_wrapper->add_node($_) for ($wrapper,$RHS); >- $LHS->{query} = [$outer_wrapper]; >- $struct = $LHS; >- } else { >- $wrapper->add_node($_) for ($LHS, $RHS); >- $wrapper->plan_level($wrapper->plan_level); # reset levels all the way down >- $struct = $self->new_plan( level => $recursing ); >- $struct->add_node($wrapper); >- } >- >- $self->parse_tree( $struct ) if ($self->parse_tree == $LHS); >- >- local $last_type = ''; >- } elsif ($self->facet_class_count && /$facet_re/) { # changing current class >- warn ' 'x$recursing."Encountered facet: $1$2 => $3\n" if $self->debug; >- >- my $negate = ($1 eq $pkg->operator('disallowed')) ? 1 : 0; >- my $facet = $2; >- my $facet_value = [ split '\s*#\s*', $3 ]; >- $struct->new_facet( $facet => $facet_value, $negate ); >- $_ = $'; >- >- local $last_type = ''; >- } elsif ($self->search_class_count && /$search_class_re/) { # changing current class >- >- if ($last_type eq 'CLASS') { >- $struct->remove_last_node( $current_class ); >- warn ' 'x$recursing."Encountered class change with no searches!\n" if $self->debug; >- } >- >- warn ' 'x$recursing."Encountered class change: $1\n" if $self->debug; >- >- $current_class = $struct->classed_node( $1 )->requested_class(); >- $_ = $'; >- >- local $last_type = 'CLASS'; >- } elsif (/^\s*($required_re|$disallowed_re|$negated_re)?"([^"]+)"/) { # phrase, always anded >- warn ' 'x$recursing.'Encountered' . ($1 ? " ['$1' modified]" : '') . " phrase: $2\n" if $self->debug; >- >- my $req_ness = $1 || ''; >- $req_ness = $disallowed_op if ($req_ness eq $negated_op); >- my $phrase = $2; >- >- if (!$phrase_helper) { >- warn ' 'x$recursing."Recursing into decompose with the phrase as a subquery\n" if $self->debug; >- my $after = $'; >- my ($substruct, $subremainder) = $self->decompose( qq/$req_ness"$phrase"/, $current_class, $recursing + 1, 1 ); >- $struct->add_node( $substruct ) if ($substruct); >- $_ = $after; >- } else { >- warn ' 'x$recursing."Directly parsing the phrase subquery\n" if $self->debug; >- $struct->joiner( '&' ); >- >- my $class_node = $struct->classed_node($current_class); >- >- if ($req_ness eq $disallowed_op) { >- $class_node->negate(1); >- } >- $class_node->add_phrase( $phrase ); >- >- # Save $' before we clean up $phrase >- my $temp_val = $'; >- >- # Cleanup the phrase to make it so that we don't parse things in it as anything other than atoms >- $phrase =~ s/$phrase_cleanup_re/ /g; >- >- $_ = $temp_val; >- >- } >- >- local $last_type = ''; >- >- } elsif (/^\s*($required_re|$disallowed_re)([^${group_end}${float_end}\s"]+)/) { # convert require/disallow word to {un}phrase >- warn ' 'x$recursing."Encountered required atom (mini phrase), transforming for phrase parse: $1\n" if $self->debug; >- >- $_ = $1 . '"' . $2 . '"' . $'; >- >- local $last_type = ''; >- } elsif (/^\s*([^${group_end}${float_end}\s]+)/o) { # atom >- warn ' 'x$recursing."Encountered atom: $1\n" if $self->debug; >- warn ' 'x$recursing."Remainder: $'\n" if $self->debug; >- >- my $atom = $1; >- my $after = $'; >- >- $_ = $after; >- local $last_type = ''; >- >- my $class_node = $struct->classed_node($current_class); >- >- my $prefix = ($atom =~ s/^$negated_re//o) ? '!' : ''; >- my $truncate = ($atom =~ s/\*$//o) ? '*' : ''; >- >- if ($atom ne '' and !grep { $atom =~ /^\Q$_\E+$/ } ('&','|')) { # throw away & and |, not allowed in tsquery, and not really useful anyway >-# $class_node->add_phrase( $atom ) if ($atom =~ s/^$required_re//o); >- >- $class_node->add_fts_atom( $atom, suffix => $truncate, prefix => $prefix, node => $class_node ); >- $struct->joiner( '&' ); >- } >- >- local $last_type = ''; >- } >- >- last unless ($_); >- >- } >- >- $struct = undef if >- scalar(@{$struct->query_nodes}) == 0 && >- scalar(@{$struct->filters}) == 0 && >- !$struct->top_plan; >- >- return $struct if !wantarray; >- return ($struct, $remainder); >-} >- >-=head2 find_class_index >- >- $index = $QParser->find_class_index($class, $query); >-=cut >- >-sub find_class_index { >- my $class = shift; >- my $query = shift; >- >- my ($class_part, @field_parts) = split '\|', $class; >- $class_part ||= $class; >- >- for my $idx ( 0 .. scalar(@$query) - 1 ) { >- next unless ref($$query[$idx]); >- return $idx if ( $$query[$idx]{requested_class} && $class eq $$query[$idx]{requested_class} ); >- } >- >- push(@$query, { classname => $class_part, (@field_parts ? (fields => \@field_parts) : ()), requested_class => $class, ftsquery => [], phrases => [] }); >- return -1; >-} >- >-=head2 core_limit >- >- $limit = $QParser->core_limit([$limit]); >- >-Return and/or set the core_limit. >-=cut >- >-sub core_limit { >- my $self = shift; >- my $l = shift; >- $self->{core_limit} = $l if ($l); >- return $self->{core_limit}; >-} >- >-=head2 superpage >- >- $superpage = $QParser->superpage([$superpage]); >- >-Return and/or set the superpage. >-=cut >- >-sub superpage { >- my $self = shift; >- my $l = shift; >- $self->{superpage} = $l if ($l); >- return $self->{superpage}; >-} >- >-=head2 superpage_size >- >- $size = $QParser->superpage_size([$size]); >- >-Return and/or set the superpage size. >-=cut >- >-sub superpage_size { >- my $self = shift; >- my $l = shift; >- $self->{superpage_size} = $l if ($l); >- return $self->{superpage_size}; >-} >- >- >-#------------------------------- >-package OpenILS::QueryParser::_util; >- >-# At this level, joiners are always & or |. This is not >-# the external, configurable representation of joiners that >-# defaults to # && and ||. >-sub is_joiner { >- my $str = shift; >- >- return (not ref $str and ($str eq '&' or $str eq '|')); >-} >- >-sub default_joiner { '&' } >- >-# 0 for different, 1 for the same. >-sub compare_abstract_atoms { >- my ($left, $right) = @_; >- >- foreach (qw/prefix suffix content/) { >- no warnings; # undef can stand in for '' here >- return 0 unless $left->{$_} eq $right->{$_}; >- } >- >- return 1; >-} >- >-sub fake_abstract_atom_from_phrase { >- my $phrase = shift; >- my $neg = shift; >- my $qp_class = shift || 'OpenILS::QueryParser'; >- >- my $prefix = '"'; >- if ($neg) { >- $prefix = >- $OpenILS::QueryParser::parser_config{$qp_class}{operators}{disallowed} . >- $prefix; >- } >- >- return { >- "type" => "atom", "prefix" => $prefix, "suffix" => '"', >- "content" => $phrase >- } >-} >- >-sub find_arrays_in_abstract { >- my ($hash) = @_; >- >- my @arrays; >- foreach my $key (keys %$hash) { >- if (ref $hash->{$key} eq "ARRAY") { >- push @arrays, $hash->{$key}; >- foreach (@{$hash->{$key}}) { >- push @arrays, find_arrays_in_abstract($_); >- } >- } >- } >- >- return @arrays; >-} >- >-#------------------------------- >-package OpenILS::QueryParser::Canonicalize; # not OO >-use Data::Dumper; >- >-sub _abstract_query2str_filter { >- my $f = shift; >- my $qp_class = shift || 'OpenILS::QueryParser'; >- my $qpconfig = $OpenILS::QueryParser::parser_config{$qp_class}; >- >- return sprintf( >- '%s%s(%s)', >- $f->{negate} ? $qpconfig->{operators}{disallowed} : "", >- $f->{name}, >- join(",", @{$f->{args}}) >- ); >-} >- >-sub _abstract_query2str_modifier { >- my $f = shift; >- my $qp_class = shift || 'OpenILS::QueryParser'; >- my $qpconfig = $OpenILS::QueryParser::parser_config{$qp_class}; >- >- return $qpconfig->{operators}{modifier} . $f; >-} >- >-sub _kid_list { >- my $children = shift; >- my $op = (keys %$children)[0]; >- return @{$$children{$op}}; >-} >- >- >-# This should produce an equivalent query to the original, given an >-# abstract_query. >-sub abstract_query2str_impl { >- my $abstract_query = shift; >- my $depth = shift || 0; >- >- my $qp_class ||= shift || 'OpenILS::QueryParser'; >- my $force_qp_node = shift || 0; >- my $qpconfig = $OpenILS::QueryParser::parser_config{$qp_class}; >- >- my $fs = $qpconfig->{operators}{float_start}; >- my $fe = $qpconfig->{operators}{float_end}; >- my $gs = $qpconfig->{operators}{group_start}; >- my $ge = $qpconfig->{operators}{group_end}; >- my $and = $qpconfig->{operators}{and}; >- my $or = $qpconfig->{operators}{or}; >- my $ng = $qpconfig->{operators}{negated}; >- >- my $isnode = 0; >- my $negate = ''; >- my $size = 0; >- my $q = ""; >- >- if (exists $abstract_query->{type}) { >- if ($abstract_query->{type} eq 'query_plan') { >- $q .= join(" ", map { _abstract_query2str_filter($_, $qp_class) } @{$abstract_query->{filters}}) if >- exists $abstract_query->{filters}; >- >- $q .= ($q ? ' ' : '') . join(" ", map { _abstract_query2str_modifier($_, $qp_class) } @{$abstract_query->{modifiers}}) if >- exists $abstract_query->{modifiers}; >- >- $size = _kid_list($abstract_query->{children}); >- if ($abstract_query->{negate}) { >- $isnode = 1; >- $negate = $ng; >- } >- $isnode = 1 if ($size > 1 and ($force_qp_node or $depth)); >- #warn "size: $size, depth: $depth, isnode: $isnode, AQ: ".Dumper($abstract_query); >- } elsif ($abstract_query->{type} eq 'node') { >- if ($abstract_query->{alias}) { >- $q .= ($q ? ' ' : '') . $abstract_query->{alias}; >- $q .= "|$_" foreach @{$abstract_query->{alias_fields}}; >- } else { >- $q .= ($q ? ' ' : '') . $abstract_query->{class}; >- $q .= "|$_" foreach @{$abstract_query->{fields}}; >- } >- $q .= ":"; >- $isnode = 1; >- } elsif ($abstract_query->{type} eq 'atom') { >- my $prefix = $abstract_query->{prefix} || ''; >- $prefix = $qpconfig->{operators}{negated} if $prefix eq '!'; >- $q .= ($q ? ' ' : '') . $prefix . >- ($abstract_query->{content} || '') . >- ($abstract_query->{suffix} || ''); >- } elsif ($abstract_query->{type} eq 'facet') { >- # facet syntax [ # ] is hardcoded I guess? >- my $prefix = $abstract_query->{negate} ? $qpconfig->{operators}{disallowed} : ''; >- $q .= ($q ? ' ' : '') . $prefix . $abstract_query->{name} . "[" . >- join(" # ", @{$abstract_query->{values}}) . "]"; >- } >- } >- >- my $next_depth = int($size > 1); >- >- if (exists $abstract_query->{children}) { >- >- my $op = (keys(%{$abstract_query->{children}}))[0]; >- >- if ($abstract_query->{floating}) { # always the top node! >- my $sub_node = pop @{$abstract_query->{children}{$op}}; >- >- $abstract_query->{floating} = 0; >- $q = $fs . " " . abstract_query2str_impl($abstract_query,0,$qp_class, 1) . $fe. " "; >- >- $abstract_query = $sub_node; >- } >- >- if ($abstract_query && exists $abstract_query->{children}) { >- $op = (keys(%{$abstract_query->{children}}))[0]; >- $q .= ($q ? ' ' : '') . join( >- ($op eq '&' ? ' ' : " $or "), >- map { >- my $x = abstract_query2str_impl($_, $depth + $next_depth, $qp_class, $force_qp_node); $x =~ s/^\s+//; $x =~ s/\s+$//; $x; >- } @{$abstract_query->{children}{$op}} >- ); >- } >- } elsif ($abstract_query->{'&'} or $abstract_query->{'|'}) { >- my $op = (keys(%{$abstract_query}))[0]; >- $q .= ($q ? ' ' : '') . join( >- ($op eq '&' ? ' ' : " $or "), >- map { >- my $x = abstract_query2str_impl($_, $depth + $next_depth, $qp_class, $force_qp_node); $x =~ s/^\s+//; $x =~ s/\s+$//; $x; >- } @{$abstract_query->{$op}} >- ); >- } >- >- $q = "$gs$q$ge" if ($isnode); >- $q = $negate . $q if ($q);; >- >- return $q; >-} >- >-#------------------------------- >-package OpenILS::QueryParser::query_plan; >- >-sub QueryParser { >- my $self = shift; >- return unless ref($self); >- return $self->{QueryParser}; >-} >- >-sub new { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my %args = (query => [], joiner => '&', @_); >- >- return bless \%args => $pkg; >-} >- >-sub new_node { >- my $self = shift; >- my $pkg = ref($self) || $self; >- my $node = do{$pkg.'::node'}->new( plan => $self, @_ ); >- $self->add_node( $node ); >- return $node; >-} >- >-sub new_facet { >- my $self = shift; >- my $pkg = ref($self) || $self; >- my $name = shift; >- my $args = shift; >- my $negate = shift; >- >- my $node = do{$pkg.'::facet'}->new( plan => $self, name => $name, 'values' => $args, negate => $negate ); >- $self->add_node( $node ); >- >- return $node; >-} >- >-sub new_filter { >- my $self = shift; >- my $pkg = ref($self) || $self; >- my $name = shift; >- my $args = shift; >- my $negate = shift; >- >- my $node = do{$pkg.'::filter'}->new( plan => $self, name => $name, args => $args, negate => $negate ); >- $self->add_filter( $node ); >- >- return $node; >-} >- >- >-sub _merge_filters { >- my $left_filter = shift; >- my $right_filter = shift; >- my $join = shift; >- >- return unless $left_filter or $right_filter; >- return $right_filter unless $left_filter; >- return $left_filter unless $right_filter; >- >- my $args = $left_filter->{args} || []; >- >- if ($join eq '|') { >- push(@$args, @{$right_filter->{args}}); >- >- } else { >- # find the intersect values >- my %new_vals; >- map { $new_vals{$_} = 1 } @{$right_filter->{args} || []}; >- $args = [ grep { $new_vals{$_} } @$args ]; >- } >- >- $left_filter->{args} = $args; >- return $left_filter; >-} >- >-sub collapse_filters { >- my $self = shift; >- my $name = shift; >- >- # start by merging any filters at this level. >- # like-level filters are always ORed together >- >- my $cur_filter; >- my @cur_filters = grep {$_->name eq $name } @{ $self->filters }; >- if (@cur_filters) { >- $cur_filter = shift @cur_filters; >- my $args = $cur_filter->{args} || []; >- $cur_filter = _merge_filters($cur_filter, $_, '|') for @cur_filters; >- } >- >- # next gather the collapsed filters from sub-plans and >- # merge them with our own >- >- my @subquery = @{$self->{query}}; >- >- while (@subquery) { >- my $blob = shift @subquery; >- shift @subquery; # joiner >- next unless $blob->isa('OpenILS::QueryParser::query_plan'); >- my $sub_filter = $blob->collapse_filters($name); >- $cur_filter = _merge_filters($cur_filter, $sub_filter, $self->joiner); >- } >- >- if ($self->QueryParser->debug) { >- my @args = ($cur_filter and $cur_filter->{args}) ? @{$cur_filter->{args}} : (); >- warn "collapse_filters($name) => [@args]\n"; >- } >- >- return $cur_filter; >-} >- >-sub find_filter { >- my $self = shift; >- my $needle = shift;; >- return unless ($needle); >- >- my $filter = $self->collapse_filters($needle); >- >- warn "find_filter($needle) => " . >- (($filter and $filter->{args}) ? "@{$filter->{args}}" : '[]') . "\n" >- if $self->QueryParser->debug; >- >- return $filter ? ($filter) : (); >-} >- >-sub find_modifier { >- my $self = shift; >- my $needle = shift;; >- return unless ($needle); >- return grep { $_->name eq $needle } @{ $self->modifiers }; >-} >- >-sub new_modifier { >- my $self = shift; >- my $pkg = ref($self) || $self; >- my $name = shift; >- >- my $node = do{$pkg.'::modifier'}->new( $name ); >- $self->add_modifier( $node ); >- >- return $node; >-} >- >-sub classed_node { >- my $self = shift; >- my $requested_class = shift; >- >- my $node; >- for my $n (@{$self->{query}}) { >- next unless (ref($n) && $n->isa( 'OpenILS::QueryParser::query_plan::node' )); >- if ($n->requested_class eq $requested_class) { >- $node = $n; >- last; >- } >- } >- >- if (!$node) { >- $node = $self->new_node; >- $node->requested_class( $requested_class ); >- } >- >- return $node; >-} >- >-sub remove_last_node { >- my $self = shift; >- my $requested_class = shift; >- >- my $old = pop(@{$self->query_nodes}); >- pop(@{$self->query_nodes}) if (@{$self->query_nodes}); >- >- return $old; >-} >- >-sub query_nodes { >- my $self = shift; >- return $self->{query}; >-} >- >-sub floating { >- my $self = shift; >- my $f = shift; >- $self->{floating} = $f if (defined $f); >- return $self->{floating}; >-} >- >-sub add_node { >- my $self = shift; >- my $node = shift; >- >- $self->{query} ||= []; >- push(@{$self->{query}}, $self->joiner) if (@{$self->{query}}); >- push(@{$self->{query}}, $node); >- >- return $self; >-} >- >-sub top_plan { >- my $self = shift; >- >- return $self->{level} ? 0 : 1; >-} >- >-sub plan_level { >- my $self = shift; >- my $level = shift; >- >- if (defined $level) { >- $self->{level} = $level; >- for (@{$self->query_nodes}) { >- $_->plan_level($level + 1) if (ref and $_->isa('OpenILS::QueryParser::query_plan')); >- } >- } >- >- return $self->{level}; >-} >- >-sub joiner { >- my $self = shift; >- my $joiner = shift; >- >- $self->{joiner} = $joiner if ($joiner); >- return $self->{joiner}; >-} >- >-sub modifiers { >- my $self = shift; >- $self->{modifiers} ||= []; >- return $self->{modifiers}; >-} >- >-sub add_modifier { >- my $self = shift; >- my $modifier = shift; >- >- $self->{modifiers} ||= []; >- $self->{modifiers} = [ grep {$_->name ne $modifier->name} @{$self->{modifiers}} ]; >- >- push(@{$self->{modifiers}}, $modifier); >- >- return $self; >-} >- >-sub facets { >- my $self = shift; >- $self->{facets} ||= []; >- return $self->{facets}; >-} >- >-sub add_facet { >- my $self = shift; >- my $facet = shift; >- >- $self->{facets} ||= []; >- $self->{facets} = [ grep {$_->name ne $facet->name} @{$self->{facets}} ]; >- >- push(@{$self->{facets}}, $facet); >- >- return $self; >-} >- >-sub filters { >- my $self = shift; >- $self->{filters} ||= []; >- return $self->{filters}; >-} >- >-sub add_filter { >- my $self = shift; >- my $filter = shift; >- >- $self->{filters} ||= []; >- >- push(@{$self->{filters}}, $filter); >- >- return $self; >-} >- >-sub negate { >- my $self = shift; >- my $negate = shift; >- >- $self->{negate} = $negate if (defined $negate); >- >- return $self->{negate}; >-} >- >-# %opts supports two options at this time: >-# no_phrases : >-# If true, do not do anything to the phrases >-# fields on any discovered nodes. >-# with_config : >-# If true, also return the query parser config as part of the blob. >-# This will get set back to 0 before recursion to avoid repetition. >-sub to_abstract_query { >- my $self = shift; >- my %opts = @_; >- >- my $pkg = ref $self->QueryParser || $self->QueryParser; >- >- my $abstract_query = { >- type => "query_plan", >- floating => $self->floating, >- level => $self->plan_level, >- filters => [map { $_->to_abstract_query } @{$self->filters}], >- modifiers => [map { $_->to_abstract_query } @{$self->modifiers}], >- negate => $self->negate >- }; >- >- if ($opts{with_config}) { >- $opts{with_config} = 0; >- $abstract_query->{config} = $OpenILS::QueryParser::parser_config{$pkg}; >- } >- >- my $kids = []; >- >- for my $qnode (@{$self->query_nodes}) { >- # Remember: qnode can be a joiner string, a node, or another query_plan >- >- if (OpenILS::QueryParser::_util::is_joiner($qnode)) { >- if ($abstract_query->{children}) { >- my $open_joiner = (keys(%{$abstract_query->{children}}))[0]; >- next if $open_joiner eq $qnode; >- >- my $oldroot = $abstract_query->{children}; >- $kids = [$oldroot]; >- $abstract_query->{children} = {$qnode => $kids}; >- } else { >- $abstract_query->{children} = {$qnode => $kids}; >- } >- } else { >- push @$kids, $qnode->to_abstract_query(%opts); >- } >- } >- >- $abstract_query->{children} ||= { OpenILS::QueryParser::_util::default_joiner() => $kids }; >- return $abstract_query; >-} >- >- >-#------------------------------- >-package OpenILS::QueryParser::query_plan::node; >-use Data::Dumper; >-$Data::Dumper::Indent = 0; >- >-sub new { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my %args = @_; >- >- return bless \%args => $pkg; >-} >- >-sub new_atom { >- my $self = shift; >- my $pkg = ref($self) || $self; >- return do{$pkg.'::atom'}->new( @_ ); >-} >- >-sub requested_class { # also split into classname, fields and alias >- my $self = shift; >- my $class = shift; >- >- if ($class) { >- my @afields; >- my (undef, $alias) = split '#', $class; >- if ($alias) { >- $class =~ s/#[^|]+//; >- ($alias, @afields) = split '\|', $alias; >- } >- >- my @fields = @afields; >- my ($class_part, @field_parts) = split '\|', $class; >- for my $f (@field_parts) { >- push(@fields, $f) unless (grep { $f eq $_ } @fields); >- } >- >- $class_part ||= $class; >- >- $self->{requested_class} = $class; >- $self->{alias} = $alias if $alias; >- $self->{alias_fields} = \@afields if $alias; >- $self->{classname} = $class_part; >- $self->{fields} = \@fields; >- } >- >- return $self->{requested_class}; >-} >- >-sub plan { >- my $self = shift; >- my $plan = shift; >- >- $self->{plan} = $plan if ($plan); >- return $self->{plan}; >-} >- >-sub alias { >- my $self = shift; >- my $alias = shift; >- >- $self->{alias} = $alias if ($alias); >- return $self->{alias}; >-} >- >-sub alias_fields { >- my $self = shift; >- my $alias = shift; >- >- $self->{alias_fields} = $alias if ($alias); >- return $self->{alias_fields}; >-} >- >-sub classname { >- my $self = shift; >- my $class = shift; >- >- $self->{classname} = $class if ($class); >- return $self->{classname}; >-} >- >-sub fields { >- my $self = shift; >- my @fields = @_; >- >- $self->{fields} ||= []; >- $self->{fields} = \@fields if (@fields); >- return $self->{fields}; >-} >- >-sub phrases { >- my $self = shift; >- my @phrases = @_; >- >- $self->{phrases} ||= []; >- $self->{phrases} = \@phrases if (@phrases); >- return $self->{phrases}; >-} >- >-sub add_phrase { >- my $self = shift; >- my $phrase = shift; >- >- push(@{$self->phrases}, $phrase); >- >- return $self; >-} >- >-sub negate { >- my $self = shift; >- my $negate = shift; >- >- $self->{negate} = $negate if (defined $negate); >- >- return $self->{negate}; >-} >- >-sub query_atoms { >- my $self = shift; >- my @query_atoms = @_; >- >- $self->{query_atoms} ||= []; >- $self->{query_atoms} = \@query_atoms if (@query_atoms); >- return $self->{query_atoms}; >-} >- >-sub add_fts_atom { >- my $self = shift; >- my $atom = shift; >- >- if (!ref($atom)) { >- my $content = $atom; >- my @parts = @_; >- >- $atom = $self->new_atom( content => $content, @parts ); >- } >- >- push(@{$self->query_atoms}, $self->plan->joiner) if (@{$self->query_atoms}); >- push(@{$self->query_atoms}, $atom); >- >- return $self; >-} >- >-sub add_dummy_atom { >- my $self = shift; >- my @parts = @_; >- >- my $atom = $self->new_atom( @parts, dummy => 1 ); >- >- push(@{$self->query_atoms}, $self->plan->joiner) if (@{$self->query_atoms}); >- push(@{$self->query_atoms}, $atom); >- >- return $self; >-} >- >-# This will find up to one occurence of @$short_list within @$long_list, and >-# replace it with the single atom $replacement. >-sub replace_phrase_in_abstract_query { >- my ($self, $short_list, $long_list, $replacement) = @_; >- >- my $success = 0; >- my @already = (); >- my $goal = scalar @$short_list; >- >- for (my $i = 0; $i < scalar (@$long_list); $i++) { >- my $right = $long_list->[$i]; >- >- if (OpenILS::QueryParser::_util::compare_abstract_atoms( >- $short_list->[scalar @already], $right >- )) { >- push @already, $i; >- } elsif (scalar @already) { >- @already = (); >- next; >- } >- >- if (scalar @already == $goal) { >- splice @$long_list, $already[0], scalar(@already), $replacement; >- $success = 1; >- last; >- } >- } >- >- return $success; >-} >- >-sub to_abstract_query { >- my $self = shift; >- my %opts = @_; >- >- my $pkg = ref $self->plan->QueryParser || $self->plan->QueryParser; >- >- my $abstract_query = { >- "type" => "node", >- "alias" => $self->alias, >- "alias_fields" => $self->alias_fields, >- "class" => $self->classname, >- "fields" => $self->fields >- }; >- >- my $kids = []; >- >- for my $qatom (@{$self->query_atoms}) { >- if (OpenILS::QueryParser::_util::is_joiner($qatom)) { >- if ($abstract_query->{children}) { >- my $open_joiner = (keys(%{$abstract_query->{children}}))[0]; >- next if $open_joiner eq $qatom; >- >- my $oldroot = $abstract_query->{children}; >- $kids = [$oldroot]; >- $abstract_query->{children} = {$qatom => $kids}; >- } else { >- $abstract_query->{children} = {$qatom => $kids}; >- } >- } else { >- push @$kids, $qatom->to_abstract_query; >- } >- } >- >- $abstract_query->{children} ||= { OpenILS::QueryParser::_util::default_joiner() => $kids }; >- >- if ($self->{phrases} and not $opts{no_phrases}) { >- for my $phrase (@{$self->{phrases}}) { >- # Phrases appear duplication in a real QP tree, and we don't want >- # that duplication in our abstract query. So for all our phrases, >- # break them into atoms as QP would, and remove any matching >- # sequences of atoms from our abstract query. >- >- my $tmp_prefix = ''; >- $tmp_prefix = $OpenILS::QueryParser::parser_config{$pkg}{operators}{disallowed} if ($self->{negate}); >- >- my $tmptree = $self->{plan}->{QueryParser}->new(query => $tmp_prefix.'"'.$phrase.'"')->parse->parse_tree; >- if ($tmptree) { >- # For a well-behaved phrase, we should now have only one node >- # in the $tmptree query plan, and that node should have an >- # orderly list of atoms and joiners. >- >- if ($tmptree->{query} and scalar(@{$tmptree->{query}}) == 1) { >- my $tmplist; >- >- eval { >- $tmplist = $tmptree->{query}->[0]->to_abstract_query( >- no_phrases => 1 >- )->{children}->{'&'}->[0]->{children}->{'&'}; >- }; >- next if $@; >- >- foreach ( >- OpenILS::QueryParser::_util::find_arrays_in_abstract($abstract_query->{children}) >- ) { >- last if $self->replace_phrase_in_abstract_query( >- $tmplist, >- $_, >- OpenILS::QueryParser::_util::fake_abstract_atom_from_phrase($phrase, $self->{negate}, $pkg) >- ); >- } >- } >- } >- } >- } >- >- $abstract_query->{children} ||= { OpenILS::QueryParser::_util::default_joiner() => $kids }; >- return $abstract_query; >-} >- >-#------------------------------- >-package OpenILS::QueryParser::query_plan::node::atom; >- >-sub new { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my %args = @_; >- >- return bless \%args => $pkg; >-} >- >-sub node { >- my $self = shift; >- return unless (ref $self); >- return $self->{node}; >-} >- >-sub content { >- my $self = shift; >- return unless (ref $self); >- return $self->{content}; >-} >- >-sub prefix { >- my $self = shift; >- return unless (ref $self); >- return $self->{prefix}; >-} >- >-sub suffix { >- my $self = shift; >- return unless (ref $self); >- return $self->{suffix}; >-} >- >-sub to_abstract_query { >- my ($self) = @_; >- >- return { >- (map { $_ => $self->$_ } qw/prefix suffix content/), >- "type" => "atom" >- }; >-} >-#------------------------------- >-package OpenILS::QueryParser::query_plan::filter; >- >-sub new { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my %args = @_; >- >- return bless \%args => $pkg; >-} >- >-sub plan { >- my $self = shift; >- return $self->{plan}; >-} >- >-sub name { >- my $self = shift; >- return $self->{name}; >-} >- >-sub negate { >- my $self = shift; >- return $self->{negate}; >-} >- >-sub args { >- my $self = shift; >- return $self->{args}; >-} >- >-sub to_abstract_query { >- my ($self) = @_; >- >- return { >- map { $_ => $self->$_ } qw/name negate args/ >- }; >-} >- >-#------------------------------- >-package OpenILS::QueryParser::query_plan::facet; >- >-sub new { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my %args = @_; >- >- return bless \%args => $pkg; >-} >- >-sub plan { >- my $self = shift; >- return $self->{plan}; >-} >- >-sub name { >- my $self = shift; >- return $self->{name}; >-} >- >-sub negate { >- my $self = shift; >- return $self->{negate}; >-} >- >-sub values { >- my $self = shift; >- return $self->{'values'}; >-} >- >-sub to_abstract_query { >- my ($self) = @_; >- >- return { >- (map { $_ => $self->$_ } qw/name negate values/), >- "type" => "facet" >- }; >-} >- >-#------------------------------- >-package OpenILS::QueryParser::query_plan::modifier; >- >-sub new { >- my $pkg = shift; >- $pkg = ref($pkg) || $pkg; >- my $modifier = shift; >- my $negate = shift; >- >- return bless { name => $modifier, negate => $negate } => $pkg; >-} >- >-sub name { >- my $self = shift; >- return $self->{name}; >-} >- >-sub negate { >- my $self = shift; >- return $self->{negate}; >-} >- >-sub to_abstract_query { >- my ($self) = @_; >- >- return $self->name; >-} >-1; >diff --git a/about.pl b/about.pl >index 184e9375db..f0ae48de97 100755 >--- a/about.pl >+++ b/about.pl >@@ -230,35 +230,6 @@ if ( ! C4::Context->config('tmp_path') ) { > } > } > >-# Test QueryParser configuration sanity >-if ( C4::Context->preference( 'UseQueryParser' ) ) { >- # Get the QueryParser configuration file name >- my $queryparser_file = C4::Context->config( 'queryparser_config' ); >- my $queryparser_fallback_file = '/etc/koha/searchengine/queryparser.yaml'; >- # Check QueryParser is functional >- my $QParser = C4::Context->queryparser(); >- my $queryparser_error = {}; >- if ( ! defined $QParser || ref($QParser) ne 'Koha::QueryParser::Driver::PQF' ) { >- # Error initializing the QueryParser object >- # Get the used queryparser.yaml file path to report the user >- $queryparser_error->{ fallback } = ( defined $queryparser_file ) ? 0 : 1; >- $queryparser_error->{ file } = ( defined $queryparser_file ) >- ? $queryparser_file >- : $queryparser_fallback_file; >- # Report error data to the template >- $template->param( QueryParserError => $queryparser_error ); >- } else { >- # Check for an absent queryparser_config entry in koha-conf.xml >- if ( ! defined $queryparser_file ) { >- # Not an error but a warning for the missing entry in koha-conf-xml >- push @xml_config_warnings, { >- error => 'queryparser_entry_missing', >- file => $queryparser_fallback_file >- }; >- } >- } >-} >- > # Test Zebra facets configuration > if ( !defined C4::Context->config('use_zebra_facets') ) { > push @xml_config_warnings, { error => 'use_zebra_facets_entry_missing' }; >diff --git a/cataloguing/addbooks.pl b/cataloguing/addbooks.pl >index f3b2e43fbb..9f6fec7f05 100755 >--- a/cataloguing/addbooks.pl >+++ b/cataloguing/addbooks.pl >@@ -68,21 +68,14 @@ if ($query) { > # build query > my @operands = $query; > >- my $QParser; >- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); > my $builtquery; > my $query_cgi; > my $builder = Koha::SearchEngine::QueryBuilder->new( > { index => $Koha::SearchEngine::BIBLIOS_INDEX } ); > my $searcher = Koha::SearchEngine::Search->new( > { index => $Koha::SearchEngine::BIBLIOS_INDEX } ); >- if ($QParser) { >- $builtquery = $query; >- $query_cgi = "q=".uri_escape_utf8($query); >- } else { >- ( undef, $builtquery, undef, $query_cgi, undef, undef, undef, undef, undef, undef ) = >- $builder->build_query_compat( undef, \@operands, undef, undef, undef, 0, $lang ); >- } >+ ( undef, $builtquery, undef, $query_cgi, undef, undef, undef, undef, undef, undef ) = >+ $builder->build_query_compat( undef, \@operands, undef, undef, undef, 0, $lang ); > > $template->param( search_query => $builtquery ) if C4::Context->preference('DumpSearchQueryTemplate'); > >diff --git a/cataloguing/value_builder/marc21_linking_section.pl b/cataloguing/value_builder/marc21_linking_section.pl >index f537516c04..ccadc9cac6 100755 >--- a/cataloguing/value_builder/marc21_linking_section.pl >+++ b/cataloguing/value_builder/marc21_linking_section.pl >@@ -172,15 +172,8 @@ my $launcher = sub { > my $startfrom = $query->param('startfrom'); > my $resultsperpage = $query->param('resultsperpage') || 20; > my $orderby; >- my $QParser; >- $QParser = C4::Context->queryparser if ( C4::Context->preference('UseQueryParser') ); >- my $op; >+ my $op = 'and'; > >- if ($QParser) { >- $op = '&&'; >- } else { >- $op = 'and'; >- } > my $searcher = Koha::SearchEngine::Search->new( > { index => $Koha::SearchEngine::BIBLIOS_INDEX } ); > $search = 'kw:' . $search . " $op mc-itemtype:" . $itype if $itype; >diff --git a/cataloguing/value_builder/unimarc_field_4XX.pl b/cataloguing/value_builder/unimarc_field_4XX.pl >index 5b95f14581..259a903817 100755 >--- a/cataloguing/value_builder/unimarc_field_4XX.pl >+++ b/cataloguing/value_builder/unimarc_field_4XX.pl >@@ -359,14 +359,7 @@ sub plugin { > my $startfrom = $query->param('startfrom'); > my $resultsperpage = $query->param('resultsperpage') || 20; > my $orderby; >- my $QParser; >- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); >- my $op; >- if ($QParser) { >- $op = '&&'; >- } else { >- $op = 'and'; >- } >+ my $op = 'and'; > $search = 'kw:'.$search." $op mc-itemtype:".$itype if $itype; > my $searcher = Koha::SearchEngine::Search->new({index => $Koha::SearchEngine::BIBLIOS_INDEX}); > my ( $errors, $results, $total_hits ) = $searcher->simple_search_compat($search, $startfrom * $resultsperpage, $resultsperpage ); >diff --git a/debian/templates/koha-conf-site.xml.in b/debian/templates/koha-conf-site.xml.in >index a6883945c4..c8a7bee487 100644 >--- a/debian/templates/koha-conf-site.xml.in >+++ b/debian/templates/koha-conf-site.xml.in >@@ -304,7 +304,6 @@ __END_SRU_PUBLICSERVER__ > <zebra_lockdir>/var/lock/koha/__KOHASITE__</zebra_lockdir> > <use_zebra_facets>1</use_zebra_facets> > <zebra_max_record_size>1024</zebra_max_record_size> >- <queryparser_config>/etc/koha/searchengine/queryparser.yaml</queryparser_config> > <log4perl_conf>__KOHA_CONF_DIR__/log4perl.conf</log4perl_conf> > <!-- Uncomment/edit next setting if you want to adjust zebra log levels. > Default is: none,fatal,warn. >diff --git a/etc/koha-conf.xml b/etc/koha-conf.xml >index b3466070d9..17e3e1be4e 100644 >--- a/etc/koha-conf.xml >+++ b/etc/koha-conf.xml >@@ -121,7 +121,6 @@ __PAZPAR2_TOGGLE_XML_POST__ > <zebra_lockdir>__ZEBRA_LOCK_DIR__</zebra_lockdir> > <use_zebra_facets>1</use_zebra_facets> > <zebra_max_record_size>1024</zebra_max_record_size> >- <queryparser_config>__KOHA_CONF_DIR__/searchengine/queryparser.yaml</queryparser_config> > <log4perl_conf>__KOHA_CONF_DIR__/log4perl.conf</log4perl_conf> > <!-- Uncomment/edit next setting if you want to adjust zebra log levels. > Default is: none,fatal,warn. >diff --git a/etc/searchengine/queryparser.yaml b/etc/searchengine/queryparser.yaml >deleted file mode 100644 >index d9f637bfd3..0000000000 >--- a/etc/searchengine/queryparser.yaml >+++ /dev/null >@@ -1,1647 +0,0 @@ >---- >-field_mappings: >- author: >- author: >- aliases: >- - au >- bib1_mapping: >- biblioserver: >- 1: 1003 >- enabled: 1 >- index: '' >- label: '' >- conference: >- aliases: >- - conference >- - cfn >- bib1_mapping: >- biblioserver: >- 1: 1006 >- enabled: 1 >- index: conference >- label: Conference >- corporate: >- aliases: >- - corporate >- - cpn >- bib1_mapping: >- biblioserver: >- 1: 1005 >- enabled: 1 >- index: corporate >- label: Corporate >- exactauthor: >- aliases: >- - exactauthor >- - au,ext >- bib1_mapping: >- biblioserver: >- 1: 1003 >- 4: 1 >- 6: 3 >- enabled: 1 >- index: exactauthor >- label: Exactauthor >- personal: >- aliases: >- - personal >- bib1_mapping: >- biblioserver: >- 1: 1004 >- enabled: 1 >- index: personal >- label: Personal >- keyword: >- keyword: >- aliases: >- - any >- - kw >- bib1_mapping: >- authorityserver: >- 1: Any >- biblioserver: >- 1: 1016 >- enabled: 1 >- index: 'keyword' >- label: 'Keyword' >- abstract: >- aliases: >- - abstract >- - ab >- bib1_mapping: >- biblioserver: >- 1: 62 >- enabled: 1 >- index: abstract >- label: Abstract >- acqsource: >- aliases: >- - acqsource >- bib1_mapping: >- biblioserver: >- 1: 8015 >- enabled: 1 >- index: acqsource >- label: Acqsource >- alwaysmatch: >- aliases: >- - alwaysmatch >- - all >- bib1_mapping: >- authorityserver: >- 1: _ALLRECORDS >- 2: 103 >- biblioserver: >- 1: _ALLRECORDS >- 2: 103 >- enabled: 1 >- index: alwaysmatch >- label: Alwaysmatch >- arl: >- aliases: >- - arl >- bib1_mapping: >- biblioserver: >- 1: 9904 r=r >- enabled: 1 >- index: arl >- label: Arl >- arp: >- aliases: >- - arp >- bib1_mapping: >- biblioserver: >- 1: 9013 r=r >- enabled: 1 >- index: arp >- label: Arp >- audience: >- aliases: >- - audience >- - aud >- bib1_mapping: >- biblioserver: >- 1: 8822 >- enabled: 1 >- index: audience >- label: Audience >- authtype: >- aliases: >- - authtype >- - at >- bib1_mapping: >- authorityserver: >- 1: authtype >- 5: 100 >- enabled: 1 >- index: authtype >- label: Authtype >- authorext: >- alias: [] >- bib1_mapping: >- biblioserver: >- 1: 1003 >- 4: 1 >- 6: 2 >- enabled: 1 >- index: author >- label: Authorkw >- authorkw: >- alias: [] >- bib1_mapping: >- biblioserver: >- 1: 1003 >- enabled: 1 >- index: author >- label: Authorkw >- barcode: >- aliases: >- - barcode >- - bc >- bib1_mapping: >- biblioserver: >- 1: 8023 >- enabled: 1 >- index: barcode >- label: Barcode >- bib-level: >- bib1_mapping: >- biblioserver: >- 1: 1021 >- enabled: 1 >- index: bib-level >- aliases: >- - bib-level >- label: Bib-level >- biblionumber: >- bib1_mapping: >- biblioserver: >- 1: 9002 >- 5: 100 >- enabled: 1 >- index: biblionumber >- aliases: >- - biblionumber >- label: Biblionumber >- biography: >- bib1_mapping: >- biblioserver: >- 1: 8834 >- enabled: 1 >- index: biography >- aliases: >- - biography >- - bio >- label: Biography >- biography-code: >- bib1_mapping: >- biblioserver: >- 1: Biography-code >- enabled: 1 >- index: biography-code >- aliases: >- - biography-code >- label: Biography-code >- ccode: >- bib1_mapping: >- biblioserver: >- 1: 8009 >- enabled: 1 >- index: ccode >- aliases: >- - ccode >- - mc-ccode >- label: Ccode >- classification-source: >- bib1_mapping: >- biblioserver: >- 1: 8003 >- enabled: 1 >- index: classification-source >- aliases: >- - classification-source >- label: Classification-source >- cn-bib-sort: >- bib1_mapping: >- biblioserver: >- 1: 9005 >- enabled: 1 >- index: cn-bib-sort >- aliases: >- - cn-bib-sort >- label: Cn-bib-sort >- cn-bib-source: >- bib1_mapping: >- biblioserver: >- 1: 9004 >- enabled: 1 >- index: cn-bib-source >- aliases: >- - cn-bib-source >- label: Cn-bib-source >- cn-class: >- bib1_mapping: >- biblioserver: >- 1: 9007 >- enabled: 1 >- index: cn-class >- aliases: >- - cn-class >- label: Cn-class >- cn-item: >- bib1_mapping: >- biblioserver: >- 1: 9008 >- enabled: 1 >- index: cn-item >- aliases: >- - cn-item >- label: Cn-item >- cn-prefix: >- bib1_mapping: >- biblioserver: >- 1: 9009 >- enabled: 1 >- index: cn-prefix >- aliases: >- - cn-prefix >- label: Cn-prefix >- cn-sort: >- bib1_mapping: >- biblioserver: >- 1: 8007 >- enabled: 1 >- index: cn-sort >- aliases: >- - cn-sort >- label: Cn-sort >- cn-suffix: >- bib1_mapping: >- biblioserver: >- 1: 9010 >- enabled: 1 >- index: cn-suffix >- aliases: >- - cn-suffix >- label: Cn-suffix >- code-institution: >- bib1_mapping: >- biblioserver: >- 1: 56 >- enabled: 1 >- index: code-institution >- aliases: >- - code-institution >- label: Code-institution >- coded-location-qualifier: >- bib1_mapping: >- biblioserver: >- 1: 8016 >- enabled: 1 >- index: coded-location-qualifier >- aliases: >- - coded-location-qualifier >- label: Coded-location-qualifier >- content-type: >- bib1_mapping: >- biblioserver: >- 1: 1034 >- enabled: 1 >- index: content-type >- aliases: >- - content-type >- - ctype >- label: Content-type >- control-number: >- bib1_mapping: >- biblioserver: >- 1: 9001 >- enabled: 1 >- index: control-number >- aliases: >- - control-number >- label: Control-number >- copynumber: >- bib1_mapping: >- biblioserver: >- 1: 8027 >- enabled: 1 >- index: copynumber >- aliases: >- - copynumber >- label: Copynumber >- curriculum: >- bib1_mapping: >- biblioserver: >- 1: 9658 >- enabled: 1 >- index: curriculum >- aliases: >- - curriculum >- label: Curriculum >- damaged: >- bib1_mapping: >- biblioserver: >- 1: 8005 >- enabled: 1 >- index: damaged >- aliases: >- - damaged >- label: Damaged >- date-entered-on-file: >- bib1_mapping: >- biblioserver: >- 1: date-entered-on-file >- enabled: 1 >- index: date-entered-on-file >- aliases: >- - date-entered-on-file >- label: Date-entered-on-file >- datelastborrowed: >- bib1_mapping: >- biblioserver: >- 1: 8026 >- enabled: 1 >- index: datelastborrowed >- aliases: >- - datelastborrowed >- label: Datelastborrowed >- datelastseen: >- bib1_mapping: >- biblioserver: >- 1: 8025 >- enabled: 1 >- index: datelastseen >- aliases: >- - datelastseen >- label: Datelastseen >- dissertation-information: >- bib1_mapping: >- biblioserver: >- 1: 1056 >- enabled: 1 >- index: dissertation-information >- aliases: >- - dissertation-information >- label: Dissertation-information >- dt-bks: >- bib1_mapping: >- biblioserver: >- 1: 8700 >- enabled: 1 >- index: dt-bks >- aliases: >- - dt-bks >- label: Dt-bks >- dt-cf: >- bib1_mapping: >- biblioserver: >- 1: 8700 >- enabled: 1 >- index: dt-cf >- aliases: >- - dt-cf >- label: Dt-cf >- dt-map: >- bib1_mapping: >- biblioserver: >- 1: 8700 >- enabled: 1 >- index: dt-map >- aliases: >- - dt-map >- label: Dt-map >- dt-sr: >- bib1_mapping: >- biblioserver: >- 1: 8700 >- enabled: 1 >- index: dt-sr >- aliases: >- - dt-sr >- label: Dt-sr >- dt-vis: >- bib1_mapping: >- biblioserver: >- 1: 8700 >- enabled: 1 >- index: dt-vis >- aliases: >- - dt-vis >- label: Dt-vis >- ean: >- bib1_mapping: >- biblioserver: >- 1: 1214 >- enabled: 1 >- index: ean >- aliases: >- - ean >- label: Ean >- extent: >- bib1_mapping: >- biblioserver: >- 1: Extent >- enabled: 1 >- index: extent >- aliases: >- - extent >- label: Extent >- ff8-22: >- bib1_mapping: >- biblioserver: >- 1: 8822 >- enabled: 1 >- index: ff8-22 >- aliases: >- - ff8-22 >- label: Ff8-22 >- ff8-23: >- bib1_mapping: >- biblioserver: >- 1: 8823 >- enabled: 1 >- index: ff8-23 >- aliases: >- - ff8-23 >- label: Ff8-23 >- ff8-34: >- bib1_mapping: >- biblioserver: >- 1: 8834 >- enabled: 1 >- index: ff8-34 >- aliases: >- - ff8-34 >- label: Ff8-34 >- fiction: >- bib1_mapping: >- biblioserver: >- 1: 8833 >- enabled: 1 >- index: fiction >- aliases: >- - fiction >- - fic >- label: Fiction >- format: >- bib1_mapping: >- biblioserver: >- 1: 8823 >- enabled: 1 >- index: format >- aliases: >- - format >- label: Format >- graphics-support: >- bib1_mapping: >- biblioserver: >- 1: Graphic-support >- enabled: 1 >- index: graphics-support >- aliases: >- - graphics-support >- label: Graphics-support >- graphics-type: >- bib1_mapping: >- biblioserver: >- 1: Graphic-type >- enabled: 1 >- index: graphics-type >- aliases: >- - graphics-type >- label: Graphics-type >- holdingbranch: >- bib1_mapping: >- biblioserver: >- 1: 8012 >- enabled: 1 >- index: holdingbranch >- aliases: >- - holdingbranch >- label: Holdingbranch >- homebranch: >- bib1_mapping: >- biblioserver: >- 1: homebranch >- enabled: 1 >- index: homebranch >- aliases: >- - homebranch >- - branch >- label: Homebranch >- host-item: >- bib1_mapping: >- biblioserver: >- 1: 1033 >- enabled: 1 >- index: host-item >- aliases: >- - host-item >- label: Host-item >- host-item-number: >- bib1_mapping: >- biblioserver: >- 1: 8911 >- enabled: 1 >- index: host-item-number >- aliases: >- - host-item-number >- - hi >- label: Host-item-number >- id-other: >- bib1_mapping: >- biblioserver: >- 1: 9012 >- enabled: 1 >- index: id-other >- aliases: >- - id-other >- label: Id-other >- identifier-standard: >- bib1_mapping: >- biblioserver: >- 1: 1007 >- enabled: 1 >- index: identifier-standard >- aliases: >- - identifier-standard >- - ident >- label: Identifier-standard >- illustration-code: >- bib1_mapping: >- biblioserver: >- 1: "Illustration-code " >- enabled: 1 >- index: illustration-code >- aliases: >- - illustration-code >- label: Illustration-code >- isbn: >- bib1_mapping: >- biblioserver: >- 1: 7 >- enabled: 1 >- index: isbn >- aliases: >- - isbn >- - nb >- label: Isbn >- issn: >- bib1_mapping: >- biblioserver: >- 1: 8 >- enabled: 1 >- index: issn >- aliases: >- - issn >- - ns >- label: Issn >- issues: >- bib1_mapping: >- biblioserver: >- 1: 8019 >- enabled: 1 >- index: issues >- aliases: >- - issues >- label: Issues >- item: >- bib1_mapping: >- biblioserver: >- 1: 9520 >- enabled: 1 >- index: item >- aliases: >- - item >- label: Item >- itemnumber: >- bib1_mapping: >- biblioserver: >- 1: 8010 >- enabled: 1 >- index: itemnumber >- aliases: >- - itemnumber >- label: Itemnumber >- itemtype: >- bib1_mapping: >- biblioserver: >- 1: 9006 >- enabled: 1 >- index: itemtype >- aliases: >- - itemtype >- - mc-itemtype >- label: Itemtype >- itype: >- bib1_mapping: >- biblioserver: >- 1: 8031 >- enabled: 1 >- index: itype >- aliases: >- - itype >- - mc-itype >- label: Itype >- l-format: >- bib1_mapping: >- biblioserver: >- 1: 8703 >- enabled: 1 >- index: l-format >- aliases: >- - l-format >- label: L-format >- language: >- bib1_mapping: >- biblioserver: >- 1: 54 >- enabled: 1 >- index: language >- aliases: >- - language >- - ln >- label: Language >- language-original: >- bib1_mapping: >- biblioserver: >- 1: 1095 >- enabled: 1 >- index: language-original >- aliases: >- - language-original >- label: Language of original >- ln-audio: >- bib1_mapping: >- biblioserver: >- 1: 9906 >- enabled: 1 >- index: ln-audio >- aliases: >- - ln-audio >- label: Language code of text/sound track or separate title >- ln-subtitle: >- bib1_mapping: >- biblioserver: >- 1: 9907 >- enabled: 1 >- index: ln-subtitle >- aliases: >- - ln-subtitle >- label: Language of subtitles >- lc-card-number: >- bib1_mapping: >- biblioserver: >- 1: 9 >- enabled: 1 >- index: lc-card-number >- aliases: >- - lc-card-number >- - lc-card >- label: Lc-card-number >- literature-code: >- bib1_mapping: >- biblioserver: >- 1: Literature-Code >- enabled: 1 >- index: literature-code >- aliases: >- - literature-code >- label: Literature-code >- llength: >- bib1_mapping: >- biblioserver: >- 1: llength >- enabled: 1 >- index: llength >- aliases: >- - llength >- label: Llength >- local-classification: >- bib1_mapping: >- biblioserver: >- 1: 8022 >- enabled: 1 >- index: local-classification >- aliases: >- - local-classification >- - lcn >- - callnum >- label: Local-classification >- local-number: >- bib1_mapping: >- biblioserver: >- 1: 12 >- enabled: 1 >- index: local-number >- aliases: >- - local-number >- - sn >- label: Local-number >- location: >- bib1_mapping: >- biblioserver: >- 1: 8013 >- enabled: 1 >- index: location >- aliases: >- - location >- - mc-loc >- label: Location >- lost: >- bib1_mapping: >- biblioserver: >- 1: 8002 >- enabled: 1 >- index: lost >- aliases: >- - lost >- label: Lost >- match: >- bib1_mapping: >- authorityserver: >- 1: Match >- enabled: 1 >- index: match >- aliases: >- - match >- label: Match >- material-type: >- bib1_mapping: >- biblioserver: >- 1: Material-type >- enabled: 1 >- index: material-type >- aliases: >- - material-type >- label: Material-type >- materials-specified: >- bib1_mapping: >- biblioserver: >- 1: 8004 >- enabled: 1 >- lc-card-number: >- bib1_mapping: >- biblioserver: >- 1: 9 >- enabled: 1 >- index: lc-card-number >- aliases: >- - lc-card-number >- - lc-card >- label: Lc-card-number >- lex: >- bib1_mapping: >- biblioserver: >- 1: 9903 >- enabled: 1 >- index: lex >- aliases: >- - lex >- label: Lexile >- literature-code: >- bib1_mapping: >- biblioserver: >- 1: Literature-Code >- enabled: 1 >- index: literature-code >- aliases: >- - literature-code >- label: Literature-code >- llength: >- bib1_mapping: >- biblioserver: >- 1: llength >- enabled: 1 >- index: llength >- aliases: >- - llength >- label: Llength >- local-classification: >- bib1_mapping: >- biblioserver: >- 1: 8022 >- enabled: 1 >- index: local-classification >- aliases: >- - local-classification >- - lcn >- - callnum >- label: Local-classification >- local-number: >- bib1_mapping: >- biblioserver: >- 1: 12 >- enabled: 1 >- index: local-number >- aliases: >- - local-number >- - sn >- label: Local-number >- location: >- bib1_mapping: >- biblioserver: >- 1: 8013 >- enabled: 1 >- index: location >- aliases: >- - location >- - mc-loc >- label: Location >- lost: >- bib1_mapping: >- biblioserver: >- 1: 8002 >- enabled: 1 >- index: lost >- aliases: >- - lost >- label: Lost >- match: >- bib1_mapping: >- authorityserver: >- 1: Match >- enabled: 1 >- index: match >- aliases: >- - match >- label: Match >- material-type: >- bib1_mapping: >- biblioserver: >- 1: Material-type >- enabled: 1 >- index: material-type >- aliases: >- - material-type >- label: Material-type >- materials-specified: >- bib1_mapping: >- biblioserver: >- 1: 8004 >- enabled: 1 >- index: materials-specified >- aliases: >- - materials-specified >- label: Materials-specified >- music: >- bib1_mapping: >- biblioserver: >- 1: Music-number >- enabled: 1 >- index: music >- aliases: >- - music >- label: Music >- name: >- bib1_mapping: >- biblioserver: >- 1: 1002 >- enabled: 1 >- index: name >- aliases: >- - name >- label: Name >- note: >- bib1_mapping: >- biblioserver: >- 1: 63 >- enabled: 1 >- index: note >- aliases: >- - note >- - nt >- label: Note >- notforloan: >- bib1_mapping: >- biblioserver: >- 1: 8008 >- 4: 109 >- enabled: 1 >- index: notforloan >- aliases: >- - notforloan >- label: Notforloan >- onloan: >- bib1_mapping: >- biblioserver: >- 1: 8024 >- enabled: 1 >- index: onloan >- aliases: >- - onloan >- label: Onloan >- personal-name: >- bib1_mapping: >- biblioserver: >- 1: Personal-name >- enabled: 1 >- index: personal-name >- aliases: >- - personal-name >- - pn >- label: Personal-name >- place-publication: >- bib1_mapping: >- biblioserver: >- 1: 59 >- enabled: 1 >- index: place-publication >- aliases: >- - place-publication >- - pl >- label: Place-publication >- popularity: >- bib1_mapping: >- biblioserver: >- 1: issues >- enabled: 1 >- index: popularity >- aliases: >- - popularity >- label: Popularity >- price: >- bib1_mapping: >- biblioserver: >- 1: 8017 >- enabled: 1 >- index: price >- aliases: >- - price >- label: Price >- publisher: >- bib1_mapping: >- biblioserver: >- 1: 1018 >- enabled: 1 >- index: publisher >- aliases: >- - publisher >- - pb >- label: Publisher >- record-control-number: >- bib1_mapping: >- biblioserver: >- 1: 1045 >- enabled: 1 >- index: record-control-number >- aliases: >- - record-control-number >- - rcn >- label: Record-control-number >- record-type: >- bib1_mapping: >- biblioserver: >- 1: 1001 >- enabled: 1 >- index: record-type >- aliases: >- - record-type >- - rtype >- - mc-rtype >- - mus >- label: Record-type >- regularity-code: >- bib1_mapping: >- biblioserver: >- 1: Regularity-code >- enabled: 1 >- index: regularity-code >- aliases: >- - regularity-code >- label: Regularity-code >- renewals: >- bib1_mapping: >- biblioserver: >- 1: 8020 >- enabled: 1 >- index: renewals >- aliases: >- - renewals >- label: Renewals >- replacementprice: >- bib1_mapping: >- biblioserver: >- 1: 8029 >- enabled: 1 >- index: replacementprice >- aliases: >- - replacementprice >- label: Replacementprice >- replacementpricedate: >- bib1_mapping: >- biblioserver: >- 1: 8030 >- enabled: 1 >- index: replacementpricedate >- aliases: >- - replacementpricedate >- label: Replacementpricedate >- reserves: >- bib1_mapping: >- biblioserver: >- 1: 8021 >- enabled: 1 >- index: reserves >- aliases: >- - reserves >- label: Reserves >- restricted: >- bib1_mapping: >- biblioserver: >- 1: 8006 >- enabled: 1 >- index: restricted >- aliases: >- - restricted >- label: Restricted >- stack: >- bib1_mapping: >- biblioserver: >- 1: 8018 >- enabled: 1 >- index: stack >- aliases: >- - stack >- label: Stack >- stock-number: >- bib1_mapping: >- biblioserver: >- 1: 1028 >- enabled: 1 >- index: stock-number >- aliases: >- - stock-number >- label: Stock-number >- number-local-acquisition: >- bib1_mapping: >- biblioserver: >- 1: 1062 >- enabled: 1 >- index: number-local-acquisition >- aliases: >- - number-local-acquisition >- - stocknumber >- - inv >- label: Number-local-acquisition >- summary: >- bib1_mapping: >- biblioserver: >- 1: Summary >- enabled: 1 >- index: summary >- aliases: >- - summary >- label: Summary >- suppress: >- bib1_mapping: >- biblioserver: >- 1: 9011 >- enabled: 1 >- index: suppress >- aliases: >- - suppress >- label: Suppress >- titleext: >- bib1_mapping: >- biblioserver: >- 1: 4 >- 4: 1 >- 6: 2 >- enabled: 1 >- index: title >- aliases: [] >- label: Title ext >- titlekw: >- bib1_mapping: >- biblioserver: >- 1: 4 >- enabled: 1 >- index: title >- aliases: [] >- label: Title kw >- thesaurus: >- bib1_mapping: >- authorityserver: >- 1: Subject-heading-thesaurus >- enabled: 1 >- index: thesaurus >- aliases: >- - thesaurus >- label: Thesaurus >- totalissues: >- bib1_mapping: >- biblioserver: >- 1: 9003 >- enabled: 1 >- index: totalissues >- aliases: >- - totalissues >- label: Totalissues >- type-of-serial: >- bib1_mapping: >- biblioserver: >- 1: Type-Of-Serial >- enabled: 1 >- index: type-of-serial >- aliases: >- - type-of-serial >- label: Type-of-serial >- upc: >- bib1_mapping: >- biblioserver: >- 1: UPC >- enabled: 1 >- index: upc >- aliases: >- - upc >- label: Upc >- uri: >- bib1_mapping: >- biblioserver: >- 1: 8028 >- enabled: 1 >- index: uri >- aliases: >- - uri >- label: Uri >- video-mt: >- bib1_mapping: >- biblioserver: >- 1: Video-mt >- enabled: 1 >- index: video-mt >- aliases: >- - video-mt >- label: Video-mt >- withdrawn: >- bib1_mapping: >- biblioserver: >- 1: 8001 >- enabled: 1 >- index: withdrawn >- aliases: >- - withdrawn >- label: Withdrawn >- subject: >- subject: >- bib1_mapping: >- authorityserver: >- 1: Match-heading >- biblioserver: >- 1: 21 >- enabled: 1 >- index: '' >- label: '' >- aliases: >- - su >- - su-to >- - su-geo >- - su-ut >- authority-number: >- bib1_mapping: >- biblioserver: >- 1: Koha-Auth-Number >- 3: 1 >- 4: 1 >- 5: 100 >- enabled: 1 >- index: authority-number >- label: Authority-number >- aliases: >- - authority-number >- - an >- complete: >- bib1_mapping: >- biblioserver: >- 1: 21 >- 3: 1 >- 4: 1 >- 5: 100 >- 6: 3 >- enabled: 1 >- index: complete >- label: Complete >- aliases: >- - complete >- exact: >- bib1_mapping: >- authorityserver: >- 4: 1 >- 5: 100 >- 6: 3 >- enabled: 1 >- index: exact >- label: Exact >- aliases: >- - exact >- heading: >- bib1_mapping: >- authorityserver: >- 1: Heading >- enabled: 1 >- index: heading >- label: Heading >- aliases: >- - heading >- - mainentry >- - he >- headingmain: >- bib1_mapping: >- authorityserver: >- 1: Heading-Main >- enabled: 1 >- index: headingmain >- label: Headingmain >- aliases: >- - headingmain >- - mainmainentry >- matchheading: >- bib1_mapping: >- authorityserver: >- 1: Match-heading >- 4: 1 >- 5: 100 >- 6: 3 >- enabled: 1 >- index: matchheading >- label: Matchheading >- aliases: >- - matchheading >- - match-heading >- name-personal: >- bib1_mapping: >- biblioserver: >- 1: 1009 >- enabled: 1 >- index: name-personal >- label: Name-personal >- aliases: >- - name-personal >- - su-na >- index-term-genre: >- bib1_mapping: >- biblioserver: >- 1: 9655 >- enabled: 1 >- index: index-term-genre >- label: Genre term >- aliases: >- - index-term-genre >- - itg >- index-term-uncontrolled: >- bib1_mapping: >- biblioserver: >- 1: 9653 >- enabled: 1 >- index: index-term-uncontrolled >- label: Uncontrolled index term >- aliases: >- - index-term-uncontrolled >- - itu >- seefrom: >- bib1_mapping: >- authorityserver: >- 1: Match-heading-see-from >- enabled: 1 >- index: seefrom >- label: Seefrom >- aliases: >- - seefrom >- - see-from >- start: >- bib1_mapping: >- authorityserver: >- 3: 2 >- 4: 1 >- 5: 1 >- enabled: 1 >- index: start >- label: Start >- aliases: >- - start >- title: >- title: >- bib1_mapping: >- biblioserver: >- 1: 4 >- enabled: 1 >- index: '' >- label: '' >- aliases: >- - ti >- cover: >- bib1_mapping: >- biblioserver: >- 1: 36 >- enabled: 1 >- index: cover >- label: Cover >- aliases: >- - cover >- - title-cover >- exacttitle: >- bib1_mapping: >- biblioserver: >- 1: 4 >- 4: 1 >- 6: 3 >- enabled: 1 >- index: exacttitle >- label: Exacttitle >- aliases: >- - exacttitle >- - ti,ext >- series: >- bib1_mapping: >- biblioserver: >- 1: 5 >- enabled: 1 >- index: series >- label: Series >- aliases: >- - series >- - title-series >- - se >- uniform: >- bib1_mapping: >- biblioserver: >- 1: Title-uniform >- enabled: 1 >- index: uniform >- label: Uniform >- aliases: >- - uniform >- - title-uniform >-filter_mappings: >- acqdate: >- bib1_mapping: >- biblioserver: >- 1: 32 >- 4: 4 >- target_syntax_callback: date_filter_target_callback >- enabled: 1 >- label: Acqdate >- copydate: >- bib1_mapping: >- biblioserver: >- 1: 30 >- 4: 4 >- target_syntax_callback: date_filter_target_callback >- >- enabled: 1 >- label: Copydate >- pubdate: >- bib1_mapping: >- biblioserver: >- 1: 31 >- 4: 4 >- target_syntax_callback: date_filter_target_callback >- enabled: 1 >- label: Pubdate >-modifier_mappings: >- AuthidAsc: >- bib1_mapping: >- authorityserver: >- "": 0 >- 1: Local-Number >- 7: 1 >- op: "@or" >- enabled: 1 >- label: AuthidAsc >- AuthidDsc: >- bib1_mapping: >- authorityserver: >- "": 0 >- 1: Local-Number >- 7: 2 >- op: "@or" >- enabled: 1 >- label: AuthidDsc >- HeadingAsc: >- bib1_mapping: >- authorityserver: >- "": 0 >- 1: Heading >- 7: 1 >- op: "@or" >- enabled: 1 >- label: HeadingAsc >- HeadingDsc: >- bib1_mapping: >- authorityserver: >- "": 0 >- 1: Heading >- 7: 2 >- op: "@or" >- enabled: 1 >- label: HeadingDsc >- Relevance: >- bib1_mapping: >- authorityserver: >- 2: 102 >- enabled: 1 >- label: Relevance >- acqdate_asc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 32 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Acqdate_asc >- acqdate_dsc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 32 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Acqdate_dsc >- ascending: >- bib1_mapping: >- biblioserver: >- 7: 1 >- enabled: 1 >- label: Ascending >- author_asc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 1003 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Author_asc >- author_az: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 1003 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Author_az >- author_dsc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 1003 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Author_dsc >- author_za: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 1003 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Author_za >- call_number_asc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 8007 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Call_number_asc >- call_number_dsc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 8007 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Call_number_dsc >- descending: >- bib1_mapping: >- biblioserver: >- 7: 2 >- enabled: 1 >- label: Descending >- popularity_asc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 9003 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Popularity_asc >- popularity_dsc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 9003 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Popularity_dsc >- pubdate_asc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 31 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Pubdate_asc >- pubdate_dsc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 31 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Pubdate_dsc >- relevance: >- bib1_mapping: >- biblioserver: >- 2: 102 >- enabled: 1 >- label: Relevance >- relevance_dsc: >- bib1_mapping: >- biblioserver: >- 2: 102 >- enabled: 1 >- label: Relevance_dsc >- relevance_asc: >- bib1_mapping: >- biblioserver: >- 2: 102 >- enabled: 1 >- label: Relevance_asc >- title-sort-az: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 36 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Title-sort-az >- title-sort-za: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 36 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Title-sort-za >- title_asc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 4 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Title_asc >- title_az: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 4 >- 7: 1 >- op: "@or" >- enabled: 1 >- label: Title_az >- title_dsc: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 4 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Title_dsc >- title_za: >- bib1_mapping: >- biblioserver: >- "": 0 >- 1: 4 >- 7: 2 >- op: "@or" >- enabled: 1 >- label: Title_za >-relevance_bumps: >- keyword: >- titleext: >- enabled: 1 >- bib1_mapping: >- biblioserver: 34 >- titlekw: >- enabled: 1 >- bib1_mapping: >- biblioserver: 20 >diff --git a/installer/data/mysql/atomicupdate/bug_xxxxx.perl b/installer/data/mysql/atomicupdate/bug_xxxxx.perl >new file mode 100644 >index 0000000000..8048704dfb >--- /dev/null >+++ b/installer/data/mysql/atomicupdate/bug_xxxxx.perl >@@ -0,0 +1,9 @@ >+$DBversion = 'XXX'; # will be replaced by the RM >+if( CheckVersion( $DBversion ) ) { >+ $dbh->do( q| >+ DELETE FROM systempreferences WHERE variable="UseQueryParser" >+ |); >+ >+ SetVersion( $DBversion ); >+ print "Upgrade to $DBversion done (Bug XXXXX - Remove UseQueryParser system preference)\n"; >+} >diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql >index b565b7f0bf..40f7a4740f 100644 >--- a/installer/data/mysql/sysprefs.sql >+++ b/installer/data/mysql/sysprefs.sql >@@ -673,7 +673,6 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` > ('UseEmailReceipts','0','','Send email receipts for payments and write-offs','YesNo'), > ('UseICU','0','1','Tell Koha if ICU indexing is in use for Zebra or not.','YesNo'), > ('UseKohaPlugins','0','','Enable or disable the ability to use Koha Plugins.','YesNo'), >-('UseQueryParser','0',NULL,'If enabled, try to use QueryParser for queries.','YesNo'), > ('UseTransportCostMatrix','0','','Use Transport Cost Matrix when filling holds','YesNo'), > ('UseWYSIWYGinSystemPreferences','0','','Show WYSIWYG editor when editing certain HTML system preferences.','YesNo'), > ('viewISBD','1','','Allow display of ISBD view of bibiographic records','YesNo'), >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt >index 61927acbbc..5885241ffe 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt >@@ -184,7 +184,7 @@ > </div> > > <div id="sysinfo"> >- [% IF warnPrefBiblioAddsAuthorities || warnPrefEasyAnalyticalRecords || warnPrefAnonymousPatronOPACPrivacy || warnPrefAnonymousPatronAnonSuggestions || warnPrefAnonymousPatronOPACPrivacy_PatronDoesNotExist || warnPrefAnonymousPatronAnonSuggestions_PatronDoesNotExist || warnNoActiveCurrency || QueryParserError || warnIsRootUser || xml_config_warnings.size || AutoSelfCheckPatronDoesNotHaveSelfCheckPerm || AutoSelfCheckPatronHasTooManyPerm || warnStatisticsFieldsError || warnNoTemplateCaching || warnILLConfiguration || has_ai_issues || oauth2_missing_deps || bad_yaml_prefs || warnRelationships %] >+ [% IF warnPrefBiblioAddsAuthorities || warnPrefEasyAnalyticalRecords || warnPrefAnonymousPatronOPACPrivacy || warnPrefAnonymousPatronAnonSuggestions || warnPrefAnonymousPatronOPACPrivacy_PatronDoesNotExist || warnPrefAnonymousPatronAnonSuggestions_PatronDoesNotExist || warnNoActiveCurrency || warnIsRootUser || xml_config_warnings.size || AutoSelfCheckPatronDoesNotHaveSelfCheckPerm || AutoSelfCheckPatronHasTooManyPerm || warnStatisticsFieldsError || warnNoTemplateCaching || warnILLConfiguration || has_ai_issues || oauth2_missing_deps || bad_yaml_prefs || warnRelationships %] > [% IF (warnIsRootUser) %] > <h2>Warning regarding current user</h2> > <p>You are logged in as the database administrative user. This is not recommended because some parts of Koha will not function as expected when using this account.</p> >@@ -246,7 +246,7 @@ > [% END %] > [% END %] > >- [% IF warnPrefBiblioAddsAuthorities || warnPrefEasyAnalyticalRecords || warnPrefAnonymousPatronOPACPrivacy || warnPrefAnonymousPatronAnonSuggestions || warnPrefAnonymousPatronOPACPrivacy_PatronDoesNotExist || warnPrefAnonymousPatronAnonSuggestions_PatronDoesNotExist || warnNoActiveCurrency || QueryParserError || AutoSelfCheckPatronDoesNotHaveSelfCheckPerm || AutoSelfCheckPatronHasTooManyPerm || warnStatisticsFieldsError || warnNoTemplateCaching || warnILLConfiguration || oauth2_missing_deps || bad_yaml_prefs || warnIssuingRules %] >+ [% IF warnPrefBiblioAddsAuthorities || warnPrefEasyAnalyticalRecords || warnPrefAnonymousPatronOPACPrivacy || warnPrefAnonymousPatronAnonSuggestions || warnPrefAnonymousPatronOPACPrivacy_PatronDoesNotExist || warnPrefAnonymousPatronAnonSuggestions_PatronDoesNotExist || warnNoActiveCurrency || AutoSelfCheckPatronDoesNotHaveSelfCheckPerm || AutoSelfCheckPatronHasTooManyPerm || warnStatisticsFieldsError || warnNoTemplateCaching || warnILLConfiguration || oauth2_missing_deps || bad_yaml_prefs || warnIssuingRules %] > <h2>Warnings regarding the system configuration</h2> > <table> > <caption>Preferences and parameters</caption> >@@ -274,18 +274,6 @@ > [% IF warnStatisticsFieldsError %] > <tr><th scope="row"><b>Warning</b> </th><td>System preference 'StatisticsFields' contains field names not belonging to the items database table: [% warnStatisticsFieldsError | html %] </td></tr> > [% END %] >- [% IF QueryParserError %] >- <tr><th scope="row"><b>Warning</b> </th><td> >- You have set UseQueryParser but there was a problem inititializing QueryParser. >- [% IF QueryParserError.fallback %] >- The 'queryparser_config' entry is missing in your configuration file. >- The following configuration file was used without success: [% QueryParserError.file | html %]. >- [% ELSE %] >- The following configuration file was used without success: [% QueryParserError.file | html %]. >- [% END %] >- </td> >- </tr> >- [% END %] > [% IF AutoSelfCheckPatronDoesNotHaveSelfCheckPerm %] > <tr><th scope="row"><b>Warning</b> </th><td> > The patron used for the self checkout module at the OPAC does not have the self_check => self_checkout_module permission. >@@ -357,13 +345,6 @@ > <a href="https://wiki.koha-community.org/wiki/Switching_to_dom_indexing">Switching to dom indexing</a> > </td> > </tr> >- [% ELSIF config_entry.error == 'queryparser_entry_missing' %] >- <tr> >- <th scope="row"><b>Warning</b></th> >- <td>You have set UseQueryParser but the 'queryparser_config' entry is missing in your configuration >- file. [% config_entry.file | html %] is used as a fallback. >- </td> >- </tr> > [% ELSIF config_entry.error == 'use_zebra_facets_entry_missing' %] > <tr> > <th scope="row"><b>Warning</b></th> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref >index 83c6ca40b2..20587fa310 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref >@@ -258,7 +258,7 @@ Cataloging: > choices: > yes: "do" > no: "don't" >- - attempt to match aggressively by trying all variations of the ISBNs in the imported record as a phrase in the ISBN fields of already cataloged records. Note that this preference has no effect if UseQueryParser is on. >+ - attempt to match aggressively by trying all variations of the ISBNs in the imported record as a phrase in the ISBN fields of already cataloged records. > - > - Display the MARC field/subfields > - pref: AdditionalFieldsInZ3950ResultSearch >@@ -269,7 +269,7 @@ Cataloging: > choices: > yes: "do" > no: "don't" >- - attempt to match aggressively by trying all variations of the ISSNs in the imported record as a phrase in the ISSN fields of already cataloged records. Note that this preference has no effect if UseQueryParser is on. >+ - attempt to match aggressively by trying all variations of the ISSNs in the imported record as a phrase in the ISSN fields of already cataloged records. > > Exporting: > - >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref >index 497447f5aa..9f2711ab27 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref >@@ -66,13 +66,6 @@ Searching: > no: "Not using" > - 'ICU Zebra indexing. Please note: This setting will not affect Zebra indexing, it should only be used to tell Koha that you have activated ICU indexing if you have actually done so, since there is no way for Koha to figure this out on its own.' > - >- - pref: UseQueryParser >- default: 1 >- choices: >- yes: Try >- no: "Do not try" >- - 'to use the QueryParser module for parsing queries. Please note: enabling this will have no impact if you do not have QueryParser installed, and everything will continue to work as usual.' >- - > - pref: IncludeSeeFromInSearches > default: 0 > choices: >@@ -250,7 +243,7 @@ Searching: > choices: > yes: "search" > no: "don't search" >- - on all variations of the ISBN. Note that this preference has no effect if UseQueryParser is on. >+ - on all variations of the ISBN. > Did you Mean/Spell checking: > - > - "Swedish service for spellchecking.<br/>" >diff --git a/labels/label-item-search.pl b/labels/label-item-search.pl >index 5b202245de..fdade75a4e 100755 >--- a/labels/label-item-search.pl >+++ b/labels/label-item-search.pl >@@ -67,8 +67,6 @@ 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 ) { >@@ -82,14 +80,8 @@ if ( $op eq "do_search" ) { > $datefrom = eval { dt_from_string ( $datefrom ) }; > if ($datefrom) { > $datefrom = output_pref( { dt => $datefrom, dateonly => 1, dateformat => 'iso' } ); >- if ($QParser) { >- $ccl_query .= ' && ' if $ccl_textbox; >- $ccl_query .= >- "acqdate(" . $datefrom . '-)'; >- } else { >- $ccl_query .= ' and ' if $ccl_textbox; >- $ccl_query .= "acqdate,ge,st-date-normalized=" . $datefrom; >- } >+ $ccl_query .= ' and ' if $ccl_textbox; >+ $ccl_query .= "acqdate,ge,st-date-normalized=" . $datefrom; > } > } > >@@ -97,13 +89,8 @@ if ( $op eq "do_search" ) { > $dateto = eval { dt_from_string ( $dateto ) }; > if ($dateto) { > $dateto = output_pref( { dt => $dateto, dateonly => 1, dateformat => 'iso' } ); >- if ($QParser) { >- $ccl_query .= ' && ' if ( $ccl_textbox || $datefrom ); >- $ccl_query .= "acqdate(-" . $dateto . ')'; >- } else { >- $ccl_query .= ' and ' if ( $ccl_textbox || $datefrom ); >- $ccl_query .= "acqdate,le,st-date-normalized=" . $dateto; >- } >+ $ccl_query .= ' and ' if ( $ccl_textbox || $datefrom ); >+ $ccl_query .= "acqdate,le,st-date-normalized=" . $dateto; > } > } > >diff --git a/misc/migration_tools/bulkmarcimport.pl b/misc/migration_tools/bulkmarcimport.pl >index 5bfb04c1a2..6a08c6cd56 100755 >--- a/misc/migration_tools/bulkmarcimport.pl >+++ b/misc/migration_tools/bulkmarcimport.pl >@@ -598,14 +598,7 @@ sub build_query { > my $string = build_simplequery($matchingpoint,$record); > push @searchstrings,$string if (length($string)>0); > } >- my $QParser; >- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); >- my $op; >- if ($QParser) { >- $op = '&&'; >- } else { >- $op = 'and'; >- } >+ my $op = 'and'; > return join(" $op ",@searchstrings); > } > sub build_simplequery { >@@ -621,14 +614,7 @@ sub build_simplequery { > } > } > } >- my $QParser; >- $QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser')); >- my $op; >- if ($QParser) { >- $op = '&&'; >- } else { >- $op = 'and'; >- } >+ my $op = 'and'; > return join(" $op ",@searchstrings); > } > sub report_item_errors { >diff --git a/serials/subscription-bib-search.pl b/serials/subscription-bib-search.pl >index c6ff67d428..743fe512f6 100755 >--- a/serials/subscription-bib-search.pl >+++ b/serials/subscription-bib-search.pl >@@ -92,7 +92,7 @@ if ( $op eq "do_search" && $query ) { > # add the limits if applicable > my $itemtypelimit = $input->param('itemtypelimit'); > my $ccodelimit = $input->param('ccodelimit'); >- my $op = C4::Context->preference('UseQueryParser') ? '&&' : 'and'; >+ my $op = 'and'; > $query .= " $op $itype_or_itemtype:$itemtypelimit" if $itemtypelimit; > $query .= " $op ccode:$ccodelimit" if $ccodelimit; > $debug && warn $query; >diff --git a/t/QueryParser.t b/t/QueryParser.t >deleted file mode 100644 >index 7b694300bd..0000000000 >--- a/t/QueryParser.t >+++ /dev/null >@@ -1,136 +0,0 @@ >-#!/usr/bin/perl >- >-use strict; >-use warnings; >- >-use Test::More; >-use Test::Deep; >-use Module::Load::Conditional qw(can_load); >- >-BEGIN { >- use_ok( 'Koha::QueryParser::Driver::PQF' ); >-} >- >-my $QParser = Koha::QueryParser::Driver::PQF->new(); >- >-ok(defined $QParser, 'Successfully created empty QP object'); >-ok($QParser->load_config('./etc/searchengine/queryparser.yaml'), 'Loaded QP config'); >- >-is($QParser->search_class_count, 4, 'Initialized 4 search classes'); >-is (scalar(@{$QParser->search_fields()->{'keyword'}}), 111, "Correct number of search fields for 'keyword' class"); >- >-# Set keyword search as the default >-$QParser->default_search_class('keyword'); >- >-my $kwd_search = q/@attr 1=1016 @attr 4=6/; >-my $weight1 = q/@attr 2=102 @attr 9=20 @attr 4=6/; >-my $weight2 = q/@attr 2=102 @attr 9=34 @attr 4=6/; >- >-like( $QParser->target_syntax('biblioserver', 'smith'), >- qr/\@or \@or $kwd_search "smith" ($weight1 "smith" $weight2 "smith"|$weight2 "smith" $weight1 "smith")/, >- 'super simple keyword query'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith'), >- '@attr 1=1003 @attr 4=6 "smith"', 'simple author query'); >- >-is($QParser->target_syntax('biblioserver', 'keyword|publisher:smith'), >- '@attr 1=1018 @attr 4=6 "smith"', 'fielded publisher query'); >- >-is($QParser->target_syntax('biblioserver', 'ti:"little engine that could"'), >- '@attr 1=4 @attr 4=1 "little engine that could"', 'phrase query'); >- >-is($QParser->target_syntax('biblioserver', 'keyword|titlekw:smith'), >- '@attr 1=4 @attr 2=102 @attr 9=20 @attr 4=6 "smith"', >- 'relevance-bumped query'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith && johnson'), >- '@and @attr 1=1003 @attr 4=6 "smith" @attr 1=1003 @attr 4=6 "johnson"', >- 'query with boolean &&'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith && ti:johnson'), >- '@and @attr 1=1003 @attr 4=6 "smith" @attr 1=4 @attr 4=6 "johnson"', 'query with boolean &&'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith pubdate(-2008)'), >- '@and @attr 1=1003 @attr 4=6 "smith" @attr 1=31 @attr 4=4 @attr 2=2 "2008"', >- 'keyword search with pubdate limited to -2008'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith pubdate(2008-)'), >- '@and @attr 1=1003 @attr 4=6 "smith" @attr 1=31 @attr 4=4 @attr 2=4 "2008"', >- 'keyword search with pubdate limited to 2008-'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith pubdate(2008)'), >- '@and @attr 1=1003 @attr 4=6 "smith" @attr 1=31 @attr 4=4 "2008"', >- 'keyword search with pubdate limited to 2008'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith pubdate(1980,2008)'), >- '@and @attr 1=1003 @attr 4=6 "smith" @or @attr 1=31 @attr 4=4 "1980" @attr 1=31 @attr 4=4 "2008"', >- 'keyword search with pubdate limited to 1980, 2008'); >- >-is($QParser->target_syntax('biblioserver', 'au:smith #acqdate_dsc'), >- '@or @attr 1=32 @attr 7=1 0 @attr 1=1003 @attr 4=6 "smith"', >- 'keyword search sorted by acqdate descending'); >- >-is($QParser->bib1_mapping_by_attr('field', 'biblioserver', {'1' => '1004'})->{'field'}, >- 'personal', 'retrieve field by attr'); >- >-is($QParser->bib1_mapping_by_attr_string('field', 'biblioserver', '@attr 1=1004')->{'field'}, >- 'personal', 'retrieve field by attrstring'); >- >-is ($QParser->clear_all_mappings, $QParser, 'clear all mappings returns self'); >-is ($QParser->clear_all_configuration, $QParser, 'clear all configuration returns self'); >-is (scalar(keys(%{$QParser->search_fields})), 0, "All mapping erased"); >- >-$QParser->add_bib1_field_map('author' => 'personal' => 'biblioserver' => { '1' => '1004' } ); >-$QParser->add_bib1_modifier_map('relevance' => 'biblioserver' => { '2' => '102' } ); >-my $desired_config = { >- 'field_mappings' => { >- 'author' => { >- 'personal' => { >- 'aliases' => [ ], >- 'bib1_mapping' => { >- 'biblioserver' => { >- '1' => '1004' >- } >- }, >- 'enabled' => '1', >- 'index' => 'personal', >- 'label' => 'Personal' >- } >- } >- }, >- 'filter_mappings' => {}, >- 'modifier_mappings' => { >- 'relevance' => { >- 'bib1_mapping' => { >- 'biblioserver' => { >- '2' => '102' >- } >- }, >- 'enabled' => 1, >- 'label' => 'Relevance' >- } >- }, >- 'relevance_bumps' => {} >-}; >- >-SKIP: { >- my $got_config; >- skip 'YAML is unavailable', 2 unless can_load('modules' => { 'YAML::Any' => undef }); >- $got_config = YAML::Any::Load($QParser->serialize_mappings()); >- ok(ref $got_config, 'serialized YAML valid'); >- is_deeply($got_config, $desired_config, 'Mappings serialized correctly to YAML'); >- >- skip 'JSON is unavailable', 2 unless can_load('modules' => { 'JSON' => undef }); >- undef $got_config; >- eval { >- $got_config = JSON::from_json($QParser->serialize_mappings('json')); >- }; >- is($@, '', 'serialized JSON valid'); >- is_deeply($got_config, $desired_config, 'Mappings serialized correctly to JSON'); >-} >- >-$QParser->clear_all_mappings; >-is($QParser->TEST_SETUP, $QParser, 'TEST_SETUP returns self'); >-is($QParser->search_class_count, 4, 'Initialized 4 search classes in test setup'); >- >-done_testing(); >diff --git a/t/db_dependent/QueryParser.t b/t/db_dependent/QueryParser.t >deleted file mode 100644 >index 58120d7dd2..0000000000 >--- a/t/db_dependent/QueryParser.t >+++ /dev/null >@@ -1,70 +0,0 @@ >-#!/usr/bin/perl >- >-# This file is part of Koha. >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use Modern::Perl; >- >-use Test::More tests => 7; >- >-use File::Basename; >-use t::lib::Mocks; >-use C4::Context; >- >-t::lib::Mocks::mock_preference( "UseQueryParser", 1 ); >-my $QParser = C4::Context->queryparser(); >- >-# Check initialization correctly parsed the config file >-ok( defined $QParser && ref($QParser) eq "Koha::QueryParser::Driver::PQF" >- , 'C4::Context successfully created a QP object' ); >- >-is( $QParser->search_class_count, 4, >- "Initialized 4 search classes" ); >-is( scalar(@{$QParser->search_fields()->{'keyword'}}), 111, >- "Correct number of search fields for 'keyword' class"); >-is( scalar(@{$QParser->search_fields()->{'author'}}), 5, >- "Correct number of search fields for 'author' class"); >-is( scalar(@{$QParser->search_fields()->{'subject'}}), 12, >- "Correct number of search fields for 'subject' class"); >-is( scalar(@{$QParser->search_fields()->{'title'}}), 5, >- "Correct number of search fields for 'title' class"); >- >-# Load C4::Context 4 times with different randomization seeds >-$ENV{ PERL_PERTURB_KEYS } = "1"; >-my $hash_seed = "AB123"; >-my $default_search_class1 = get_default_search_class(); >-$hash_seed = "CD456"; >-my $default_search_class2 = get_default_search_class(); >-$hash_seed = "ABCDE"; >-my $default_search_class3 = get_default_search_class(); >-$hash_seed = "123456"; >-my $default_search_class4 = get_default_search_class(); >- >-ok( $default_search_class1 eq 'keyword' && >- $default_search_class2 eq 'keyword' && >- $default_search_class3 eq 'keyword' && >- $default_search_class4 eq 'keyword', >- "C4::Context correctly sets the default search class to 'keyword' (Bug 12738)"); >- >-sub get_default_search_class { >- # get the default search class from a forked proccess >- # that just loads C4::Context >- $ENV{ PERL_HASH_SEED } = $hash_seed; >- my $running_dir = dirname(__FILE__); >- my $default_search_class = qx/$running_dir\/default_search_class.pl/; >- >- return $default_search_class; >-} >- >diff --git a/t/db_dependent/Search.t b/t/db_dependent/Search.t >index 60da046249..53f98c147d 100644 >--- a/t/db_dependent/Search.t >+++ b/t/db_dependent/Search.t >@@ -22,6 +22,7 @@ use utf8; > use YAML; > > use C4::Debug; >+use C4::AuthoritiesMarc qw( SearchAuthorities ); > use C4::XSLT; > require C4::Context; > >@@ -97,7 +98,6 @@ our $QueryStemming = 0; > our $QueryAutoTruncate = 0; > our $QueryWeightFields = 0; > our $QueryFuzzy = 0; >-our $UseQueryParser = 0; > our $SearchEngine = 'Zebra'; > our $marcflavour = 'MARC21'; > our $htdocs = File::Spec->rel2abs(dirname($0)); >@@ -118,8 +118,6 @@ $contextmodule->mock('preference', sub { > return $QueryWeightFields; > } elsif ($pref eq 'QueryFuzzy') { > return $QueryFuzzy; >- } elsif ($pref eq 'UseQueryParser') { >- return $UseQueryParser; > } elsif ($pref eq 'SearchEngine') { > return $SearchEngine; > } elsif ($pref eq 'maxRecordsForFacets') { >@@ -196,11 +194,6 @@ $contextmodule->mock('preference', sub { > return 0; > } > }); >-$contextmodule->mock('queryparser', sub { >- my $QParser = Koha::QueryParser::Driver::PQF->new(); >- $QParser->load_config("$datadir/etc/searchengine/queryparser.yaml"); >- return $QParser; >-}); > > our $bibliomodule = new Test::MockModule('C4::Biblio'); > >@@ -269,7 +262,6 @@ sub run_marc21_search_tests { > $QueryAutoTruncate = 0; > $QueryWeightFields = 0; > $QueryFuzzy = 0; >- $UseQueryParser = 0; > $marcflavour = 'MARC21'; > > my $indexes = C4::Search::getIndexes(); >@@ -485,19 +477,6 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' > is($results_hashref->{biblioserver}->{hits}, 12, > 'search using index whose name contains "ns" returns expected results (bug 10271)'); > >- $UseQueryParser = 1; >- ( $error, $query, $simple_query, $query_cgi, >- $query_desc, $limit, $limit_cgi, $limit_desc, >- $query_type ) = buildQuery([], [ 'book' ], [ 'kw' ], [], [], 0, 'en'); >- ($error, $results_hashref, $facets_loop) = getRecords($query,$simple_query,[ ], [ 'biblioserver' ],20,0,\%branches,\%itemtypes,$query_type,0); >- is($results_hashref->{biblioserver}->{hits}, 101, "Search for 'book' with index set to 'kw' returns 101 hits"); >- ( $error, $query, $simple_query, $query_cgi, >- $query_desc, $limit, $limit_cgi, $limit_desc, >- $query_type ) = buildQuery([ 'and' ], [ 'book', 'another' ], [ 'kw', 'kw' ], [], [], 0, 'en'); >- ($error, $results_hashref, $facets_loop) = getRecords($query,$simple_query,[ ], [ 'biblioserver' ],20,0,\%branches,\%itemtypes,$query_type,0); >- is($results_hashref->{biblioserver}->{hits}, 1, "Search for 'kw:book && kw:another' returns 1 hit"); >- $UseQueryParser = 0; >- > ( $error, $query, $simple_query, $query_cgi, > $query_desc, $limit, $limit_cgi, $limit_desc, > $query_type ) = buildQuery([], [ '' ], [ 'kw' ], [ 'available' ], [], 0, 'en'); >@@ -700,66 +679,6 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' > return '', [ $record->as_usmarc() ], 1; > }); > >- $UseQueryParser = 1; >- $term = 'Arizona'; >- ( $error, $query, $simple_query, $query_cgi, >- $query_desc, $limit, $limit_cgi, $limit_desc, >- $query_type ) = buildQuery([], [ $term ], [ 'su-br' ], [ ], [], 0, 'en'); >- matchesExplodedTerms("Advanced search for broader subjects", $query, 'Arizona', 'United States'); >- >- ( $error, $query, $simple_query, $query_cgi, >- $query_desc, $limit, $limit_cgi, $limit_desc, >- $query_type ) = buildQuery([], [ $term ], [ 'su-na' ], [ ], [], 0, 'en'); >- matchesExplodedTerms("Advanced 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, >- $query_type ) = buildQuery([], [ $term ], [ 'su-rl' ], [ ], [], 0, 'en'); >- matchesExplodedTerms("Advanced 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, >- $query_type ) = buildQuery([], [ "$term", 'history' ], [ 'su-rl', 'kw' ], [ ], [], 0, 'en'); >- matchesExplodedTerms("Advanced search for related subjects and keyword 'history' searches related subjects", $query, 'Arizona', 'United States', 'Maricopa County', 'Navajo County', 'Pima County'); >- like($query, qr/history/, "Advanced search for related subjects and keyword 'history' searches for 'history'"); >- >- ( $error, $query, $simple_query, $query_cgi, >- $query_desc, $limit, $limit_cgi, $limit_desc, >- $query_type ) = buildQuery([], [ 'history', "$term" ], [ 'kw', 'su-rl' ], [ ], [], 0, 'en'); >- matchesExplodedTerms("Order of terms doesn't matter for advanced search", $query, 'Arizona', 'United States', 'Maricopa County', 'Navajo County', 'Pima County'); >- like($query, qr/history/, "Order of terms doesn't matter for advanced search"); >- >- ( $error, $query, $simple_query, $query_cgi, >- $query_desc, $limit, $limit_cgi, $limit_desc, >- $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, >- $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, >- $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, >- $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 = '(' . join ('|', map { " \@attr 1=Subject \@attr 4=1 \"$_\"" } @terms) . "){" . scalar(@terms) . "}"; >- like($query, qr/$match/, $message); >- } >- >- # authority records >- use_ok('C4::AuthoritiesMarc'); >- $UseQueryParser = 0; >- > my ($auths, $count) = SearchAuthorities( > ['mainentry'], ['and'], [''], ['starts'], > ['shakespeare'], 0, 10, '', '', 1 >@@ -791,29 +710,6 @@ ok(MARC::Record::new_from_xml($results_hashref->{biblioserver}->{RECORDS}->[0],' > ); > is($count, 1, 'MARC21 authorities: one hit on "all" (entire record) contains "professional wrestler"'); > >- $UseQueryParser = 1; >- >- ($auths, $count) = SearchAuthorities( >- ['mainentry'], ['and'], [''], ['starts'], >- ['shakespeare'], 0, 10, '', '', 1 >- ); >- is($count, 1, 'MARC21 authorities: one hit on mainentry starts with "shakespeare" (QP)'); >- ($auths, $count) = SearchAuthorities( >- ['mainentry'], ['and'], [''], ['starts'], >- ['shakespeare'], 0, 10, '', 'HeadingAsc', 1 >- ); >- is($count, 1, 'MARC21 authorities: one hit on mainentry starts with "shakespeare" sorted by heading ascending (QP)'); >- ($auths, $count) = SearchAuthorities( >- ['mainentry'], ['and'], [''], ['starts'], >- ['shakespeare'], 0, 10, '', 'HeadingDsc', 1 >- ); >- is($count, 1, 'MARC21 authorities: one hit on mainentry starts with "shakespeare" sorted by heading descending (QP)'); >- ($auths, $count) = SearchAuthorities( >- ['match'], ['and'], [''], ['contains'], >- ['æ²å£«åäºå¨å»å§'], 0, 10, '', '', 1 >- ); >- is($count, 1, 'MARC21 authorities: one hit on match contains "æ²å£«åäºå¨å»å§" (QP)'); >- > # retrieve records that are larger than the MARC limit of 99,999 octets > ( undef, $results_hashref, $facets_loop ) = > getRecords('ti:marc the large record', '', [], [ 'biblioserver' ], '20', 0, \%branches, \%itemtypes, 'ccl', undef); >@@ -899,7 +795,6 @@ sub run_unimarc_search_tests { > $QueryAutoTruncate = 0; > $QueryWeightFields = 0; > $QueryFuzzy = 0; >- $UseQueryParser = 0; > $marcflavour = 'UNIMARC'; > > index_sample_records_and_launch_zebra($datadir, 'unimarc'); >@@ -917,7 +812,6 @@ sub run_unimarc_search_tests { > > # authority records > use_ok('C4::AuthoritiesMarc'); >- $UseQueryParser = 0; > > my ($auths, $count) = SearchAuthorities( > ['mainentry'], ['and'], [''], ['contains'], >@@ -971,7 +865,7 @@ sub run_unimarc_search_tests { > } > > subtest 'MARC21 + DOM' => sub { >- plan tests => 112; >+ plan tests => 84; > run_marc21_search_tests(); > }; > >diff --git a/t/db_dependent/UsageStats.t b/t/db_dependent/UsageStats.t >index 7c9e8be1de..6b8db7e641 100644 >--- a/t/db_dependent/UsageStats.t >+++ b/t/db_dependent/UsageStats.t >@@ -566,7 +566,6 @@ sub mocking_systempreferences_to_a_set_value { > TraceCompleteSubfields > TraceSubjectSubdivisions > UseICU >- UseQueryParser > defaultSortField > displayFacetCount > OPACdefaultSortField >diff --git a/t/db_dependent/default_search_class.pl b/t/db_dependent/default_search_class.pl >deleted file mode 100755 >index a95a4b60bb..0000000000 >--- a/t/db_dependent/default_search_class.pl >+++ /dev/null >@@ -1,37 +0,0 @@ >-#!/usr/bin/perl >- >-# This file is part of Koha. >-# >-# Koha is free software; you can redistribute it and/or modify it >-# under the terms of the GNU General Public License as published by >-# the Free Software Foundation; either version 3 of the License, or >-# (at your option) any later version. >-# >-# Koha is distributed in the hope that it will be useful, but >-# WITHOUT ANY WARRANTY; without even the implied warranty of >-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >-# GNU General Public License for more details. >-# >-# You should have received a copy of the GNU General Public License >-# along with Koha; if not, see <http://www.gnu.org/licenses>. >- >-use Modern::Perl; >- >-=head1 default_search_class.pl >- >-The only purpose of this script is to load C4::Context and print >-the default search class from the QueryParser object >- >-=cut >- >-use t::lib::Mocks; >-use C4::Context; >- >-t::lib::Mocks::mock_preference("UseQueryParser","1"); >- >-my $QParser = C4::Context->queryparser(); >-my $default_search_class = $QParser->default_search_class(); >- >-print $default_search_class; >- >-exit 0; >diff --git a/t/db_dependent/zebra_config.pl b/t/db_dependent/zebra_config.pl >index a8a2ccbeb4..8dde14cf32 100755 >--- a/t/db_dependent/zebra_config.pl >+++ b/t/db_dependent/zebra_config.pl >@@ -49,7 +49,6 @@ $ENV{'__DB_USER__'} = C4::Context->config('user') // 'kohaadmin'; > $ENV{'__DB_PASS__'} = C4::Context->config('pass') // 'katikoan'; > > my @files = ( "$source/etc/koha-conf.xml", >- "$source/etc/searchengine/queryparser.yaml", > ); > > find(sub { push @files, $File::Find::name if ( -f $File::Find::name ); }, "$source/etc/zebradb"); >-- >2.11.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 24735
:
99643
|
99647
|
99648
|
99649
| 99811 |
99812