Removed old stuff, and done some cleanup in the documentation, and
[idzebra-moved-to-github.git] / perl / lib / IDZebra / Session.pm
1 # $Id: Session.pm,v 1.5 2003-02-28 18:38:29 pop Exp $
2
3 # Zebra perl API header
4 # =============================================================================
5 use strict;
6 package IDZebra::Session;
7 use IDZebra;
8 use IDZebra::Logger qw(:flags :calls);
9 use IDZebra::Resultset;
10 use Scalar::Util;
11 use Carp;
12 use strict;
13 our @ISA = qw(IDZebra::Logger);
14
15 1;
16 # -----------------------------------------------------------------------------
17 # Class constructors, destructor
18 # -----------------------------------------------------------------------------
19 sub new {
20     my ($proto, %args) = @_;
21     my $class = ref($proto) || $proto;
22     my $self = {};
23     $self->{args} = \%args;
24     
25     bless ($self, $class);
26     $self->{cql_ct} = undef;
27     return ($self);
28
29     $self->{databases} = {};
30 }
31
32 sub start_service {
33     my ($self, %args) = @_;
34
35     my $zs;
36     unless (defined($self->{zs})) {
37         if (defined($args{'configFile'})) {
38             $self->{zs} = IDZebra::start($args{'configFile'});
39         } else {
40             $self->{zs} = IDZebra::start("zebra.cfg");
41         }
42     }
43 }
44
45 sub stop_service {
46     my ($self) = @_;
47     if (defined($self->{zs})) {
48         IDZebra::stop($self->{zs}) if ($self->{zs});    
49         $self->{zs} = undef;
50     }
51 }
52
53
54 sub open {
55     my ($proto,%args) = @_;
56     my $self = {};
57
58     if (ref($proto)) { $self = $proto; } else { 
59         $self = $proto->new(%args);
60     }
61
62     unless (%args) {
63         %args = %{$self->{args}};
64     }
65
66     $self->start_service(%args);
67
68     unless (defined($self->{zs})) {
69         croak ("Falied to open zebra service");
70     }    
71
72     unless (defined($self->{zh})) {
73         $self->{zh}=IDZebra::open($self->{zs}); 
74     }   
75
76     # Reset result set counter
77     $self->{rscount} = 0;
78
79     # This is needed in order to somehow initialize the service
80     $self->databases("Default");
81
82     # Load the default configuration
83     $self->group(%args);
84     
85     $self->{odr_input} = IDZebra::odr_createmem($IDZebra::ODR_DECODE);
86     $self->{odr_output} = IDZebra::odr_createmem($IDZebra::ODR_ENCODE);
87
88     return ($self);
89 }
90
91 sub close {
92     my ($self) = @_;
93
94     if ($self->{zh}) {
95         while (IDZebra::trans_no($self->{zh}) > 0) {
96             logf (LOG_WARN,"Explicitly closing transaction with session");
97             $self->end_trans;
98         }
99
100         IDZebra::close($self->{zh});
101         $self->{zh} = undef;
102     }
103     
104     if ($self->{odr_input}) {
105         IDZebra::odr_reset($self->{odr_input});
106         IDZebra::odr_destroy($self->{odr_input});
107         $self->{odr_input} = undef;  
108     }
109
110     if ($self->{odr_output}) {
111         IDZebra::odr_reset($self->{odr_output});
112         IDZebra::odr_destroy($self->{odr_output});
113         $self->{odr_output} = undef;  
114     }
115
116     $self->stop_service;
117 }
118
119 sub DESTROY {
120     my ($self) = @_;
121     logf (LOG_LOG,"DESTROY $self");
122     $self->close; 
123
124     if (defined ($self->{cql_ct})) {
125       IDZebra::cql_transform_close($self->{cql_ct});
126     }
127 }
128 # -----------------------------------------------------------------------------
129 # Record group selection  This is a bit nasty... but used at many places 
130 # -----------------------------------------------------------------------------
131 sub group {
132     my ($self,%args) = @_;
133     if ($#_ > 0) {
134         $self->{rg} = $self->_makeRecordGroup(%args);
135         $self->_selectRecordGroup($self->{rg});
136     }
137     return($self->{rg});
138 }
139
140 sub selectRecordGroup {
141     my ($self, $groupName) = @_;
142     $self->{rg} = $self->_getRecordGroup($groupName);
143     $self->_selectRecordGroup($self->{rg});
144 }
145
146 sub _displayRecordGroup {
147     my ($self, $rg) = @_;
148     print STDERR "-----\n";
149     foreach my $key qw (groupName 
150                         databaseName 
151                         path recordId 
152                         recordType 
153                         flagStoreData 
154                         flagStoreKeys 
155                         flagRw 
156                         fileVerboseLimit 
157                         databaseNamePath 
158                         explainDatabase 
159                         followLinks) {
160         print STDERR "$key:",$rg->{$key},"\n";
161     }
162 }
163
164 sub _cloneRecordGroup {
165     my ($self, $orig) = @_;
166     my $rg = IDZebra::recordGroup->new();
167     my $r = IDZebra::init_recordGroup($rg);
168     foreach my $key qw (groupName 
169                         databaseName 
170                         path 
171                         recordId 
172                         recordType 
173                         flagStoreData 
174                         flagStoreKeys 
175                         flagRw 
176                         fileVerboseLimit 
177                         databaseNamePath 
178                         explainDatabase 
179                         followLinks) {
180         $rg->{$key} = $orig->{$key} if ($orig->{$key});
181     }
182     return ($rg);
183 }
184
185 sub _getRecordGroup {
186     my ($self, $groupName, $ext) = @_;
187     my $rg = IDZebra::recordGroup->new();
188     my $r = IDZebra::init_recordGroup($rg);
189     $rg->{groupName} = $groupName if ($groupName ne "");  
190     $ext = "" unless ($ext);
191     my $r = IDZebra::res_get_recordGroup($self->{zh}, $rg, $ext);
192     return ($rg);
193 }
194
195 sub _makeRecordGroup {
196     my ($self, %args) = @_;
197     my $rg;
198
199     my @keys = keys(%args);
200     unless ($#keys >= 0) {
201         return ($self->{rg});
202     }
203
204     if ($args{groupName}) {
205         $rg = $self->_getRecordGroup($args{groupName});
206     } else {
207         $rg = $self->_cloneRecordGroup($self->{rg});
208     }
209     $self->_setRecordGroupOptions($rg, %args);
210     return ($rg);
211 }
212
213 sub _setRecordGroupOptions {
214     my ($self, $rg, %args) = @_;
215
216     foreach my $key qw (databaseName 
217                         path 
218                         recordId 
219                         recordType 
220                         flagStoreData 
221                         flagStoreKeys 
222                         flagRw 
223                         fileVerboseLimit 
224                         databaseNamePath 
225                         explainDatabase 
226                         followLinks) {
227         if (defined ($args{$key})) {
228             $rg->{$key} = $args{$key};
229         }
230     }
231 }
232 sub _selectRecordGroup {
233     my ($self, $rg) = @_;
234     my $r = IDZebra::set_group($self->{zh}, $rg);
235     my $dbName;
236     unless ($dbName = $rg->{databaseName}) {
237         $dbName = 'Default';
238     }
239     unless ($self->databases($dbName)) {
240         croak("Fatal error selecting database $dbName");
241     }
242 }
243 # -----------------------------------------------------------------------------
244 # Selecting databases for search (and also for updating - internally)
245 # -----------------------------------------------------------------------------
246 sub databases {
247     my ($self, @databases) = @_;
248
249     unless ($#_ >0) {
250         return (keys(%{$self->{databases}}));
251     }
252
253     my %tmp;
254
255     my $changed = 0;
256     foreach my $db (@databases) {
257         next if ($self->{databases}{$db});
258         $tmp{$db}++;
259         $changed++;
260     }
261
262     foreach my $db (keys (%{$self->{databases}})) {
263         $changed++ unless ($tmp{$db});
264     }
265
266     if ($changed) {
267
268         delete ($self->{databases});
269         foreach my $db (@databases) {
270             $self->{databases}{$db}++;
271         }
272
273         if (IDZebra::select_databases($self->{zh}, 
274                                                 ($#databases + 1), 
275                                                 \@databases)) {
276             logf(LOG_FATAL, 
277                  "Could not select database(s) %s errCode=%d",
278                  join(",",@databases),
279                  $self->errCode());
280             return (0);
281         } else {
282             logf(LOG_LOG,"Database(s) selected: %s",join(",",@databases));
283         }
284     }
285     return (keys(%{$self->{databases}}));
286 }
287
288 # -----------------------------------------------------------------------------
289 # Error handling
290 # -----------------------------------------------------------------------------
291 sub errCode {
292     my ($self) = @_;
293     return(IDZebra::errCode($self->{zh}));
294 }
295
296 sub errString {
297     my ($self) = @_;
298     return(IDZebra::errString($self->{zh}));
299 }
300
301 sub errAdd {
302     my ($self) = @_;
303     return(IDZebra::errAdd($self->{zh}));
304 }
305
306 # -----------------------------------------------------------------------------
307 # Transaction stuff
308 # -----------------------------------------------------------------------------
309 sub begin_trans {
310     my ($self) = @_;
311     IDZebra::begin_trans($self->{zh});
312 }
313
314 sub end_trans {
315     my ($self) = @_;
316     my $stat = IDZebra::ZebraTransactionStatus->new();
317     IDZebra::end_trans($self->{zh}, $stat);
318     return ($stat);
319 }
320
321 sub begin_read {
322     my ($self) =@_;
323     return(IDZebra::begin_read($self->{zh}));
324 }
325
326 sub end_read {
327     my ($self) =@_;
328     IDZebra::end_read($self->{zh});
329 }
330
331 sub shadow_enable {
332     my ($self, $value) = @_;
333     if ($#_ > 0) { IDZebra::set_shadow_enable($self->{zh},$value); }
334     return (IDZebra::get_shadow_enable($self->{zh}));
335 }
336
337 sub commit {
338     my ($self) = @_;
339     if ($self->shadow_enable) {
340         return(IDZebra::commit($self->{zh}));
341     }
342 }
343
344 # -----------------------------------------------------------------------------
345 # We don't really need that...
346 # -----------------------------------------------------------------------------
347 sub odr_reset {
348     my ($self, $name) = @_;
349     if ($name !~/^(input|output)$/) {
350         croak("Undefined ODR '$name'");
351     }
352   IDZebra::odr_reset($self->{"odr_$name"});
353 }
354
355 # -----------------------------------------------------------------------------
356 # Init/compact
357 # -----------------------------------------------------------------------------
358 sub init {
359     my ($self) = @_;
360     return(IDZebra::init($self->{zh}));
361 }
362
363 sub compact {
364     my ($self) = @_;
365     return(IDZebra::compact($self->{zh}));
366 }
367
368 sub update {
369     my ($self, %args) = @_;
370     my $rg = $self->_update_args(%args);
371     $self->_selectRecordGroup($rg);
372     $self->begin_trans;
373     IDZebra::repository_update($self->{zh});
374     $self->_selectRecordGroup($self->{rg});
375     $self->end_trans;
376 }
377
378 sub delete {
379     my ($self, %args) = @_;
380     my $rg = $self->_update_args(%args);
381     $self->_selectRecordGroup($rg);
382     $self->begin_trans;
383     IDZebra::repository_delete($self->{zh});
384     $self->_selectRecordGroup($self->{rg});
385     $self->end_trans;
386 }
387
388 sub show {
389     my ($self, %args) = @_;
390     my $rg = $self->_update_args(%args);
391     $self->_selectRecordGroup($rg);
392     $self->begin_trans;
393     IDZebra::repository_show($self->{zh});
394     $self->_selectRecordGroup($self->{rg});
395     $self->end_trans;
396 }
397
398 sub _update_args {
399     my ($self, %args) = @_;
400     my $rg = $self->_makeRecordGroup(%args);
401     $self->_selectRecordGroup($rg);
402     return ($rg);
403 }
404
405 # -----------------------------------------------------------------------------
406 # Per record update
407 # -----------------------------------------------------------------------------
408
409 sub update_record {
410     my ($self, %args) = @_;
411     return(IDZebra::update_record($self->{zh},
412                                   $self->_record_update_args(%args)));
413 }
414
415 sub delete_record {
416     my ($self, %args) = @_;
417     return(IDZebra::delete_record($self->{zh},
418                                   $self->_record_update_args(%args)));
419 }
420 sub _record_update_args {
421     my ($self, %args) = @_;
422
423     my $sysno   = $args{sysno}      ? $args{sysno}      : 0;
424     my $match   = $args{match}      ? $args{match}      : "";
425     my $rectype = $args{recordType} ? $args{recordType} : "";
426     my $fname   = $args{file}       ? $args{file}       : "<no file>";
427
428     my $buff;
429
430     if ($args{data}) {
431         $buff = $args{data};
432     } 
433     elsif ($args{file}) {
434         open (F, $args{file}) || warn ("Cannot open $args{file}");
435         $buff = join('',(<F>));
436         close (F);
437     }
438     my $len = length($buff);
439
440     delete ($args{sysno});
441     delete ($args{match});
442     delete ($args{recordType});
443     delete ($args{file});
444     delete ($args{data});
445
446     my $rg = $self->_makeRecordGroup(%args);
447
448     # If no record type is given, then try to find it out from the
449     # file extension;
450     unless ($rectype) {
451         if (my ($ext) = $fname =~ /\.(\w+)$/) {
452             my $rg2 = $self->_getRecordGroup($rg->{groupName},$ext);
453             $rectype = $rg2->{recordType};
454         } 
455     }
456
457     $rg->{databaseName} = "Default" unless ($rg->{databaseName});
458
459 #    print STDERR "$rectype,$sysno,$match,$fname,$len\n";
460     unless ($rectype) {
461         $rectype="";
462     }
463     return ($rg, $rectype, $sysno, $match, $fname, $buff, $len);
464 }
465
466 # -----------------------------------------------------------------------------
467 # CQL stuff
468 sub cqlmap {
469     my ($self,$mapfile) = @_;
470     if ($#_ > 0) {
471         if ($self->{cql_mapfile} ne $mapfile) {
472             unless (-f $mapfile) {
473                 croak("Cannot find $mapfile");
474             }
475             if (defined ($self->{cql_ct})) {
476               IDZebra::cql_transform_close($self->{cql_ct});
477             }
478             $self->{cql_ct} = IDZebra::cql_transform_open_fname($mapfile);
479             $self->{cql_mapfile} = $mapfile;
480         }
481     }
482     return ($self->{cql_mapfile});
483 }
484
485 sub cql2pqf {
486     my ($self, $cqlquery) = @_;
487     unless (defined($self->{cql_ct})) {
488         croak("CQL map file is not specified yet.");
489     }
490     my $res = "\0" x 2048;
491     my $r = IDZebra::cql2pqf($self->{cql_ct}, $cqlquery, $res, 2048);
492     unless ($r) {return (undef)};
493     $res=~s/\0.+$//g;
494     return ($res); 
495 }
496
497
498 # -----------------------------------------------------------------------------
499 # Search 
500 # -----------------------------------------------------------------------------
501 sub search {
502     my ($self, %args) = @_;
503
504     if ($args{cqlmap}) { $self->cqlmap($args{cqlmap}); }
505
506     my $query;
507     if ($args{pqf}) {
508         $query = $args{pqf};
509     }
510     elsif ($args{cql}) {
511         unless ($query = $self->cql2pqf($args{cql})) {
512             croak ("Invalid CQL query: '$args{cql}'");
513         }
514     }
515     unless ($query) {
516         croak ("No query given to search");
517     }
518
519     my @origdbs;
520
521     if ($args{databases}) {
522         @origdbs = $self->databases;
523         $self->databases(@{$args{databases}});
524     }
525
526     my $rsname = $args{rsname} ? $args{rsname} : $self->_new_setname;
527
528     my $rs = $self->_search_pqf($query, $rsname);
529
530     if ($args{databases}) {
531         $self->databases(@origdbs);
532     }
533
534     return ($rs);
535 }
536
537 sub _new_setname {
538     my ($self) = @_;
539     return ("set_".$self->{rscount}++);
540 }
541
542 sub _search_pqf {
543     my ($self, $query, $setname) = @_;
544
545     my $hits = IDZebra::search_PQF($self->{zh},
546                                    $self->{odr_input},
547                                    $self->{odr_output},
548                                    $query,
549                                    $setname);
550
551     my $rs  = IDZebra::Resultset->new($self,
552                                       name        => $setname,
553                                       recordCount => $hits,
554                                       errCode     => $self->errCode,
555                                       errString   => $self->errString);
556     return($rs);
557 }
558
559 # -----------------------------------------------------------------------------
560 # Sort
561 #
562 # Sorting of multiple result sets is not supported by zebra...
563 # -----------------------------------------------------------------------------
564
565 sub sortResultsets {
566     my ($self, $sortspec, $setname, @sets) = @_;
567
568     my @setnames;
569     my $count = 0;
570     foreach my $rs (@sets) {
571         push (@setnames, $rs->{name});
572         $count += $rs->{recordCount};  # is this really sure ??? It doesn't 
573                                        # matter now...
574     }
575
576     my $status = IDZebra::sort($self->{zh},
577                                $self->{odr_output},
578                                $sortspec,
579                                $setname,
580                                \@setnames);
581
582     my $errCode = $self->errCode;
583     my $errString = $self->errString;
584
585     if ($status || $errCode) {$count = 0;}
586
587     my $rs  = IDZebra::Resultset->new($self,
588                                       name        => $setname,
589                                       recordCount => $count,
590                                       errCode     => $errCode,
591                                       errString   => $errString);
592     
593     return ($rs);
594 }
595
596 # ============================================================================
597
598
599 __END__
600
601 =head1 NAME
602
603 IDZebra::Session - A Zebra database server session for update and retrieval
604
605 =head1 SYNOPSIS
606
607   $sess = IDZebra::Session->new(configFile => 'demo/zebra.cfg');
608   $sess->open();
609
610   $sess = IDZebra::Session->open(configFile => 'demo/zebra.cfg',
611                                  groupName  => 'demo1');
612
613   $sess->group(groupName => 'demo2');
614
615   $sess->init();
616
617   $sess->begin_trans;
618
619   $sess->update(path      =>  'lib');
620
621   my $s1=$sess->update_record(data       => $rec1,
622                               recordType => 'grs.perl.pod',
623                               groupName  => "demo1",
624                               );
625
626   my $stat = $sess->end_trans;
627
628   $sess->databases('demo1','demo2');
629
630   my $rs1 = $sess->search(cqlmap    => 'demo/cql.map',
631                           cql       => 'dc.title=IDZebra',
632                           databases => [qw(demo1 demo2)]);
633   $sess->close;
634
635 =head1 DESCRIPTION
636
637 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. 
638
639 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. 
640
641 =head1 OPENING AND CLOSING A ZEBRA SESSIONS
642
643 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
644
645   $sess = IDZebra::Session->new(configFile => 'demo/zebra.cfg');
646   $sess->open();
647
648 or
649
650   $sess = IDZebra::Session->open(configFile => 'demo/zebra.cfg');
651
652 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:
653
654   $sess->close();
655
656 This will
657   - close all transactions
658   - destroy all result sets
659   - close the session
660
661 In the future different database access methods are going to be available, 
662 like:
663
664   $sess = IDZebra::Session->open(server => 'ostrich.technomat.hu:9999');
665
666 You can also use the B<record group> arguments described below directly when calling the constructor, or the open method:
667
668   $sess = IDZebra::Session->open(configFile => 'demo/zebra.cfg',
669                                  groupName  => 'demo');
670
671
672 =head1 RECORD GROUPS 
673
674 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). 
675
676 For each open session a default record group is assigned. You can configure it in the constructor, or by the B<set_group> method:
677
678   $sess->group(groupName => ..., ...)
679
680 The following options are available:
681
682 =over 4
683
684 =item B<groupName>
685
686 This will select the named record group, and load the corresponding settings from the configuration file. All subsequent values will overwrite those...
687
688 =item B<databaseName>
689
690 The name of the (logical) database the updated records will belong to. 
691
692 =item B<path>
693
694 This path is used for directory updates (B<update>, B<delete> methods);
695  
696 =item B<recordId>
697
698 This option determines how to identify your records. See I<Zebra manual: Locating Records>
699
700 =item B<recordType>
701
702 The record type used for indexing. 
703
704 =item B<flagStoreData> 
705
706 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). 
707
708 =item B<flagStoreKeys>
709
710 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. 
711
712 =item B<flagRw>
713
714 ?
715
716 =item B<fileVerboseLimit>
717
718 Skip log messages, when doing a directory update, and the specified number of files are processed...
719
720 =item B<databaseNamePath>
721
722 ?
723
724 =item B<explainDatabase>
725
726 The name of the explain database to be used
727
728 =item B<followLinks>              
729
730 Follow links when doing directory update.
731
732 =back
733
734 You can use the same parameters calling all update methods.
735
736 =head1 TRANSACTIONS (WRITE LOCKS)
737
738 A transaction is a block of record update (insert / modify / delete) procedures. So, all call to such function will implicitly start a transaction, unless one is started by
739
740   $sess->begin_trans;
741
742 For multiple per record 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
743
744   $stat = $sess->end_trans;
745
746 The return value is a ZebraTransactionStatus object, containing the following members as a hash reference:
747
748   $stat->{processed} # Number of records processed
749   $stat->{updated}   # Number of records processed
750   $stat->{deleted}   # Number of records processed
751   $stat->{inserted}  # Number of records processed
752   $stat->{stime}     # System time used
753   $stat->{utime}     # User time used
754
755 =head1 UPDATING DATA
756
757 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:
758
759   $sess->update(path      =>  'lib');
760
761 This will update the database with the files in directory "lib", according to the current record group settings.
762
763   $sess->update();
764
765 This will update the database with the files, specified by the default record group setting. I<path> has to be specified there...
766
767   $sess->update(groupName => 'demo1',
768                 path      =>  'lib');
769
770 Update the database with files in "lib" according to the settings of group "demo1"
771
772   $sess->delete(groupName => 'demo1',
773                 path      =>  'lib');
774
775 Delete the records derived from the files in directory "lib", according to the "demo1" group settings. Sounds complex? Read zebra documentation about identifying records.
776
777 You can also update records one by one, even directly from the memory:
778
779   $sysno = $sess->update_record(data       => $rec1,
780                                 recordType => 'grs.perl.pod',
781                                 groupName  => "demo1");
782
783 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.
784
785 You can also index a single file:
786
787   $sysno = $sess->update_record(file => "lib/IDZebra/Data1.pm");
788
789 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):
790
791   $sysno = $sess->update_record(data => $rec1,
792                                 file => "lib/IDZebra/Data1.pm");
793
794 And some crazy stuff:
795
796   $sysno = $sess->delete_record(sysno => $sysno);
797
798 where sysno in itself is sufficient to identify the record
799
800   $sysno = $sess->delete_record(data => $rec1,
801                                 recordType => 'grs.perl.pod',
802                                 groupName  => "demo1");
803
804 This case the record is extracted, and if already exists, located in the database, then deleted... 
805
806   $sysno = $sess->delete_record(data       => $rec1,
807                                 match      => $myid,
808                                 recordType => 'grs.perl.pod',
809                                 groupName  => "demo1");
810
811 Don't try this at home! This case, the record identifier string (which is normally generated according to the rules set in recordId directive of zebra.cfg) is provided directly....
812
813
814 B<Important:> Note, that one record can be updated only once within a transaction - all subsequent updates are skipped. 
815
816 =head1 DATABASE SELECTION
817
818 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. 
819
820 For searching, you can select databases by calling:
821
822   $sess->databases('db1','db2');
823
824 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:
825   
826   @dblist = $sess->databases();
827
828 =head1 SEARCHING
829
830 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:
831
832   $rs = $sess->search(databases => [qw(demo1,demo2)], # optional
833                       pqf       => '@attr 1=4 computer');
834
835 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.
836
837 =head2 CCL searching
838
839 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. 
840
841 The CCL searching is not currently supported by this API.
842
843 =head2 CQL searching
844
845 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. 
846
847 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:
848
849   $sess->cqlmap($mapfile);
850
851 Or, you can directly provide the I<mapfile> parameter for the search:
852
853   my $rs1 = $sess->search(cqlmap    => 'demo/cql.map',
854                           cql       => 'dc.title=IDZebra');
855
856 As you see, CQL searching is so simple: just give the query in the I<cql> parameter.
857
858 =head1 RESULTSETS
859
860 As you have seen, the result of the search request is a I<Resultset> object.
861 It contains number of hits, and search status, and can be used to sort and retrieve the resulting records.
862
863   $count = $rs->count;
864
865   printf ("RS Status is %d (%s)\n", $rs->errCode, $rs->errString);
866
867 I<$rs-E<gt>errCode> is 0, if there were no errors during search. Read the I<IDZebra::Resultset> manpage for more details.
868
869 =head1 MISC FUNCTIONS
870
871 =head1 COPYRIGHT
872
873 Fill in
874
875 =head1 AUTHOR
876
877 Peter Popovics, pop@technomat.hu
878
879 =head1 SEE ALSO
880
881 IDZebra, IDZebra::Data1, Zebra documentation
882
883 =cut