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