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