Bug 22521: DBRev 18.12.00.055
[koha.git] / Koha / QueryParser / Driver / PQF.pm
1 package Koha::QueryParser::Driver::PQF;
2
3 # This file is part of Koha.
4 #
5 # Copyright 2012 C & P Bibliography Services
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use base qw(OpenILS::QueryParser Class::Accessor);
21
22 use strict;
23 use warnings;
24
25 use Module::Load::Conditional qw(can_load);
26 use Koha::QueryParser::Driver::PQF::Util;
27 use Koha::QueryParser::Driver::PQF::query_plan;
28 use Koha::QueryParser::Driver::PQF::query_plan::facet;
29 use Koha::QueryParser::Driver::PQF::query_plan::filter;
30 use Koha::QueryParser::Driver::PQF::query_plan::modifier;
31 use Koha::QueryParser::Driver::PQF::query_plan::node;
32 use Koha::QueryParser::Driver::PQF::query_plan::node::atom;
33 use Koha::QueryParser::Driver::PQF::query_plan::node::atom;
34
35
36 =head1 NAME
37
38 Koha::QueryParser::Driver::PQF - QueryParser driver for PQF
39
40 =head1 SYNOPSIS
41
42     use Koha::QueryParser::Driver::PQF;
43     my $QParser = Koha::QueryParser::Driver::PQF->new(%args);
44
45 =head1 DESCRIPTION
46
47 Main entrypoint into the QueryParser PQF driver. PQF is the Prefix Query
48 Language, the syntax used to serialize Z39.50 queries.
49
50 =head1 ACCESSORS
51
52 In order to simplify Bib-1 attribute mapping, this driver uses Class::Accessor
53 for accessing the following maps:
54
55 =over 4
56
57 =item B<bib1_field_map> - search class/field Bib-1 mappings
58
59 =item B<bib1_modifier_map> - search modifier mappings
60
61 =item B<bib1_filter_map> - search filter mappings
62
63 =item B<bib1_relevance_bump_map> - relevance bump mappings
64
65 =back
66
67 =cut
68
69 __PACKAGE__->mk_accessors(qw(bib1_field_map bib1_modifier_map bib1_filter_map bib1_relevance_bump_map));
70
71 =head1 FUNCTIONS
72
73 =cut
74
75 =head2 get
76
77 Overridden accessor method for Class::Accessor. (Do not call directly)
78
79 =cut
80
81 sub get {
82     my $self = shift;
83     return $self->_map(@_);
84 }
85
86 =head2 set
87
88 Overridden mutator method for Class::Accessor. (Do not call directly)
89
90 =cut
91
92 sub set {
93     my $self = shift;
94     return $self->_map(@_);
95 }
96
97 =head2 add_bib1_field_map
98
99     $QParser->add_bib1_field_map($class => $field => $server => \%attributes);
100
101     $QParser->add_bib1_field_map('author' => 'personal' => 'biblioserver' =>
102                                     { '1' => '1003' });
103
104 Adds a search field<->bib1 attribute mapping for the specified server. The
105 %attributes hash contains maps Bib-1 Attributes to the appropropriate
106 values. Not all attributes must be specified.
107
108 =cut
109
110 sub add_bib1_field_map {
111     my ($self, $class, $field, $server, $attributes) = @_;
112
113     $self->add_search_field( $class => $field );
114     return $self->_add_field_mapping($self->bib1_field_map, $class, $field, $server, $attributes);
115 }
116
117 =head2 add_bib1_modifier_map
118
119     $QParser->add_bib1_modifier_map($name => $server => \%attributes);
120
121     $QParser->add_bib1_modifier_map('ascending' => 'biblioserver' =>
122                                     { '7' => '1' });
123
124 Adds a search modifier<->bib1 attribute mapping for the specified server. The
125 %attributes hash contains maps Bib-1 Attributes to the appropropriate
126 values. Not all attributes must be specified.
127
128 =cut
129
130 sub add_bib1_modifier_map {
131     my ($self, $name, $server, $attributes) = @_;
132
133     $self->add_search_modifier( $name );
134
135     return $self->_add_mapping($self->bib1_modifier_map, $name, $server, $attributes);
136 }
137
138 =head2 add_bib1_filter_map
139
140     $QParser->add_bib1_filter_map($name => $server => \%attributes);
141
142     $QParser->add_bib1_filter_map('date' => 'biblioserver' =>
143                                     { 'callback' => &_my_callback });
144
145 Adds a search filter<->bib1 attribute mapping for the specified server. The
146 %attributes hash maps Bib-1 Attributes to the appropropriate values and
147 provides a callback for the filter. Not all attributes must be specified.
148
149 =cut
150
151 sub add_bib1_filter_map {
152     my ($self, $name, $server, $attributes) = @_;
153
154     $self->add_search_filter( $name, $attributes->{'callback'} );
155
156     return $self->_add_mapping($self->bib1_filter_map, $name, $server, $attributes);
157 }
158
159 =head2 add_relevance_bump
160
161     $QParser->add_relevance_bump($class, $field, $server, $multiplier, $active);
162     $QParser->add_relevance_bump('title' => 'exact' => 'biblioserver' => 34, 1);
163
164 Add a relevance bump to the specified field. When searching for a class without
165 any fields, all the relevance bumps for the specified class will be 'OR'ed
166 together.
167
168 =cut
169
170 sub add_relevance_bump {
171     my ($self, $class, $field, $server, $multiplier, $active) = @_;
172     my $attributes = { '9' => $multiplier, '2' => '102', 'active' => $active };
173
174     $self->add_search_field( $class => $field );
175     return $self->_add_field_mapping($self->bib1_relevance_bump_map, $class, $field, $server, $attributes);
176 }
177
178
179 =head2 target_syntax
180
181     my $pqf = $QParser->target_syntax($server, [$query]);
182     my $pqf = $QParser->target_syntax('biblioserver', 'author|personal:smith');
183     print $pqf; # assuming all the indexes are configured,
184                 # prints '@attr 1=1003 @attr 4=6 "smith"'
185
186 Transforms the current or specified query into a PQF query string for the
187 specified server.
188
189 =cut
190
191 sub target_syntax {
192     my ($self, $server, $query) = @_;
193     my $pqf = '';
194     $self->parse($query) if $query;
195     warn "QP query for $server: " . $self->query . "\n" if $self->debug;
196     $pqf = $self->parse_tree->target_syntax($server);
197     warn "PQF query: $pqf\n" if $self->debug;
198     $pqf =~ s/ +/ /g;
199     $pqf =~ s/^ //;
200     $pqf =~ s/ $//;
201     return $pqf;
202 }
203
204 =head2 date_filter_target_callback
205
206     $QParser->add_bib1_filter_map($server, { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => 'pubdate' });
207
208 Callback for date filters. Note that although the first argument is the QParser
209 object, this is technically not an object-oriented routine. This has no
210 real-world implications.
211
212 =cut
213
214 sub date_filter_target_callback {
215     my ($QParser, $filter, $params, $negate, $server) = @_;
216     my $attr_string = $QParser->bib1_mapping_by_name( 'filter', $filter, $server )->{'attr_string'};
217     my $pqf = '';
218     foreach my $datespec (@$params) {
219         my $datepqf = ' ';
220         if ($datespec) {
221             if ($datespec =~ m/(.*)-(.*)/) {
222                 if ($1) {
223                     $datepqf .= $attr_string . ' @attr 2=4 "' . $1 . '"';
224                 }
225                 if ($2) {
226                     $datepqf .= $attr_string . ' @attr 2=2 "' . $2 . '"';
227                     $datepqf = ' @and ' . $datepqf if $1;
228                 }
229             } else {
230                 $datepqf .= $attr_string . ' "' . $datespec . '"';
231             }
232         }
233         $pqf = ' @or ' . ($negate ? '@not @attr 1=_ALLRECORDS @attr 2=103 "" ' : '') . $pqf if $pqf;
234         $pqf .= $datepqf;
235     }
236     return $pqf;
237 }
238
239 =head2 _map
240
241     return $self->_map('bib1_field_map', $map);
242
243 Retrieves or sets a map.
244
245 =cut
246
247 sub _map {
248     my ($self, $name, $map) = @_;
249     $self->custom_data->{$name} ||= {};
250     $self->custom_data->{$name} = $map if ($map);
251     return $self->custom_data->{$name};
252 }
253
254 =head2 _add_mapping
255
256     return $self->_add_mapping($map, $name, $server, $attributes)
257
258 Adds a mapping. Note that this is not used for mappings relating to fields.
259
260 =cut
261
262 sub _add_mapping {
263     my ($self, $map, $name, $server, $attributes) = @_;
264
265     my $attr_string = Koha::QueryParser::Driver::PQF::Util::attributes_to_attr_string($attributes);
266     $attributes->{'attr_string'} = $attr_string;
267
268     $map->{'by_name'}{$name}{$server} = $attributes;
269     $map->{'by_attr'}{$server}{$attr_string} = { 'name' => $name, %$attributes };
270
271     return $map;
272 }
273
274 =head2 _add_field_mapping
275
276     return $self->_add_field_mapping($map, $class, $field, $server, $attributes)
277
278 Adds a mapping for field-related data.
279
280 =cut
281
282 sub _add_field_mapping {
283     my ($self, $map, $class, $field, $server, $attributes) = @_;
284     my $attr_string = Koha::QueryParser::Driver::PQF::Util::attributes_to_attr_string($attributes);
285     $attributes->{'attr_string'} = $attr_string;
286
287     $map->{'by_name'}{$class}{$field}{$server} = $attributes;
288     $map->{'by_attr'}{$server}{$attr_string} = { 'classname' => $class, 'field' => $field, %$attributes };
289     return $map;
290 }
291
292
293 =head2 bib1_mapping_by_name
294
295     my $attributes = $QParser->bib1_mapping_by_name($type, $name[, $subname], $server);
296     my $attributes = $QParser->bib1_mapping_by_name('field', 'author', 'personal', 'biblioserver');
297     my $attributes = $QParser->bib1_mapping_by_name('filter', 'pubdate', 'biblioserver');
298
299 Retrieve the Bib-1 attribute set associated with the specified mapping.
300 =cut
301
302 sub bib1_mapping_by_name {
303     my $server = pop;
304     my ($self, $type, $name, $field) = @_;
305
306     return unless ($server && $name);
307     return unless ($type eq 'field' || $type eq 'modifier' || $type eq 'filter' || $type eq 'relevance_bump');
308     if ($type eq 'field' || $type eq 'relevance_bump') {
309     # Unfortunately field is a special case thanks to the class->field hierarchy
310         return $self->_map('bib1_' . $type . '_map')->{'by_name'}{$name}{$field}{$server} if $field;
311         return $self->_map('bib1_' . $type . '_map')->{'by_name'}{$name};
312     } else {
313         return $self->_map('bib1_' . $type . '_map')->{'by_name'}{$name}{$server};
314     }
315 }
316
317 =head2 bib1_mapping_by_attr
318
319     my $field = $QParser->bib1_mapping_by_attr($type, $server, \%attr);
320     my $field = $QParser->bib1_mapping_by_attr('field', 'biblioserver', {'1' => '1004'});
321     print $field->{'classname'}; # prints "author"
322     print $field->{'field'}; # prints "personal"
323
324 Retrieve the search field/modifier/filter used for the specified Bib-1 attribute set.
325
326 =cut
327
328 sub bib1_mapping_by_attr {
329     my ($self, $type, $server, $attributes) = @_;
330     return unless ($server && $attributes);
331
332     my $attr_string = Koha::QueryParser::Driver::PQF::Util::attributes_to_attr_string($attributes);
333
334     return $self->bib1_mapping_by_attr_string($type, $server, $attr_string);
335 }
336
337 =head2 bib1_mapping_by_attr_string
338
339     my $field = $QParser->bib1_mapping_by_attr_string($type, $server, $attr_string);
340     my $field = $QParser->bib1_mapping_by_attr_string('field', 'biblioserver', '@attr 1=1004');
341     print $field->{'classname'}; # prints "author"
342     print $field->{'field'}; # prints "personal"
343
344 Retrieve the search field/modifier/filter used for the specified Bib-1 attribute string
345 (i.e. PQF snippet).
346
347 =cut
348
349 sub bib1_mapping_by_attr_string {
350     my ($self, $type, $server, $attr_string) = @_;
351     return unless ($server && $attr_string);
352     return unless ($type eq 'field' || $type eq 'modifier' || $type eq 'filter' || $type eq 'relevance_bump');
353
354     return $self->_map('bib1_' . $type . '_map')->{'by_attr'}{$server}{$attr_string};
355 }
356
357 =head2 clear_all_configuration
358
359     $QParser->clear_all_configuration
360
361 Clear all configuration. This is a highly destructive method. You may
362 not want to use it.
363
364 =cut
365
366 sub clear_all_configuration {
367     my ($self) = @_;
368     %OpenILS::QueryParser::parser_config = (
369         'OpenILS::QueryParser' => {
370             filters => [],
371             modifiers => [],
372             operators => {
373                 'and' => '&&',
374                 'or' => '||',
375                 float_start => '{{',
376                 float_end => '}}',
377                 group_start => '(',
378                 group_end => ')',
379                 required => '+',
380                 disallowed => '-',
381                 modifier => '#',
382                 negated => '!'
383             }
384         }
385     );
386     return $self;
387 }
388
389 =head2 clear_all_mappings
390
391     $QParser->clear_all_mappings
392
393 Clear all bib-1 mappings.
394
395 =cut
396
397 sub clear_all_mappings {
398     my ($self) = @_;
399
400     foreach my $name (qw(field modifier filter relevance_bump)) {
401         $self->custom_data->{'bib1_' . $name . '_map'} = { };
402     }
403     return $self;
404 }
405
406
407 =head2 _canonicalize_field_map
408
409 Convert a field map into its canonical form for serialization. Used only for
410 fields and relevance bumps.
411
412 =cut
413
414 sub _canonicalize_field_map {
415     my ( $map, $aliases ) = @_;
416     my $canonical_map = {};
417
418     foreach my $class ( keys %{ $map->{'by_name'} } ) {
419         $canonical_map->{$class} ||= {};
420         foreach my $field ( keys %{ $map->{'by_name'}->{$class} } ) {
421             my $field_map = {
422                 'index'   => $field,
423                 'label'   => ucfirst($field),
424                 'enabled' => '1',
425             };
426             foreach
427               my $server ( keys %{ $map->{'by_name'}->{$class}->{$field} } )
428             {
429                 $field_map->{'bib1_mapping'} ||= {};
430                 $field_map->{'bib1_mapping'}->{$server} =
431                   $map->{'by_name'}->{$class}->{$field}->{$server};
432                 delete $field_map->{'bib1_mapping'}->{$server}->{'attr_string'}
433                   if defined(
434                           $field_map->{'bib1_mapping'}->{$server}
435                             ->{'attr_string'}
436                   );
437             }
438             if ($aliases) {
439                 $field_map->{'aliases'} = [];
440                 foreach my $alias ( @{ $aliases->{$class}->{$field} } ) {
441                     push @{ $field_map->{'aliases'} },
442                       $alias;
443                 }
444             }
445             $canonical_map->{$class}->{$field} = $field_map;
446         }
447     }
448     return $canonical_map;
449 }
450
451 =head2 _canonicalize_map
452
453 Convert a map into its canonical form for serialization. Not used for fields.
454
455 =cut
456
457 sub _canonicalize_map {
458     my ($map) = @_;
459     my $canonical_map = {};
460
461     foreach my $name ( keys %{ $map->{'by_name'} } ) {
462         $canonical_map->{$name} = {
463             'label'        => ucfirst($name),
464             'enabled'      => 1,
465             'bib1_mapping' => {}
466         };
467         foreach my $server ( keys %{ $map->{'by_name'}->{$name} } ) {
468             $canonical_map->{$name}->{'bib1_mapping'}->{$server} =
469               $map->{'by_name'}->{$name}->{$server};
470             delete $canonical_map->{$name}->{'bib1_mapping'}->{$server}
471               ->{'attr_string'}
472               if defined(
473                       $canonical_map->{$name}->{'bib1_mapping'}->{$server}
474                         ->{'attr_string'}
475               );
476         }
477     }
478     return $canonical_map;
479 }
480
481 =head2 serialize_mappings
482
483     my $yaml = $QParser->serialize_mappings;
484     my $json = $QParser->serialize_mappings('json');
485
486 Serialize Bib-1 mappings to YAML or JSON.
487
488 =cut
489
490 sub serialize_mappings {
491     my ( $self, $format ) = @_;
492     $format ||= 'yaml';
493     my $config;
494
495     $config->{'field_mappings'} =
496       _canonicalize_field_map( $self->bib1_field_map,
497         $self->search_field_aliases );
498     $config->{'modifier_mappings'} =
499       _canonicalize_map( $self->bib1_modifier_map );
500     $config->{'filter_mappings'} = _canonicalize_map( $self->bib1_filter_map );
501     $config->{'relevance_bumps'} =
502       _canonicalize_field_map( $self->bib1_relevance_bump_map );
503
504     if ( $format eq 'json' && can_load( modules => { 'JSON' => undef } ) ) {
505         return JSON::to_json($config);
506     }
507     elsif ( can_load( modules => { 'YAML::Any' => undef } ) ) {
508         return YAML::Any::Dump($config);
509     }
510     return;
511 }
512
513 =head2 initialize
514
515     $QParser->initialize( { 'bib1_field_mappings' => \%bib1_field_mappings,
516                             'search_field_alias_mappings' => \%search_field_alias_mappings,
517                             'bib1_modifier_mappings' => \%bib1_modifier_mappings,
518                             'bib1_filter_mappings' => \%bib1_filter_mappings,
519                             'relevance_bumps' => \%relevance_bumps });
520
521 Initialize the QueryParser mapping tables based on the provided configuration.
522 This method was written to play nice with YAML configuration files loaded by load_config.
523
524 =cut
525
526 sub initialize {
527     my ( $self, $args ) = @_;
528
529     my $field_mappings    = $args->{'field_mappings'};
530     my $modifier_mappings = $args->{'modifier_mappings'};
531     my $filter_mappings   = $args->{'filter_mappings'};
532     my $relbumps          = $args->{'relevance_bumps'};
533     my ( $server, $bib1_mapping );
534     foreach my $class ( keys %$field_mappings ) {
535         foreach my $field ( keys %{ $field_mappings->{$class} } ) {
536             if ( $field_mappings->{$class}->{$field}->{'enabled'} ) {
537                 while ( ( $server, $bib1_mapping ) =
538                     each
539                     %{ $field_mappings->{$class}->{$field}->{'bib1_mapping'} } )
540                 {
541                     $self->add_bib1_field_map(
542                         $class => $field => $server => $bib1_mapping );
543                 }
544                 foreach my $alias (
545                     @{ $field_mappings->{$class}->{$field}->{'aliases'} } )
546                 {
547                     $self->add_search_field_alias( $class => $field => $alias );
548                 }
549             }
550         }
551     }
552     foreach my $modifier ( keys %$modifier_mappings ) {
553         if ( $modifier_mappings->{$modifier}->{'enabled'} ) {
554             while ( ( $server, $bib1_mapping ) =
555                 each %{ $modifier_mappings->{$modifier}->{'bib1_mapping'} } )
556             {
557                 $self->add_bib1_modifier_map(
558                     $modifier => $server => $bib1_mapping );
559             }
560         }
561     }
562     foreach my $filter ( keys %$filter_mappings ) {
563         if ( $filter_mappings->{$filter}->{'enabled'} ) {
564             while ( ( $server, $bib1_mapping ) =
565                 each %{ $filter_mappings->{$filter}->{'bib1_mapping'} } )
566             {
567                 if ( $bib1_mapping->{'target_syntax_callback'} eq
568                     'date_filter_target_callback' )
569                 {
570                     $bib1_mapping->{'target_syntax_callback'} =
571                       \&Koha::QueryParser::Driver::PQF::date_filter_target_callback;
572                 }
573                 $self->add_bib1_filter_map(
574                     $filter => $server => $bib1_mapping );
575             }
576         }
577     }
578     foreach my $class ( keys %$relbumps ) {
579         foreach my $field ( keys %{ $relbumps->{$class} } ) {
580             if ( $relbumps->{$class}->{$field}->{'enabled'} ) {
581                 while ( ( $server, $bib1_mapping ) =
582                     each %{ $relbumps->{$class}->{$field}->{'bib1_mapping'} } )
583                 {
584                     $self->add_relevance_bump(
585                         $class => $field => $server => $bib1_mapping,
586                         1
587                     );
588                 }
589             }
590         }
591     }
592     return $self;
593 }
594
595 =head2 load_config
596
597   $QParser->load_config($file_name);
598
599 Load a YAML file with a parser configuration. The YAML file should match the following format:
600
601     ---
602     field_mappings:
603       author:
604         "":
605           aliases:
606             - au
607           bib1_mapping:
608             biblioserver:
609               1: 1003
610           enabled: 1
611           index: ''
612           label: ''
613         conference:
614           aliases:
615             - conference
616             - cfn
617           bib1_mapping:
618             biblioserver:
619               1: 1006
620           enabled: 1
621           index: conference
622           label: Conference
623     filter_mappings:
624       acqdate:
625         bib1_mapping:
626           biblioserver:
627             1: Date-of-acquisition
628             4: 4
629             target_syntax_callback: date_filter_target_callback
630         enabled: 1
631         label: Acqdate
632     modifier_mappings:
633       AuthidAsc:
634         bib1_mapping:
635           authorityserver:
636             "": 0
637             1: Local-Number
638             7: 1
639             op: "@or"
640         enabled: 1
641         label: AuthidAsc
642     ...
643
644 =cut
645
646 sub load_config {
647     my ($self, $file) = @_;
648     require YAML::Any;
649     return unless ($file && -f $file);
650     my $config = YAML::Any::LoadFile($file);
651     return unless ($config);
652     $self->initialize($config);
653     return 1;
654 }
655
656 =head2 TEST_SETUP
657
658     $QParser->TEST_SETUP
659
660 This routine initializes the QueryParser driver with a reasonable set of
661 defaults. This is intended only for testing. Although such test stubs are
662 generally not included in Koha, this type of test stub is used by other
663 QueryParser implementations, and it seems sensible to maintain consistency
664 as much as possible.
665
666 =cut
667
668 sub TEST_SETUP {
669     my ($self) = @_;
670
671     $self->default_search_class( 'keyword' );
672
673     $self->add_bib1_field_map('keyword' => 'abstract' => 'biblioserver' => { '1' => '62' } );
674     $self->add_search_field_alias( 'keyword' => 'abstract' => 'ab' );
675     $self->add_bib1_field_map('keyword' => '' => 'biblioserver' => { '1' => '1016' } );
676     $self->add_search_field_alias( 'keyword' => '' => 'kw' );
677     $self->add_bib1_field_map('author' => '' => 'biblioserver' => { '1' => '1003' } );
678     $self->add_search_field_alias( 'author' => '' => 'au' );
679     $self->add_bib1_field_map('author' => 'personal' => 'biblioserver' => { '1' => '1004' } );
680     $self->add_bib1_field_map('author' => 'corporate' => 'biblioserver' => { '1' => '1005' } );
681     $self->add_search_field_alias( 'author' => 'corporate' => 'cpn' );
682     $self->add_bib1_field_map('author' => 'conference' => 'biblioserver' => { '1' => '1006' } );
683     $self->add_search_field_alias( 'author' => 'conference' => 'cfn' );
684     $self->add_bib1_field_map('keyword' => 'local-classification' => 'biblioserver' => { '1' => '20' } );
685     $self->add_search_field_alias( 'keyword' => 'local-classification' => 'lcn' );
686     $self->add_search_field_alias( 'keyword' => 'local-classification' => 'callnum' );
687     $self->add_bib1_field_map('keyword' => 'bib-level' => 'biblioserver' => { '1' => '1021' } );
688     $self->add_bib1_field_map('keyword' => 'code-institution' => 'biblioserver' => { '1' => '56' } );
689     $self->add_bib1_field_map('keyword' => 'language' => 'biblioserver' => { '1' => '54' } );
690     $self->add_search_field_alias( 'keyword' => 'language' => 'ln' );
691     $self->add_bib1_field_map('keyword' => 'record-type' => 'biblioserver' => { '1' => '1001' } );
692     $self->add_search_field_alias( 'keyword' => 'record-type' => 'rtype' );
693     $self->add_search_field_alias( 'keyword' => 'record-type' => 'mc-rtype' );
694     $self->add_search_field_alias( 'keyword' => 'record-type' => 'mus' );
695     $self->add_bib1_field_map('keyword' => 'content-type' => 'biblioserver' => { '1' => '1034' } );
696     $self->add_search_field_alias( 'keyword' => 'content-type' => 'ctype' );
697     $self->add_bib1_field_map('keyword' => 'lc-card-number' => 'biblioserver' => { '1' => '9' } );
698     $self->add_search_field_alias( 'keyword' => 'lc-card-number' => 'lc-card' );
699     $self->add_bib1_field_map('keyword' => 'local-number' => 'biblioserver' => { '1' => '12' } );
700     $self->add_search_field_alias( 'keyword' => 'local-number' => 'sn' );
701     $self->add_bib1_filter_map( 'biblioserver', 'copydate', { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => '30', '4' => '4' });
702     $self->add_bib1_filter_map( 'biblioserver', 'pubdate', { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => 'pubdate', '4' => '4' });
703     $self->add_bib1_filter_map( 'biblioserver', 'acqdate', { 'target_syntax_callback' => \&Koha::QueryParser::Driver::PQF::date_filter_target_callback, '1' => 'Date-of-acquisition', '4' => '4' });
704     $self->add_bib1_field_map('keyword' => 'isbn' => 'biblioserver' => { '1' => '7' } );
705     $self->add_search_field_alias( 'keyword' => 'isbn' => 'nb' );
706     $self->add_bib1_field_map('keyword' => 'issn' => 'biblioserver' => { '1' => '8' } );
707     $self->add_search_field_alias( 'keyword' => 'issn' => 'ns' );
708     $self->add_bib1_field_map('keyword' => 'identifier-standard' => 'biblioserver' => { '1' => '1007' } );
709     $self->add_search_field_alias( 'keyword' => 'identifier-standard' => 'ident' );
710     $self->add_bib1_field_map('keyword' => 'upc' => 'biblioserver' => { '1' => 'UPC' } );
711     $self->add_search_field_alias( 'keyword' => 'upc' => 'upc' );
712     $self->add_bib1_field_map('keyword' => 'ean' => 'biblioserver' => { '1' => 'EAN' } );
713     $self->add_search_field_alias( 'keyword' => 'ean' => 'ean' );
714     $self->add_bib1_field_map('keyword' => 'music' => 'biblioserver' => { '1' => 'Music-number' } );
715     $self->add_search_field_alias( 'keyword' => 'music' => 'music' );
716     $self->add_bib1_field_map('keyword' => 'stock-number' => 'biblioserver' => { '1' => '1028' } );
717     $self->add_search_field_alias( 'keyword' => 'stock-number' => 'stock-number' );
718     $self->add_bib1_field_map('keyword' => 'material-type' => 'biblioserver' => { '1' => '1031' } );
719     $self->add_search_field_alias( 'keyword' => 'material-type' => 'material-type' );
720     $self->add_bib1_field_map('keyword' => 'place-publication' => 'biblioserver' => { '1' => '59' } );
721     $self->add_search_field_alias( 'keyword' => 'place-publication' => 'pl' );
722     $self->add_bib1_field_map('keyword' => 'personal-name' => 'biblioserver' => { '1' => 'Personal-name' } );
723     $self->add_search_field_alias( 'keyword' => 'personal-name' => 'pn' );
724     $self->add_bib1_field_map('keyword' => 'publisher' => 'biblioserver' => { '1' => '1018' } );
725     $self->add_search_field_alias( 'keyword' => 'publisher' => 'pb' );
726     $self->add_bib1_field_map('keyword' => 'note' => 'biblioserver' => { '1' => '63' } );
727     $self->add_search_field_alias( 'keyword' => 'note' => 'nt' );
728     $self->add_bib1_field_map('keyword' => 'record-control-number' => 'biblioserver' => { '1' => '1045' } );
729     $self->add_search_field_alias( 'keyword' => 'record-control-number' => 'rcn' );
730     $self->add_bib1_field_map('subject' => '' => 'biblioserver' => { '1' => '21' } );
731     $self->add_search_field_alias( 'subject' => '' => 'su' );
732     $self->add_search_field_alias( 'subject' => '' => 'su-to' );
733     $self->add_search_field_alias( 'subject' => '' => 'su-geo' );
734     $self->add_search_field_alias( 'subject' => '' => 'su-ut' );
735     $self->add_bib1_field_map('subject' => 'name-personal' => 'biblioserver' => { '1' => '1009' } );
736     $self->add_search_field_alias( 'subject' => 'name-personal' => 'su-na' );
737     $self->add_bib1_field_map('title' => '' => 'biblioserver' => { '1' => '4' } );
738     $self->add_search_field_alias( 'title' => '' => 'ti' );
739     $self->add_bib1_field_map('title' => 'cover' => 'biblioserver' => { '1' => '36' } );
740     $self->add_search_field_alias( 'title' => 'cover' => 'title-cover' );
741     $self->add_bib1_field_map('keyword' => 'host-item' => 'biblioserver' => { '1' => '1033' } );
742     $self->add_bib1_field_map('keyword' => 'video-mt' => 'biblioserver' => { '1' => 'Video-mt' } );
743     $self->add_bib1_field_map('keyword' => 'graphics-type' => 'biblioserver' => { '1' => 'Graphic-type' } );
744     $self->add_bib1_field_map('keyword' => 'graphics-support' => 'biblioserver' => { '1' => 'Graphic-support' } );
745     $self->add_bib1_field_map('keyword' => 'type-of-serial' => 'biblioserver' => { '1' => 'Type-Of-Serial' } );
746     $self->add_bib1_field_map('keyword' => 'regularity-code' => 'biblioserver' => { '1' => 'Regularity-code' } );
747     $self->add_bib1_field_map('keyword' => 'material-type' => 'biblioserver' => { '1' => 'Material-type' } );
748     $self->add_bib1_field_map('keyword' => 'literature-code' => 'biblioserver' => { '1' => 'Literature-Code' } );
749     $self->add_bib1_field_map('keyword' => 'biography-code' => 'biblioserver' => { '1' => 'Biography-code' } );
750     $self->add_bib1_field_map('keyword' => 'illustration-code' => 'biblioserver' => { '1' => 'Illustration-code' } );
751     $self->add_bib1_field_map('title' => 'series' => 'biblioserver' => { '1' => '5' } );
752     $self->add_search_field_alias( 'title' => 'series' => 'title-series' );
753     $self->add_search_field_alias( 'title' => 'series' => 'se' );
754     $self->add_bib1_field_map('title' => 'uniform' => 'biblioserver' => { '1' => 'Title-uniform' } );
755     $self->add_search_field_alias( 'title' => 'uniform' => 'title-uniform' );
756     $self->add_bib1_field_map('subject' => 'authority-number' => 'biblioserver' => { '1' => 'Koha-Auth-Number' } );
757     $self->add_search_field_alias( 'subject' => 'authority-number' => 'an' );
758     $self->add_bib1_field_map('keyword' => 'control-number' => 'biblioserver' => { '1' => '9001' } );
759     $self->add_bib1_field_map('keyword' => 'biblionumber' => 'biblioserver' => { '1' => '9002', '5' => '100' } );
760     $self->add_bib1_field_map('keyword' => 'totalissues' => 'biblioserver' => { '1' => '9003' } );
761     $self->add_bib1_field_map('keyword' => 'cn-bib-source' => 'biblioserver' => { '1' => '9004' } );
762     $self->add_bib1_field_map('keyword' => 'cn-bib-sort' => 'biblioserver' => { '1' => '9005' } );
763     $self->add_bib1_field_map('keyword' => 'itemtype' => 'biblioserver' => { '1' => '9006' } );
764     $self->add_search_field_alias( 'keyword' => 'itemtype' => 'mc-itemtype' );
765     $self->add_bib1_field_map('keyword' => 'cn-class' => 'biblioserver' => { '1' => '9007' } );
766     $self->add_bib1_field_map('keyword' => 'cn-item' => 'biblioserver' => { '1' => '9008' } );
767     $self->add_bib1_field_map('keyword' => 'cn-prefix' => 'biblioserver' => { '1' => '9009' } );
768     $self->add_bib1_field_map('keyword' => 'cn-suffix' => 'biblioserver' => { '1' => '9010' } );
769     $self->add_bib1_field_map('keyword' => 'suppress' => 'biblioserver' => { '1' => '9011' } );
770     $self->add_bib1_field_map('keyword' => 'id-other' => 'biblioserver' => { '1' => '9012' } );
771     $self->add_bib1_field_map('keyword' => 'date-entered-on-file' => 'biblioserver' => { '1' => 'date-entered-on-file' } );
772     $self->add_bib1_field_map('keyword' => 'extent' => 'biblioserver' => { '1' => 'Extent' } );
773     $self->add_bib1_field_map('keyword' => 'llength' => 'biblioserver' => { '1' => 'llength' } );
774     $self->add_bib1_field_map('keyword' => 'summary' => 'biblioserver' => { '1' => 'Summary' } );
775     $self->add_bib1_field_map('keyword' => 'withdrawn' => 'biblioserver' => { '1' => '8001' } );
776     $self->add_bib1_field_map('keyword' => 'lost' => 'biblioserver' => { '1' => '8002' } );
777     $self->add_bib1_field_map('keyword' => 'classification-source' => 'biblioserver' => { '1' => '8003' } );
778     $self->add_bib1_field_map('keyword' => 'materials-specified' => 'biblioserver' => { '1' => '8004' } );
779     $self->add_bib1_field_map('keyword' => 'damaged' => 'biblioserver' => { '1' => '8005' } );
780     $self->add_bib1_field_map('keyword' => 'restricted' => 'biblioserver' => { '1' => '8006' } );
781     $self->add_bib1_field_map('keyword' => 'cn-sort' => 'biblioserver' => { '1' => '8007' } );
782     $self->add_bib1_field_map('keyword' => 'notforloan' => 'biblioserver' => { '1' => '8008', '4' => '109' } );
783     $self->add_bib1_field_map('keyword' => 'ccode' => 'biblioserver' => { '1' => '8009' } );
784     $self->add_search_field_alias( 'keyword' => 'ccode' => 'mc-ccode' );
785     $self->add_bib1_field_map('keyword' => 'itemnumber' => 'biblioserver' => { '1' => '8010' } );
786     $self->add_bib1_field_map('keyword' => 'homebranch' => 'biblioserver' => { '1' => 'homebranch' } );
787     $self->add_search_field_alias( 'keyword' => 'homebranch' => 'branch' );
788     $self->add_bib1_field_map('keyword' => 'holdingbranch' => 'biblioserver' => { '1' => '8012' } );
789     $self->add_bib1_field_map('keyword' => 'location' => 'biblioserver' => { '1' => '8013' } );
790     $self->add_search_field_alias( 'keyword' => 'location' => 'mc-loc' );
791     $self->add_bib1_field_map('keyword' => 'acqsource' => 'biblioserver' => { '1' => '8015' } );
792     $self->add_bib1_field_map('keyword' => 'coded-location-qualifier' => 'biblioserver' => { '1' => '8016' } );
793     $self->add_bib1_field_map('keyword' => 'price' => 'biblioserver' => { '1' => '8017' } );
794     $self->add_bib1_field_map('keyword' => 'stocknumber' => 'biblioserver' => { '1' => '1062' } );
795     $self->add_search_field_alias( 'keyword' => 'stocknumber' => 'inv' );
796     $self->add_bib1_field_map('keyword' => 'stack' => 'biblioserver' => { '1' => '8018' } );
797     $self->add_bib1_field_map('keyword' => 'issues' => 'biblioserver' => { '1' => '8019' } );
798     $self->add_bib1_field_map('keyword' => 'renewals' => 'biblioserver' => { '1' => '8020' } );
799     $self->add_bib1_field_map('keyword' => 'reserves' => 'biblioserver' => { '1' => '8021' } );
800     $self->add_bib1_field_map('keyword' => 'local-classification' => 'biblioserver' => { '1' => '8022' } );
801     $self->add_bib1_field_map('keyword' => 'barcode' => 'biblioserver' => { '1' => '8023' } );
802     $self->add_search_field_alias( 'keyword' => 'barcode' => 'bc' );
803     $self->add_bib1_field_map('keyword' => 'onloan' => 'biblioserver' => { '1' => '8024' } );
804     $self->add_bib1_field_map('keyword' => 'datelastseen' => 'biblioserver' => { '1' => '8025' } );
805     $self->add_bib1_field_map('keyword' => 'datelastborrowed' => 'biblioserver' => { '1' => '8026' } );
806     $self->add_bib1_field_map('keyword' => 'copynumber' => 'biblioserver' => { '1' => '8027' } );
807     $self->add_bib1_field_map('keyword' => 'uri' => 'biblioserver' => { '1' => '8028' } );
808     $self->add_bib1_field_map('keyword' => 'replacementprice' => 'biblioserver' => { '1' => '8029' } );
809     $self->add_bib1_field_map('keyword' => 'replacementpricedate' => 'biblioserver' => { '1' => '8030' } );
810     $self->add_bib1_field_map('keyword' => 'itype' => 'biblioserver' => { '1' => '8031' } );
811     $self->add_search_field_alias( 'keyword' => 'itype' => 'mc-itype' );
812     $self->add_bib1_field_map('keyword' => 'ff8-22' => 'biblioserver' => { '1' => '8822' } );
813     $self->add_bib1_field_map('keyword' => 'ff8-23' => 'biblioserver' => { '1' => '8823' } );
814     $self->add_bib1_field_map('keyword' => 'ff8-34' => 'biblioserver' => { '1' => '8834' } );
815 # Audience
816     $self->add_bib1_field_map('keyword' => 'audience' => 'biblioserver' => { '1' => '8822' } );
817     $self->add_search_field_alias( 'keyword' => 'audience' => 'aud' );
818
819 # Content and Literary form
820     $self->add_bib1_field_map('keyword' => 'fiction' => 'biblioserver' => { '1' => '8833' } );
821     $self->add_search_field_alias( 'keyword' => 'fiction' => 'fic' );
822     $self->add_bib1_field_map('keyword' => 'biography' => 'biblioserver' => { '1' => '8834' } );
823     $self->add_search_field_alias( 'keyword' => 'biography' => 'bio' );
824
825 # Format
826     $self->add_bib1_field_map('keyword' => 'format' => 'biblioserver' => { '1' => '8823' } );
827 # format used as a limit FIXME: needed?
828     $self->add_bib1_field_map('keyword' => 'l-format' => 'biblioserver' => { '1' => '8703' } );
829
830     $self->add_bib1_field_map('keyword' => 'illustration-code' => 'biblioserver' => { '1' => 'Illustration-code ' } );
831
832 # Lexile Number
833     $self->add_bib1_field_map('keyword' => 'lex' => 'biblioserver' => { '1' => '9903 r=r' } );
834
835 #Accelerated Reader Level
836     $self->add_bib1_field_map('keyword' => 'arl' => 'biblioserver' => { '1' => '9904 r=r' } );
837
838 #Accelerated Reader Point
839     $self->add_bib1_field_map('keyword' => 'arp' => 'biblioserver' => { '1' => '9014 r=r' } );
840
841 # Curriculum
842     $self->add_bib1_field_map('keyword' => 'curriculum' => 'biblioserver' => { '1' => '9658' } );
843
844 ## Statuses
845     $self->add_bib1_field_map('keyword' => 'popularity' => 'biblioserver' => { '1' => 'issues' } );
846
847 ## Type Limits
848     $self->add_bib1_field_map('keyword' => 'dt-bks' => 'biblioserver' => { '1' => '8700' } );
849     $self->add_bib1_field_map('keyword' => 'dt-vis' => 'biblioserver' => { '1' => '8700' } );
850     $self->add_bib1_field_map('keyword' => 'dt-sr' => 'biblioserver' => { '1' => '8700' } );
851     $self->add_bib1_field_map('keyword' => 'dt-cf' => 'biblioserver' => { '1' => '8700' } );
852     $self->add_bib1_field_map('keyword' => 'dt-map' => 'biblioserver' => { '1' => '8700' } );
853
854     $self->add_bib1_field_map('keyword' => 'name' => 'biblioserver' => { '1' => '1002' } );
855     $self->add_bib1_field_map('keyword' => 'item' => 'biblioserver' => { '1' => '9520' } );
856     $self->add_bib1_field_map('keyword' => 'host-item-number' => 'biblioserver' => { '1' => '8911' } );
857     $self->add_search_field_alias( 'keyword' => 'host-item-number' => 'hi' );
858
859     $self->add_bib1_field_map('keyword' => 'alwaysmatch' => 'biblioserver' => { '1' => '_ALLRECORDS', '2' => '103' } );
860     $self->add_bib1_field_map('subject' => 'complete' => 'biblioserver' => { '1' => '21', '3' => '1', '4' => '1', '5' => '100', '6' => '3' } );
861
862     $self->add_bib1_modifier_map('relevance' => 'biblioserver' => { '2' => '102' } );
863     $self->add_bib1_modifier_map('title-sort-za' => 'biblioserver' => { '7' => '2', '1' => '36', '' => '0', 'op' => '@or' } );
864     $self->add_bib1_modifier_map('title-sort-az' => 'biblioserver' => { '7' => '1', '1' => '36', '' => '0', 'op' => '@or' } );
865     $self->add_bib1_modifier_map('relevance_dsc' => 'biblioserver' => { '2' => '102' } );
866     $self->add_bib1_modifier_map('title_dsc' => 'biblioserver' => { '7' => '2', '1' => '4', '' => '0', 'op' => '@or' } );
867     $self->add_bib1_modifier_map('title_asc' => 'biblioserver' => { '7' => '1', '1' => '4', '' => '0', 'op' => '@or' } );
868     $self->add_bib1_modifier_map('author_asc' => 'biblioserver' => { '7' => '2', '1' => '1003', '' => '0', 'op' => '@or' } );
869     $self->add_bib1_modifier_map('author_dsc' => 'biblioserver' => { '7' => '1', '1' => '1003', '' => '0', 'op' => '@or' } );
870     $self->add_bib1_modifier_map('popularity_asc' => 'biblioserver' => { '7' => '2', '1' => '9003', '' => '0', 'op' => '@or' } );
871     $self->add_bib1_modifier_map('popularity_dsc' => 'biblioserver' => { '7' => '1', '1' => '9003', '' => '0', 'op' => '@or' } );
872     $self->add_bib1_modifier_map('call_number_asc' => 'biblioserver' => { '7' => '2', '1' => '8007', '' => '0', 'op' => '@or' } );
873     $self->add_bib1_modifier_map('call_number_dsc' => 'biblioserver' => { '7' => '1', '1' => '8007', '' => '0', 'op' => '@or' } );
874     $self->add_bib1_modifier_map('pubdate_asc' => 'biblioserver' => { '7' => '2', '1' => '31', '' => '0', 'op' => '@or' } );
875     $self->add_bib1_modifier_map('pubdate_dsc' => 'biblioserver' => { '7' => '1', '1' => '31', '' => '0', 'op' => '@or' } );
876     $self->add_bib1_modifier_map('acqdate_asc' => 'biblioserver' => { '7' => '2', '1' => '32', '' => '0', 'op' => '@or' } );
877     $self->add_bib1_modifier_map('acqdate_dsc' => 'biblioserver' => { '7' => '1', '1' => '32', '' => '0', 'op' => '@or' } );
878
879     $self->add_bib1_modifier_map('title_za' => 'biblioserver' => { '7' => '2', '1' => '4', '' => '0', 'op' => '@or' } );
880     $self->add_bib1_modifier_map('title_az' => 'biblioserver' => { '7' => '1', '1' => '4', '' => '0', 'op' => '@or' } );
881     $self->add_bib1_modifier_map('author_za' => 'biblioserver' => { '7' => '2', '1' => '1003', '' => '0', 'op' => '@or' } );
882     $self->add_bib1_modifier_map('author_az' => 'biblioserver' => { '7' => '1', '1' => '1003', '' => '0', 'op' => '@or' } );
883     $self->add_bib1_modifier_map('ascending' => 'biblioserver' => { '7' => '1' } );
884     $self->add_bib1_modifier_map('descending' => 'biblioserver' => { '7' => '2' } );
885
886     $self->add_bib1_field_map('title' => 'exacttitle' => 'biblioserver' => { '1' => '4', '4' => '1', '6' => '3' } );
887     $self->add_search_field_alias( 'title' => 'exacttitle' => 'ti,ext' );
888     $self->add_bib1_field_map('author' => 'exactauthor' => 'biblioserver' => { '1' => '1003', '4' => '1', '6' => '3' } );
889     $self->add_search_field_alias( 'author' => 'exactauthor' => 'au,ext' );
890
891     $self->add_bib1_field_map('subject' => 'headingmain' => 'authorityserver' => { '1' => 'Heading-Main' } );
892     $self->add_bib1_field_map('subject' => 'heading' => 'authorityserver' => { '1' => 'Heading' } );
893     $self->add_bib1_field_map('subject' => 'matchheading' => 'authorityserver' => { '1' => 'Match-heading' } );
894     $self->add_bib1_field_map('subject' => 'seefrom' => 'authorityserver' => { '1' => 'Match-heading-see-from' } );
895     $self->add_bib1_field_map('subject' => '' => 'authorityserver' => { '1' => 'Match-heading' } );
896     $self->add_bib1_field_map('keyword' => 'alwaysmatch' => 'authorityserver' => { '1' => '_ALLRECORDS', '2' => '103' } );
897     $self->add_bib1_field_map('keyword' => 'match' => 'authorityserver' => { '1' => 'Match' } );
898     $self->add_bib1_field_map('keyword' => 'thesaurus' => 'authorityserver' => { '1' => 'Subject-heading-thesaurus' } );
899     $self->add_bib1_field_map('keyword' => 'authtype' => 'authorityserver' => { '1' => 'authtype', '5' => '100' } );
900     $self->add_bib1_field_map('keyword' => '' => 'authorityserver' => { '1' => 'Any' } );
901     $self->add_search_field_alias( 'subject' => 'headingmain' => 'mainmainentry' );
902     $self->add_search_field_alias( 'subject' => 'heading' => 'mainentry' );
903     $self->add_search_field_alias( 'subject' => 'heading' => 'he' );
904     $self->add_search_field_alias( 'subject' => 'matchheading' => 'match-heading' );
905     $self->add_search_field_alias( 'keyword' => '' => 'any' );
906     $self->add_search_field_alias( 'keyword' => 'match' => 'match' );
907     $self->add_search_field_alias( 'subject' => 'seefrom' => 'see-from' );
908     $self->add_search_field_alias( 'keyword' => 'thesaurus' => 'thesaurus' );
909     $self->add_search_field_alias( 'keyword' => 'alwaysmatch' => 'all' );
910     $self->add_search_field_alias( 'keyword' => 'authtype' => 'authtype' );
911     $self->add_search_field_alias( 'keyword' => 'authtype' => 'at' );
912
913     $self->add_bib1_field_map('subject' => 'start' => 'authorityserver' => { '3' => '2', '4' => '1', '5' => '1' } );
914     $self->add_bib1_field_map('subject' => 'exact' => 'authorityserver' => { '4' => '1', '5' => '100', '6' => '3' } );
915
916     $self->add_bib1_modifier_map('HeadingAsc' => 'authorityserver' => { '7' => '1', '1' => 'Heading', '' => '0', 'op' => '@or' } );
917     $self->add_bib1_modifier_map('HeadingDsc' => 'authorityserver' => { '7' => '2', '1' => 'Heading', '' => '0', 'op' => '@or' } );
918     $self->add_bib1_modifier_map('AuthidAsc' => 'authorityserver' => { '7' => '1', '1' => 'Local-Number', '' => '0', 'op' => '@or' } );
919     $self->add_bib1_modifier_map('AuthidDsc' => 'authorityserver' => { '7' => '2', '1' => 'Local-Number', '' => '0', 'op' => '@or' } );
920     $self->add_bib1_modifier_map('Relevance' => 'authorityserver' => { '2' => '102' } );
921
922     return $self;
923 }
924
925 1;