Removed ODRs from zebra_search
[idzebra-moved-to-github.git] / perl / lib / IDZebra / Session.pm
1 # $Id: Session.pm,v 1.17 2003-05-21 08:03:02 pop Exp $
2
3 # Zebra perl API header
4 # =============================================================================
5 package IDZebra::Session;
6
7 use strict;
8 use warnings;
9 use Carp;
10
11 BEGIN {
12     use IDZebra;
13     use Scalar::Util;
14     use IDZebra::Logger qw(:flags :calls);
15     use IDZebra::Resultset;
16     use IDZebra::ScanList;
17     use IDZebra::RetrievalRecord;
18     require Exporter;
19     our $VERSION = do { my @r = (q$Revision: 1.17 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; 
20     our @ISA = qw(IDZebra::Logger Exporter);
21     our @EXPORT = qw (TRANS_RW TRANS_RO);
22 }
23
24 use constant TRANS_RW => 1;
25 use constant TRANS_RO => 0;
26
27 1;
28 # -----------------------------------------------------------------------------
29 # Class constructors, destructor
30 # -----------------------------------------------------------------------------
31 sub new {
32     my ($proto, %args) = @_;
33     my $class = ref($proto) || $proto;
34     my $self = {};
35     $self->{args} = \%args;
36     
37     bless ($self, $class);
38     $self->{cql_ct} = undef;
39     $self->{cql_mapfile} = "";
40     return ($self);
41
42     $self->{databases} = {};
43 }
44
45 sub start_service {
46     my ($self, %args) = @_;
47
48     my $zs;
49     unless (defined($self->{zs})) {
50         if (defined($args{'configFile'})) {
51             $self->{zs} = IDZebra::start($args{'configFile'});
52         } else {
53             $self->{zs} = IDZebra::start("zebra.cfg");
54         }
55     }
56 }
57
58 sub stop_service {
59     my ($self) = @_;
60     if (defined($self->{zs})) {
61         IDZebra::stop($self->{zs}) if ($self->{zs});    
62         $self->{zs} = undef;
63     }
64 }
65
66
67 sub open {
68     my ($proto,%args) = @_;
69     my $self = {};
70
71     if (ref($proto)) { $self = $proto; } else { 
72         $self = $proto->new(%args);
73     }
74
75     unless (%args) {
76         %args = %{$self->{args}};
77     }
78
79     $self->start_service(%args);
80
81     unless (defined($self->{zs})) {
82         croak ("Falied to open zebra service");
83     }    
84
85     unless (defined($self->{zh})) {
86         $self->{zh}=IDZebra::open($self->{zs}); 
87     }   
88
89     # Reset result set counter
90     $self->{rscount} = 0;
91
92     # This is needed in order to somehow initialize the service
93     $self->databases("Default");
94
95     # Load the default configuration
96     $self->group(%args);
97
98
99     # Set shadow usage
100     my $shadow = defined($args{shadow}) ? $args{shadow} : 0;
101     $self->shadow($shadow);
102     
103     $self->{odr_input} = IDZebra::odr_createmem($IDZebra::ODR_DECODE);
104     $self->{odr_output} = IDZebra::odr_createmem($IDZebra::ODR_ENCODE);
105
106     return ($self);
107 }
108
109 sub checkzh {
110     my ($self) = @_;
111     unless (defined($self->{zh})) {
112         croak ("Zebra session is not opened");
113     }
114 }
115
116 sub close {
117     my ($self) = @_;
118
119     if ($self->{zh}) {
120
121         my $stats = 0; 
122         # Delete all resulsets
123         my $r = IDZebra::deleteResultSet($self->{zh},
124                                          1, #Z_DeleteRequest_all,
125                                          0,[],
126                                          $stats);
127
128         while (IDZebra::trans_no($self->{zh}) > 0) {
129             logf (LOG_WARN,"Explicitly closing transaction with session");
130             $self->end_trans;
131         }
132
133         IDZebra::close($self->{zh});
134         $self->{zh} = undef;
135     }
136     
137     if ($self->{odr_input}) {
138         IDZebra::odr_reset($self->{odr_input});
139         IDZebra::odr_destroy($self->{odr_input});
140         $self->{odr_input} = undef;  
141     }
142
143     if ($self->{odr_output}) {
144         IDZebra::odr_reset($self->{odr_output});
145         IDZebra::odr_destroy($self->{odr_output});
146         $self->{odr_output} = undef;  
147     }
148
149     $self->stop_service;
150 }
151
152 sub DESTROY {
153     my ($self) = @_;
154     logf (LOG_LOG,"DESTROY $self");
155     $self->close; 
156
157     if (defined ($self->{cql_ct})) {
158       IDZebra::cql_transform_close($self->{cql_ct});
159     }
160
161 }
162 # -----------------------------------------------------------------------------
163 # Record group selection  This is a bit nasty... but used at many places 
164 # -----------------------------------------------------------------------------
165 sub group {
166     my ($self,%args) = @_;
167     $self->checkzh;
168     if ($#_ > 0) {
169         $self->{rg} = $self->_makeRecordGroup(%args);
170         $self->_selectRecordGroup($self->{rg});
171     }
172     return($self->{rg});
173 }
174
175 sub selectRecordGroup {
176     my ($self, $groupName) = @_;
177     $self->checkzh;
178     $self->{rg} = $self->_getRecordGroup($groupName);
179     $self->_selectRecordGroup($self->{rg});
180 }
181
182 sub _displayRecordGroup {
183     my ($self, $rg) = @_;
184     print STDERR "-----\n";
185     foreach my $key qw (groupName 
186                         databaseName 
187                         path recordId 
188                         recordType 
189                         flagStoreData 
190                         flagStoreKeys 
191                         flagRw 
192                         fileVerboseLimit 
193                         databaseNamePath 
194                         explainDatabase 
195                         followLinks) {
196         print STDERR "$key:",$rg->{$key},"\n";
197     }
198 }
199
200 sub _cloneRecordGroup {
201     my ($self, $orig) = @_;
202     my $rg = IDZebra::recordGroup->new();
203     my $r = IDZebra::init_recordGroup($rg);
204     foreach my $key qw (groupName 
205                         databaseName 
206                         path 
207                         recordId 
208                         recordType 
209                         flagStoreData 
210                         flagStoreKeys 
211                         flagRw 
212                         fileVerboseLimit 
213                         databaseNamePath 
214                         explainDatabase 
215                         followLinks) {
216         $rg->{$key} = $orig->{$key} if ($orig->{$key});
217     }
218     return ($rg);
219 }
220
221 sub _getRecordGroup {
222     my ($self, $groupName, $ext) = @_;
223     my $rg = IDZebra::recordGroup->new();
224     my $r = IDZebra::init_recordGroup($rg);
225     $rg->{groupName} = $groupName if ($groupName ne "");  
226     $ext = "" unless ($ext);
227     $r = IDZebra::res_get_recordGroup($self->{zh}, $rg, $ext);
228     return ($rg);
229 }
230
231 sub _makeRecordGroup {
232     my ($self, %args) = @_;
233     my $rg;
234
235     my @keys = keys(%args);
236     unless ($#keys >= 0) {
237         return ($self->{rg});
238     }
239
240     if ($args{groupName}) {
241         $rg = $self->_getRecordGroup($args{groupName});
242     } else {
243         $rg = $self->_cloneRecordGroup($self->{rg});
244     }
245     $self->_setRecordGroupOptions($rg, %args);
246     return ($rg);
247 }
248
249 sub _setRecordGroupOptions {
250     my ($self, $rg, %args) = @_;
251
252     foreach my $key qw (databaseName 
253                         path 
254                         recordId 
255                         recordType 
256                         flagStoreData 
257                         flagStoreKeys 
258                         flagRw 
259                         fileVerboseLimit 
260                         databaseNamePath 
261                         explainDatabase 
262                         followLinks) {
263         if (defined ($args{$key})) {
264             $rg->{$key} = $args{$key};
265         }
266     }
267 }
268 sub _selectRecordGroup {
269     my ($self, $rg) = @_;
270
271     my $r = IDZebra::set_group($self->{zh}, $rg);
272     my $dbName;
273     unless ($dbName = $rg->{databaseName}) {
274         $dbName = 'Default';
275     }
276     unless ($self->databases($dbName)) {
277         croak("Fatal error selecting database $dbName");
278     }
279 }
280 # -----------------------------------------------------------------------------
281 # Selecting databases for search (and also for updating - internally)
282 # -----------------------------------------------------------------------------
283 sub databases {
284     my ($self, @databases) = @_;
285
286     $self->checkzh;
287
288     unless ($#_ >0) {
289         return (keys(%{$self->{databases}}));
290     }
291
292     my %tmp;
293     my $changed = 0;
294     foreach my $db (@databases) {
295         $tmp{$db}++;
296         next if ($self->{databases}{$db});
297         $changed++;
298     }
299
300     foreach my $db (keys (%{$self->{databases}})) {
301         $changed++ unless ($tmp{$db});
302     }
303
304     if ($changed) {
305
306         delete ($self->{databases});
307         foreach my $db (@databases) {
308             $self->{databases}{$db}++;
309         }
310
311         if (IDZebra::select_databases($self->{zh}, 
312                                                 ($#databases + 1), 
313                                                 \@databases)) {
314             logf(LOG_FATAL, 
315                  "Could not select database(s) %s errCode=%d",
316                  join(",",@databases),
317                  $self->errCode());
318             return (0);
319         } else {
320             logf(LOG_LOG,"Database(s) selected: %s",join(",",@databases));
321         }
322     }
323     return (keys(%{$self->{databases}}));
324 }
325
326 # -----------------------------------------------------------------------------
327 # Error handling
328 # -----------------------------------------------------------------------------
329 sub errCode {
330     my ($self) = @_;
331     return(IDZebra::errCode($self->{zh}));
332 }
333
334 sub errString {
335     my ($self) = @_;
336     return(IDZebra::errString($self->{zh}));
337 }
338
339 sub errAdd {
340     my ($self) = @_;
341     return(IDZebra::errAdd($self->{zh}));
342 }
343
344 # -----------------------------------------------------------------------------
345 # Transaction stuff
346 # -----------------------------------------------------------------------------
347 sub begin_trans {
348     my ($self, $m) = @_;
349     $m = TRANS_RW unless (defined ($m));
350     if (my $err = IDZebra::begin_trans($self->{zh},$m)) {
351         if ($self->errCode == 2) {
352             croak ("TRANS_RW not allowed within TRANS_RO");
353         } else {
354             croak("Error starting transaction; code:".
355                   $self->errCode . " message: " . $self->errString);
356         }
357     }
358 }
359
360 sub end_trans {
361     my ($self) = @_;
362     $self->checkzh;
363     my $stat = IDZebra::ZebraTransactionStatus->new();
364     IDZebra::end_trans($self->{zh}, $stat);
365     return ($stat);
366 }
367
368 sub shadow {
369     my ($self, $value) = @_;
370     $self->checkzh;
371     if ($#_ > 0) { 
372         $value = 0 unless (defined($value));
373         my $r =IDZebra::set_shadow_enable($self->{zh},$value); 
374     }
375     return (IDZebra::get_shadow_enable($self->{zh}));
376 }
377
378 sub commit {
379     my ($self) = @_;
380     $self->checkzh;
381     if ($self->shadow) {
382         return(IDZebra::commit($self->{zh}));
383     }
384 }
385
386 # -----------------------------------------------------------------------------
387 # We don't really need that...
388 # -----------------------------------------------------------------------------
389 sub odr_reset {
390     my ($self, $name) = @_;
391     if ($name !~/^(input|output)$/) {
392         croak("Undefined ODR '$name'");
393     }
394   IDZebra::odr_reset($self->{"odr_$name"});
395 }
396
397 # -----------------------------------------------------------------------------
398 # Init/compact
399 # -----------------------------------------------------------------------------
400 sub init {
401     my ($self) = @_;
402     $self->checkzh;
403     return(IDZebra::init($self->{zh}));
404 }
405
406 sub compact {
407     my ($self) = @_;
408     $self->checkzh;
409     return(IDZebra::compact($self->{zh}));
410 }
411
412 sub update {
413     my ($self, %args) = @_;
414     $self->checkzh;
415     my $rg = $self->_update_args(%args);
416     $self->_selectRecordGroup($rg);
417     $self->begin_trans;
418     IDZebra::repository_update($self->{zh});
419     $self->_selectRecordGroup($self->{rg});
420     $self->end_trans;
421 }
422
423 sub delete {
424     my ($self, %args) = @_;
425     $self->checkzh;
426     my $rg = $self->_update_args(%args);
427     $self->_selectRecordGroup($rg);
428     $self->begin_trans;
429     IDZebra::repository_delete($self->{zh});
430     $self->_selectRecordGroup($self->{rg});
431     $self->end_trans;
432 }
433
434 sub show {
435     my ($self, %args) = @_;
436     $self->checkzh;
437     my $rg = $self->_update_args(%args);
438     $self->_selectRecordGroup($rg);
439     $self->begin_trans;
440     IDZebra::repository_show($self->{zh});
441     $self->_selectRecordGroup($self->{rg});
442     $self->end_trans;
443 }
444
445 sub _update_args {
446     my ($self, %args) = @_;
447     my $rg = $self->_makeRecordGroup(%args);
448     $self->_selectRecordGroup($rg);
449     return ($rg);
450 }
451
452 # -----------------------------------------------------------------------------
453 # Per record update
454 # -----------------------------------------------------------------------------
455 sub insert_record {
456     my ($self, %args) = @_;
457     $self->checkzh;
458     return(IDZebra::insert_record($self->{zh},
459                                   $self->_record_update_args(%args)));
460 }
461
462 sub update_record {
463     my ($self, %args) = @_;
464     $self->checkzh;
465     return(IDZebra::update_record($self->{zh},
466                                   $self->_record_update_args(%args)));
467 }
468
469 sub delete_record {
470     my ($self, %args) = @_;
471     $self->checkzh;
472     return(IDZebra::delete_record($self->{zh},
473                                   $self->_record_update_args(%args)));
474 }
475
476 sub _record_update_args {
477     my ($self, %args) = @_;
478
479     my $sysno   = $args{sysno}      ? $args{sysno}      : 0;
480     my $match   = $args{match}      ? $args{match}      : "";
481     my $rectype = $args{recordType} ? $args{recordType} : "";
482     my $fname   = $args{file}       ? $args{file}       : "<no file>";
483     my $force   = $args{force}      ? $args{force}      : 0;
484
485     my $buff;
486
487     if ($args{data}) {
488         $buff = $args{data};
489     } 
490     elsif ($args{file}) {
491         CORE::open (F, $args{file}) || warn ("Cannot open $args{file}");
492         $buff = join('',(<F>));
493         CORE::close (F);
494     }
495     my $len = length($buff);
496
497     delete ($args{sysno});
498     delete ($args{match});
499     delete ($args{recordType});
500     delete ($args{file});
501     delete ($args{data});
502     delete ($args{force});
503
504     my $rg = $self->_makeRecordGroup(%args);
505
506     # If no record type is given, then try to find it out from the
507     # file extension;
508     unless ($rectype) {
509         if (my ($ext) = $fname =~ /\.(\w+)$/) {
510             my $rg2 = $self->_getRecordGroup($rg->{groupName},$ext);
511             $rectype = $rg2->{recordType};
512         } 
513     }
514
515     $rg->{databaseName} = "Default" unless ($rg->{databaseName});
516
517     unless ($rectype) {
518         $rectype="";
519     }
520     return ($rg, $rectype, $sysno, $match, $fname, $buff, $len, $force);
521 }
522
523 # -----------------------------------------------------------------------------
524 # CQL stuff
525 sub cqlmap {
526     my ($self,$mapfile) = @_;
527     if ($#_ > 0) {
528         if ($self->{cql_mapfile} ne $mapfile) {
529             unless (-f $mapfile) {
530                 croak("Cannot find $mapfile");
531             }
532             if (defined ($self->{cql_ct})) {
533               IDZebra::cql_transform_close($self->{cql_ct});
534             }
535             $self->{cql_ct} = IDZebra::cql_transform_open_fname($mapfile);
536             $self->{cql_mapfile} = $mapfile;
537         }
538     }
539     return ($self->{cql_mapfile});
540 }
541
542 sub cql2pqf {
543     my ($self, $cqlquery) = @_;
544     unless (defined($self->{cql_ct})) {
545         croak("CQL map file is not specified yet.");
546     }
547     my $res = "\0" x 2048;
548     my $r = IDZebra::cql2pqf($self->{cql_ct}, $cqlquery, $res, 2048);
549     if ($r) {
550 #       carp ("Error transforming CQL query: '$cqlquery', status:$r");
551     }
552     $res=~s/\0.+$//g;
553     return ($res,$r); 
554 }
555
556
557 # -----------------------------------------------------------------------------
558 # Search 
559 # -----------------------------------------------------------------------------
560 sub search {
561     my ($self, %args) = @_;
562
563     $self->checkzh;
564
565     if ($args{cqlmap}) { $self->cqlmap($args{cqlmap}); }
566
567     my $query;
568     if ($args{pqf}) {
569         $query = $args{pqf};
570     }
571     elsif ($args{cql}) {
572         my $cqlstat;
573         ($query, $cqlstat) =  $self->cql2pqf($args{cql});
574         unless ($query) {
575             croak ("Failed to transform query: '$args{cql}', ".
576                    "status: ($cqlstat)");
577         }
578     }
579     unless ($query) {
580         croak ("No query given to search");
581     }
582
583     my @origdbs;
584
585     if ($args{databases}) {
586         @origdbs = $self->databases;
587         $self->databases(@{$args{databases}});
588     }
589
590     my $rsname = $args{rsname} ? $args{rsname} : $self->_new_setname;
591
592     my $rs = $self->_search_pqf($query, $rsname);
593
594     if ($args{databases}) {
595         $self->databases(@origdbs);
596     }
597
598     if ($args{sort}) {
599         if ($rs->errCode) {
600             carp("Sort skipped due to search error: ".
601                  $rs->errCode);
602         } else {
603             $rs->sort($args{sort});
604         }
605     }
606
607     return ($rs);
608 }
609
610 sub _new_setname {
611     my ($self) = @_;
612     return ("set_".$self->{rscount}++);
613 }
614
615 sub _search_pqf {
616     my ($self, $query, $setname) = @_;
617
618     my $hits = IDZebra::search_PQF($self->{zh},
619                                    $query,
620                                    $setname);
621
622     my $rs  = IDZebra::Resultset->new($self,
623                                       name        => $setname,
624                                       recordCount => $hits,
625                                       errCode     => $self->errCode,
626                                       errString   => $self->errString);
627     return($rs);
628 }
629
630 # -----------------------------------------------------------------------------
631 # Sort
632 #
633 # Sorting of multiple result sets is not supported by zebra...
634 # -----------------------------------------------------------------------------
635
636 sub sortResultsets {
637     my ($self, $sortspec, $setname, @sets) = @_;
638
639     $self->checkzh;
640
641     if ($#sets > 0) {
642         croak ("Sorting/merging of multiple resultsets is not supported now");
643     }
644
645     my @setnames;
646     my $count = 0;
647     foreach my $rs (@sets) {
648         push (@setnames, $rs->{name});
649         $count += $rs->{recordCount};  # is this really sure ??? It doesn't 
650                                        # matter now...
651     }
652
653     my $status = IDZebra::sort($self->{zh},
654                                $self->{odr_output},
655                                $sortspec,
656                                $setname,
657                                \@setnames);
658
659     my $errCode = $self->errCode;
660     my $errString = $self->errString;
661
662     logf (LOG_LOG, "Sort status $setname: %d, errCode: %d, errString: %s", 
663           $status, $errCode, $errString);
664
665     if ($status || $errCode) {$count = 0;}
666
667     my $rs  = IDZebra::Resultset->new($self,
668                                       name        => $setname,
669                                       recordCount => $count,
670                                       errCode     => $errCode,
671                                       errString   => $errString);
672     
673     return ($rs);
674 }
675 # -----------------------------------------------------------------------------
676 # Scan
677 # -----------------------------------------------------------------------------
678 sub scan {
679     my ($self, %args) = @_;
680
681     $self->checkzh;
682
683     unless ($args{expression}) {
684         croak ("No scan expression given");
685     }
686
687     my $sl = IDZebra::ScanList->new($self,%args);
688
689     return ($sl);
690 }
691
692 # ============================================================================
693
694 __END__
695
696 =head1 NAME
697
698 IDZebra::Session - A Zebra database server session for update and retrieval
699
700 =head1 SYNOPSIS
701
702   $sess = IDZebra::Session->new(configFile => 'demo/zebra.cfg');
703   $sess->open();
704
705   $sess = IDZebra::Session->open(configFile => 'demo/zebra.cfg',
706                                  groupName  => 'demo1');
707
708   $sess->group(groupName => 'demo2');
709
710   $sess->init();
711
712   $sess->begin_trans;
713
714   $sess->update(path      =>  'lib');
715
716   my $s1=$sess->update_record(data       => $rec1,
717                               recordType => 'grs.perl.pod',
718                               groupName  => "demo1",
719                               );
720
721   my $stat = $sess->end_trans;
722
723   $sess->databases('demo1','demo2');
724
725   my $rs1 = $sess->search(cqlmap    => 'demo/cql.map',
726                           cql       => 'dc.title=IDZebra',
727                           databases => [qw(demo1 demo2)]);
728   $sess->close;
729
730 =head1 DESCRIPTION
731
732 Zebra is a high-performance, general-purpose structured text indexing and retrieval engine. It reads structured records in a variety of input formats (eg. email, XML, MARC) and allows access to them through exact boolean search expressions and relevance-ranked free-text queries. 
733
734 Zebra supports large databases (more than ten gigabytes of data, tens of millions of records). It supports incremental, safe database updates on live systems. You can access data stored in Zebra using a variety of Index Data tools (eg. YAZ and PHP/YAZ) as well as commercial and freeware Z39.50 clients and toolkits. 
735
736 =head1 OPENING AND CLOSING A ZEBRA SESSIONS
737
738 For the time beeing only local database services are supported, the same way as calling zebraidx or zebrasrv from the command shell. In order to open a local Zebra database, with a specific configuration file, use
739
740   $sess = IDZebra::Session->new(configFile => 'demo/zebra.cfg');
741   $sess->open();
742
743 or
744
745   $sess = IDZebra::Session->open(configFile => 'demo/zebra.cfg');
746
747 where $sess is going to be the object representing a Zebra Session. Whenever this variable gets out of scope, the session is closed, together with all active transactions, etc... Anyway, if you'd like to close the session, just say:
748
749   $sess->close();
750
751 This will
752   - close all transactions
753   - destroy all result sets and scan lists 
754   - close the session
755
756 Note, that if I<shadow registers> are enabled, the changes will not be committed automatically.
757
758 In the future different database access methods are going to be available, 
759 like:
760
761   $sess = IDZebra::Session->open(server => 'ostrich.technomat.hu:9999');
762
763 You can also use the B<record group> arguments described below directly when calling the constructor, or the open method:
764
765   $sess = IDZebra::Session->open(configFile => 'demo/zebra.cfg',
766                                  groupName  => 'demo');
767
768
769 =head1 RECORD GROUPS 
770
771 If you manage different sets of records that share common characteristics, you can organize the configuration settings for each type into "groups". See the Zebra manual on the configuration file (zebra.cfg). 
772
773 For each open session a default record group is assigned. You can configure it in the constructor, or by the B<group> method:
774
775   $sess->group(groupName => ..., ...)
776
777 The following options are available:
778
779 =over 4
780
781 =item B<groupName>
782
783 This will select the named record group, and load the corresponding settings from the configuration file. All subsequent values will overwrite those...
784
785 =item B<databaseName>
786
787 The name of the (logical) database the updated records will belong to. 
788
789 =item B<path>
790
791 This path is used for directory updates (B<update>, B<delete> methods);
792  
793 =item B<recordId>
794
795 This option determines how to identify your records. See I<Zebra manual: Locating Records>
796
797 =item B<recordType>
798
799 The record type used for indexing. 
800
801 =item B<flagStoreData> 
802
803 Specifies whether the records should be stored internally in the Zebra system files. If you want to maintain the raw records yourself, this option should be false (0). If you want Zebra to take care of the records for you, it should be true(1). 
804
805 =item B<flagStoreKeys>
806
807 Specifies whether key information should be saved for a given group of records. If you plan to update/delete this type of records later this should be specified as 1; otherwise it should be 0 (default), to save register space. 
808
809 =item B<flagRw>
810
811 ?
812
813 =item B<fileVerboseLimit>
814
815 Skip log messages, when doing a directory update, and the specified number of files are processed...
816
817 =item B<databaseNamePath>
818
819 ?
820
821 =item B<explainDatabase>
822
823 The name of the explain database to be used
824
825 =item B<followLinks>              
826
827 Follow links when doing directory update.
828
829 =back
830
831 You can use the same parameters calling all update methods.
832
833 =head1 TRANSACTIONS (READ / WRITE LOCKS)
834
835 A transaction is a block of record update (insert / modify / delete) or retrieval procedures. So, all call to such function will implicitly start a transaction, unless one is already started by
836
837   $sess->begin_trans;
838
839 or 
840
841   $sess->begin_trans(TRANS_RW)
842
843 (these two are equivalents). The effect of this call is a kind of lock: if you call is a write lock is put on the registers, so other processes trying to update the database will be blocked. If there is already an RW (Read-Write) transaction opened by another process, the I<begin_trans> call will be blocked.
844
845 You can also use
846
847   $sess->begin_trans(TRANS_RO),
848
849 if you would like to put on a "read lock". This one is B<deprecated>, as while you have explicitly opened a transaction for read, you can't open another one for update. For example:
850
851   $sess->begin_trans(TRANS_RO);
852   $sess->begin_tran(TRANS_RW); # invalid, die here
853   $sess->end_trans;
854   $sess->end_trans;
855
856 is invalid, but
857
858   $sess->begin_tran(TRANS_RW); 
859   $sess->begin_trans(TRANS_RO);
860   $sess->end_trans;
861   $sess->end_trans;
862
863 is valid, but probably useless. Note again, that for each retrieval call, an RO transaction is opened. I<TRANS_RW> and I<TRANS_RO> are exported by default by IDZebra::Session.pm.
864
865 For multiple per-record I<updates> it's efficient to start transactions explicitly: otherwise registers (system files, vocabularies, etc..) are updated one by one. After finishing all requested updates, use
866
867   $stat = $sess->end_trans;
868
869 The return value is a ZebraTransactionStatus object, containing the following members as a hash reference:
870
871   $stat->{processed} # Number of records processed
872   $stat->{updated}   # Number of records processed
873   $stat->{deleted}   # Number of records processed
874   $stat->{inserted}  # Number of records processed
875   $stat->{stime}     # System time used
876   $stat->{utime}     # User time used
877
878 Normally, if the perl code dies due to some runtime error, or the session is closed, then the API attempts to close all pending transactions.
879
880 =head1 THE SHADOW REGISTERS
881
882 The Zebra server supports updating of the index structures. That is, you can add, modify, or remove records from databases managed by Zebra without rebuilding the entire index. Since this process involves modifying structured files with various references between blocks of data in the files, the update process is inherently sensitive to system crashes, or to process interruptions: Anything but a successfully completed update process will leave the register files in an unknown state, and you will essentially have no recourse but to re-index everything, or to restore the register files from a backup medium. Further, while the update process is active, users cannot be allowed to access the system, as the contents of the register files may change unpredictably. 
883
884 You can solve these problems by enabling the shadow register system in Zebra. During the updating procedure, zebraidx will temporarily write changes to the involved files in a set of "shadow files", without modifying the files that are accessed by the active server processes. If the update procedure is interrupted by a system crash or a signal, you simply repeat the procedure - the register files have not been changed or damaged, and the partially written shadow files are automatically deleted before the new updating procedure commences. 
885
886 At the end of the updating procedure (or in a separate operation, if you so desire), the system enters a "commit mode". First, any active server processes are forced to access those blocks that have been changed from the shadow files rather than from the main register files; the unmodified blocks are still accessed at their normal location (the shadow files are not a complete copy of the register files - they only contain those parts that have actually been modified). If the commit process is interrupted at any point during the commit process, the server processes will continue to access the shadow files until you can repeat the commit procedure and complete the writing of data to the main register files. You can perform multiple update operations to the registers before you commit the changes to the system files, or you can execute the commit operation at the end of each update operation. When the commit phase has completed successfully, any running server processes are instructed to switch their operations to the new, operational register, and the temporary shadow files are deleted. 
887
888 By default, (in the API !) the use of shadow registers is disabled. If zebra is configured that way (there is a "shadow" entry in zebra.cfg), then the shadow system can be enabled by calling:
889
890  $sess->shadow(1);
891
892 or disabled by
893
894  $sess->shadow(0);
895
896 If shadow system is enabled, then you have to commit changes you did, by calling:
897  
898  $sess->commit;
899
900 Note, that you can also determine shadow usage in the session constructor:
901
902  $sess = IDZebra::Session->open(configFile => 'demo/zebra.cfg',
903                                 shadow    => 1);
904  
905 Changes to I<shadow> will not have effect, within a I<transaction> (ie.: a transaction is started either with shadow enabled or disabled). For more details, read Zebra documentation: I<Safe Updating - Using Shadow Registers>.
906
907 =head1 UPDATING DATA
908
909 There are two ways to update data in a Zebra database using the perl API. You can update an entire directory structure just the way it's done by zebraidx:
910
911   $sess->update(path      =>  'lib');
912
913 This will update the database with the files in directory "lib", according to the current record group settings.
914
915   $sess->update();
916
917 This will update the database with the files, specified by the default record group setting. I<path> has to be specified there...
918
919   $sess->update(groupName => 'demo1',
920                 path      =>  'lib');
921
922 Update the database with files in "lib" according to the settings of group "demo1"
923
924   $sess->delete(groupName => 'demo1',
925                 path      =>  'lib');
926
927 Delete the records derived from the files in directory "lib", according to the "demo1" group settings. Sounds complex? Read zebra documentation about identifying records.
928
929 You can also update records one by one, even directly from the memory:
930
931   $sysno = $sess->update_record(data       => $rec1,
932                                 recordType => 'grs.perl.pod',
933                                 groupName  => "demo1");
934
935 This will update the database with the given record buffer. Note, that in this case recordType is explicitly specified, as there is no filename given, and for the demo1 group, no default record type is specified. The return value is the system assigned id of the record.
936
937 You can also index a single file:
938
939   $sysno = $sess->update_record(file => "lib/IDZebra/Data1.pm");
940
941 Or, provide a buffer, and a filename (where filename will only be used to identify the record, if configured that way, and possibly to find out it's record type):
942
943   $sysno = $sess->update_record(data => $rec1,
944                                 file => "lib/IDZebra/Data1.pm");
945
946 And some crazy stuff:
947
948   $sysno = $sess->delete_record(sysno => $sysno);
949
950 where sysno in itself is sufficient to identify the record
951
952   $sysno = $sess->delete_record(data => $rec1,
953                                 recordType => 'grs.perl.pod',
954                                 groupName  => "demo1");
955
956 This case the record is extracted, and if already exists, located in the database, then deleted... 
957
958   $sysno = $sess->update_record(data       => $rec1,
959                                 match      => $myid,
960                                 recordType => 'grs.perl.pod',
961                                 groupName  => "demo1");
962
963 Don't try this at home! This case, the record identifier string (which is normally generated according to the rules set in I<recordId> member of the record group, or in the I<recordId> parameter) is provided directly.... Looks much better this way:
964
965   $sysno = $sess->update_record(data          => $rec1,
966                                 databaseName  => 'books',
967                                 recordId      => '(bib1,ISBN)',
968                                 recordType    => 'grs.perl.pod',
969                                 flagStoreData => 1,
970                                 flagStoreKeys => 1);
971
972 You can notice, that it's not necessary to define a record group in zebra.cfg: you can do it "on the fly" in your code.
973
974 B<Important:> Note, that one record can be updated only once within a transaction - all subsequent updates are skipped. If you'd like to override this feature, use the I<force=E<gt>1> flag:
975
976   $sysno = $sess->update_record(data       => $rec1,
977                                 recordType => 'grs.perl.pod',
978                                 groupName  => "demo1",
979                                 force      => 1);
980
981 If you don't like to update the record, if it alerady exists, use the I<insert_record> method:
982
983   $sysno = $sess->insert_record(data       => $rec1,
984                                 recordType => 'grs.perl.pod',
985                                 groupName  => "demo1");
986
987 In this case, sysno will be -1, if the record could not be added, because there was already one in the database, with the same record identifier (generated according to the I<recordId> setting).
988
989 =head1 DATABASE SELECTION
990
991 Within a zebra repository you can define logical databases. You can either do this by record groups, or by providing the databaseName argument for update methods. For each record the database name it belongs to is stored. 
992
993 For searching, you can select databases by calling:
994
995   $sess->databases('db1','db2');
996
997 This will not do anything if the given and only the given databases are already selected. You can get the list of the actually selected databases, by calling:
998   
999   @dblist = $sess->databases();
1000
1001 =head1 SEARCHING
1002
1003 It's nice to be able to store data in your repository... But it's useful to reach it as well. So this is how to do searching:
1004
1005   $rs = $sess->search(databases => [qw(demo1,demo2)], # optional
1006                       pqf       => '@attr 1=4 computer');
1007
1008 This is going to execute a search in databases demo1 and demo2, for title 'com,puter'. This is a PQF (Prefix Query Format) search, see YAZ documentation for details. The database selection is optional: if it's provided, the given list of databases is selected for this particular search, then the original selection is restored.
1009
1010 =head2 CCL searching
1011
1012 Not all users enjoy typing in prefix query structures and numerical attribute values, even in a minimalistic test client. In the library world, the more intuitive Common Command Language (or ISO 8777) has enjoyed some popularity - especially before the widespread availability of graphical interfaces. It is still useful in applications where you for some reason or other need to provide a symbolic language for expressing boolean query structures. 
1013
1014 The CCL searching is not currently supported by this API.
1015
1016 =head2 CQL searching
1017
1018 CQL - Common Query Language - was defined for the SRW protocol. In many ways CQL has a similar syntax to CCL. The objective of CQL is different. Where CCL aims to be an end-user language, CQL is the protocol query language for SRW. 
1019
1020 In order to map CQL queries to Zebra internal search structures, you have to define a mapping, the way it is described in YAZ documentation: I<Specification of CQL to RPN mapping>. The mapping is interpreted by the method:
1021
1022   $sess->cqlmap($mapfile);
1023
1024 Or, you can directly provide the I<mapfile> parameter for the search:
1025
1026   $rs = $sess->search(cqlmap    => 'demo/cql.map',
1027                       cql       => 'dc.title=IDZebra');
1028
1029 As you see, CQL searching is so simple: just give the query in the I<cql> parameter.
1030
1031 =head2 Sorting
1032
1033 If you'd like the search results to be sorted, use the I<sort> parameter:
1034
1035   $rs = $sess->search(cql       => 'IDZebra',
1036                       sort      => '1=4 ia');
1037   
1038 Note, that B<currently> this is (almost) equivalent to
1039
1040   $rs = $sess->search(cql       => 'IDZebra');
1041   $rs->sort('1=4 ia');
1042   
1043 but in the further versions of Zebra and this API a single phase search and sort will take place, optimizing performance. For more details on sorting, see I<IDZebra::ResultSet> manpage.
1044
1045 =head1 RESULTSETS
1046
1047 As you have seen, the result of the search request is a I<Resultset> object.
1048 It contains number of hits, and search status, and can be used to sort and retrieve the resulting records.
1049
1050   $count = $rs->count;
1051
1052   printf ("RS Status is %d (%s)\n", $rs->errCode, $rs->errString);
1053
1054 I<$rs-E<gt>errCode> is 0, if there were no errors during search. Read the I<IDZebra::Resultset> manpage for more details.
1055
1056 =head1 SCANNING
1057
1058 Zebra supports scanning index values. The result of the 
1059
1060   $sl = $sess->scan(expression => "a");
1061
1062 call is an I<IDZebra::ScanList> object, what you can use to list the values. The scan expression has to be provided in a PQF like format. Examples:
1063
1064 B< a> (scan trough words of "default", "Any" indexes)
1065
1066
1067 B< @attr 1=1016 a> (same effect)
1068
1069
1070 B< @attr 1=4 @attr 6=2 a>  (scan trough titles as phrases)
1071
1072 An illegal scan expression will cause your code to die. If you'd like to select databases just for the scan call, you can optionally use the I<databases> parameter:
1073
1074   $sl = $sess->scan(expression => "a",
1075                     databases  => [qw(demo1 demo2)]);
1076   
1077 You can use the I<IDZebra::ScanList> object returned by the i<scan> method, to reach the result. Check I<IDZebra::ScanList> manpage for more details.
1078
1079 =head1 SESSION STATUS AND ERRORS
1080
1081 Most of the API calls causes die, if an error occures. You avoid this, by using eval {} blocks. The following methods are available to get the status of Zebra service:
1082
1083 =over 4
1084
1085 =item B<errCode>
1086
1087 The Zebra provided error code... (for the result of the last call);
1088
1089 =item B<errString>
1090
1091 Error string corresponding to the message
1092
1093 =item B<errAdd>
1094
1095 Additional information for the status
1096
1097 =back
1098
1099 This functionality may change, see TODO.
1100
1101 =head1 LOGGING AND MISC. FUNCTIONS
1102
1103 Zebra provides logging facility for the internal events, and also for application developers trough the API. See manpage I<IDZebra::Logger> for details.
1104
1105 =over 4
1106
1107 =item B<IDZebra::LogFile($filename)>
1108
1109 Will set the output file for logging. By default it's STDERR;
1110
1111 =item B<IDZebra::LogLevel(15)>
1112
1113 Set log level. 0 for no logs. See IDZebra::Logger for usable flags.
1114
1115 =back
1116
1117 Some other functions
1118
1119 =over 4
1120
1121 =item B<$sess-E<gt>init>
1122
1123 Initialize, and clean registers. This will remove all data!
1124
1125 =item B<$sess-E<gt>compact>
1126
1127 Compact the registers (? does this work)
1128
1129 =item B<$sess-E<gt>show>
1130
1131 Doesn't have too much meaning. Don't try :)
1132
1133 =back
1134
1135 =head1 TODO
1136
1137 =over 4
1138
1139 =item B<Clean up error handling>
1140
1141 By default all zebra errors should cause die. (such situations could be avoided by using eval {}), and then check for errCode, errString... An optional flag or package variable should be introduced to override this, and skip zebra errors, to let the user decide what to do.
1142
1143 =item B<Make the package self-distributable>
1144
1145 Build and link with installed header and library files
1146
1147 =item B<Testing>
1148
1149 Test shadow system, unicode...
1150
1151 =item B<C API>
1152
1153 Cleanup, arrange, remove redundancy
1154
1155 =back
1156
1157 =head1 COPYRIGHT
1158
1159 Fill in
1160
1161 =head1 AUTHOR
1162
1163 Peter Popovics, pop@technomat.hu
1164
1165 =head1 SEE ALSO
1166
1167 Zebra documentation, Zebra::ResultSet, Zebra::ScanList, Zebra::Logger manpages
1168
1169 =cut