]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher.pm
added limit/offset capability to search_where
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher.pm
1 package OpenILS::Application::Storage::Publisher;
2 use base qw/OpenILS::Application::Storage/;
3 our $VERSION = 1;
4
5 use Digest::MD5 qw/md5_hex/;
6 use OpenSRF::EX qw/:try/;
7 use OpenSRF::Utils;
8 use OpenSRF::Utils::Logger qw/:level/;
9 use OpenILS::Utils::Fieldmapper;
10
11 my $log = 'OpenSRF::Utils::Logger';
12
13
14 sub register_method {
15         my $class = shift;
16         my %args = @_;
17         my %dup_args = %args;
18
19         $class = ref($class) || $class;
20
21         $args{package} ||= $class;
22         __PACKAGE__->SUPER::register_method( %args );
23
24         if (exists($dup_args{cachable}) and $dup_args{cachable}) {
25                 (my $name = $dup_args{api_name}) =~ s/^open-ils\.storage/open-ils.storage.cachable/o;
26                 if ($name ne $dup_args{api_name}) {
27                         $dup_args{real_api_name} = $dup_args{api_name};
28                         $dup_args{method} = 'cachable_wrapper';
29                         $dup_args{api_name} = $name;
30                         $dup_args{package} = __PACKAGE__;
31                         __PACKAGE__->SUPER::register_method( %dup_args );
32                 }
33         }
34 }
35
36 sub cachable_wrapper {
37         my $self = shift;
38         my $client = shift;
39         my @args = @_;
40
41         my %cache_args = (
42                 limit           => 100,
43                 offset          => 0,
44                 timeout         => 7200,
45                 cache_page_size => 1000,
46         );
47
48         my @real_args;
49         my $key_string = $self->api_name;
50         for (my $ind = 0; $ind < scalar(@args); $ind++) {
51                 if (    $args[$ind] eq 'limit' ||
52                         $args[$ind] eq 'offset' ||
53                         $args[$ind] eq 'cache_page_size' ||
54                         $args[$ind] eq 'timeout' ) {
55
56                         my $key_ind = $ind;
57                         $ind++;
58                         my $value_ind = $ind;
59                         $cache_args{$args[$key_ind]} = $args[$value_ind];
60                         $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
61                         next;
62                 }
63                 $key_string .= $args[$ind];
64                 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
65                 push @real_args, $args[$ind];
66         }
67
68         my $cache_page = int($cache_args{offset} / $cache_args{cache_page_size});
69         my $cache_key;
70         {       use bytes;
71                 $cache_key = md5_hex($key_string.$cache_page);
72         }
73
74         $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
75         $log->debug("Cache page is $cache_page", DEBUG);
76
77         my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
78         if (defined $cached_res) {
79                 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
80                 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
81                 my $start = int($cache_args{offset} - ($cache_page * $cache_args{cache_page_size}));
82                 my $end = int($start + $cache_args{limit} - 1);
83                 $log->debug("Responding with values from ".$start.' to '.$end,DEBUG);
84                 $client->respond( $_ ) for ( grep { defined } @$cached_res[ $start .. $end ]);
85                 return undef;
86         }
87
88         my $method = $self->method_lookup($self->{real_api_name});
89         my @res = $method->run(@real_args);
90
91
92         $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
93
94         $log->debug("Saving values from ".int($cache_page * $cache_args{cache_page_size})." to ".
95                 int(($cache_page + 1) * $cache_args{cache_page_size}). "to the cache", INTERNAL);
96         try {
97                 OpenSRF::Utils::Cache->new->put_cache(
98                         $cache_key =>
99                         [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
100                         OpenSRF::Utils->interval_to_seconds( $cache_args{timeout} )
101                 );
102         } catch Error with {
103                 my $e = shift;
104                 $log->error("Cache seems to be down, $e");
105         };
106
107         return undef;
108 }
109
110 sub retrieve_node {
111         my $self = shift;
112         my $client = shift;
113         my @ids = @_;
114
115         my $cdbi = $self->{cdbi};
116
117         for my $id ( @ids ) {
118                 next unless ($id);
119
120                 my ($rec) = $cdbi->fast_fieldmapper($id);
121                 if ($self->api_name !~ /batch/o) {
122                         return $rec if ($rec);
123                 }
124                 $client->respond($rec);
125         }
126         return undef;
127 }
128
129 sub search_where {
130         my $self = shift;
131         my $client = shift;
132         my @args = @_;
133
134         if (ref($args[0]) eq 'HASH') {
135                 if ($args[1]) {
136                         $args[1]{limit_dialect} = $self->{cdbi}->db_Main;
137                 } else {
138                         $args[1] = {limit_dialect => $self->{cdbi}->db_Main };
139                 }
140         } else {
141                 $args[0] = { @args };
142                 $args[1] = {limit_dialect => $self->{cdbi} };
143         }
144
145         my $cdbi = $self->{cdbi};
146
147         for my $obj ($cdbi->search_where(@args)) {
148                 next unless ref($obj);
149                 $client->respond( $obj->to_fieldmapper );
150         }
151         return undef;
152 }
153
154 sub search {
155         my $self = shift;
156         my $client = shift;
157         my @args = @_;
158
159         my $cdbi = $self->{cdbi};
160
161         (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
162
163         for my $obj ($cdbi->$search_type(@args)) {
164                 next unless ref($obj);
165                 $client->respond( $obj->to_fieldmapper );
166         }
167         return undef;
168 }
169
170 sub search_one_field {
171         my $self = shift;
172         my $client = shift;
173         my @args = @_;
174
175         (my $field = $self->api_name) =~ s/.*\.([^\.]+)$/$1/o;
176
177         return search( $self, $client, $field, @args );
178 }
179
180 sub old_search_one_field {
181         my $self = shift;
182         my $client = shift;
183         my @terms = @_;
184
185         (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
186         (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
187         my $cdbi = $self->{cdbi};
188
189         my $like = 0;
190         $like = 1 if ($search_type =~ /like$/o);
191         $like = 2 if ($search_type =~ /fts$/o);
192         $like = 3 if ($search_type =~ /regex$/o);
193
194         for my $term (@terms) {
195                 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
196                 if (@terms == 1) {
197                         return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
198                 }
199                 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
200         }
201         return undef;
202 }
203
204
205 sub create_node {
206         my $self = shift;
207         my $client = shift;
208         my $node = shift;
209
210         my $cdbi = $self->{cdbi};
211
212         my $success;
213         try {
214                 my $rec = $cdbi->create($node);
215                 $success = $rec->id if ($rec);
216         } catch Error with {
217                 $success = 0;
218         };
219
220         return $success;
221 }
222
223 sub update_node {
224         my $self = shift;
225         my $client = shift;
226         my $node = shift;
227
228         my $cdbi = $self->{cdbi};
229
230         return $cdbi->update($node);
231 }
232
233 sub mass_delete {
234         my $self = shift;
235         my $client = shift;
236         my $search = shift;
237
238         my $where = 'WHERE ';
239
240         my $cdbi = $self->{cdbi};
241         my $table = $cdbi->table;
242
243         my @keys = sort keys %$search;
244         
245         my @binds;
246         my @wheres;
247         for my $col ( @keys ) {
248                 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
249                         push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
250                         push @binds, map { "$_" } @{ $$search{$col} };
251                 } else {
252                         push @wheres, "$col = ?";
253                         push @binds, $$search{$col};
254                 }
255         }
256         $where .= join ' AND ', @wheres;
257
258         my $delete = "DELETE FROM $table $where";
259
260         $log->debug("Performing MASS deletion : $delete",DEBUG);
261
262         my $dbh = $cdbi->db_Main;
263         my $success = 1;
264         try {
265                 my $sth = $dbh->prepare($delete);
266                 $sth->execute( @binds );
267                 $sth->finish;
268                 $log->debug("MASS Delete succeeded",DEBUG);
269         } catch Error with {
270                 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
271                 $success = 0;
272         };
273         return $success;
274 }
275
276 sub remote_update_node {
277         my $self = shift;
278         my $client = shift;
279         my $keys = shift;
280         my $vals = shift;
281
282         my $cdbi = $self->{cdbi};
283
284         my $success = 1;
285         try {
286                 $success = $cdbi->remote_update($keys,$vals);
287         } catch Error with {
288                 $success = 0;
289         };
290         return $success;
291 }
292
293 sub merge_node {
294         my $self = shift;
295         my $client = shift;
296         my $keys = shift;
297         my $vals = shift;
298
299         my $cdbi = $self->{cdbi};
300
301         my $success = 1;
302         try {
303                 $success = $cdbi->merge($keys,$vals)->id;
304         } catch Error with {
305                 $success = 0;
306         };
307         return $success;
308 }
309
310 sub delete_node {
311         my $self = shift;
312         my $client = shift;
313         my $node = shift;
314
315         my $cdbi = $self->{cdbi};
316
317         my $success = 1;
318         try {
319                 $success = $cdbi->delete($node);
320         } catch Error with {
321                 $success = 0;
322         };
323         return $success;
324 }
325
326 sub batch_call {
327         my $self = shift;
328         my $client = shift;
329         my @nodes = @_;
330
331         my $unwrap = $self->{unwrap};
332
333         my $cdbi = $self->{cdbi};
334         my $api_name = $self->api_name;
335         (my $single_call_api_name = $api_name) =~ s/batch\.//o;
336
337         $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
338         my $method = $self->method_lookup($single_call_api_name);
339
340         my @success;
341         while ( my $node = shift(@nodes) ) {
342                 my ($res) = $method->run( ($unwrap ? (@$node) : ($node)) ); 
343                 push(@success, 1) if ($res >= 0);
344         }
345
346         my $insert_total = 0;
347         $insert_total += $_ for (@success);
348
349         return $insert_total;
350 }
351
352 OpenILS::Application::Storage::Publisher::actor->use;
353 if ($@) {
354         $log->debug("ARG! Couldn't load actor class Publisher: $@", ERROR);
355         throw OpenSRF::EX::ERROR ("ARG! Couldn't load actor class Publisher: $@");
356 }
357
358 OpenILS::Application::Storage::Publisher::action->use;
359 if ($@) {
360         $log->debug("ARG! Couldn't load action class Publisher: $@", ERROR);
361         throw OpenSRF::EX::ERROR ("ARG! Couldn't load action class Publisher: $@");
362 }
363
364 OpenILS::Application::Storage::Publisher::asset->use;
365 if ($@) {
366         $log->debug("ARG! Couldn't load asset class Publisher: $@", ERROR);
367         throw OpenSRF::EX::ERROR ("ARG! Couldn't load asset class Publisher: $@");
368 }
369
370 OpenILS::Application::Storage::Publisher::biblio->use;
371 if ($@) {
372         $log->debug("ARG! Couldn't load biblio class Publisher: $@", ERROR);
373         throw OpenSRF::EX::ERROR ("ARG! Couldn't load biblio class Publisher: $@");
374 }
375
376 OpenILS::Application::Storage::Publisher::config->use;
377 if ($@) {
378         $log->debug("ARG! Couldn't load config class Publisher: $@", ERROR);
379         throw OpenSRF::EX::ERROR ("ARG! Couldn't load config class Publisher: $@");
380 }
381
382 OpenILS::Application::Storage::Publisher::metabib->use;
383 if ($@) {
384         $log->debug("ARG! Couldn't load metabib class Publisher: $@", ERROR);
385         throw OpenSRF::EX::ERROR ("ARG! Couldn't load metabib class Publisher: $@");
386 }
387
388 OpenILS::Application::Storage::Publisher::authority->use;
389 if ($@) {
390         $log->debug("ARG! Couldn't load authority class Publisher: $@", ERROR);
391         throw OpenSRF::EX::ERROR ("ARG! Couldn't load authority class Publisher: $@");
392 }
393
394 OpenILS::Application::Storage::Publisher::money->use;
395 if ($@) {
396         $log->debug("ARG! Couldn't load money class Publisher: $@", ERROR);
397         throw OpenSRF::EX::ERROR ("ARG! Couldn't load money class Publisher: $@");
398 }
399
400 OpenILS::Application::Storage::Publisher::permission->use;
401 if ($@) {
402         $log->debug("ARG! Couldn't load permission class Publisher: $@", ERROR);
403         throw OpenSRF::EX::ERROR ("ARG! Couldn't load permission class Publisher: $@");
404 }
405
406 OpenILS::Application::Storage::Publisher::container->use;
407 if ($@) {
408         $log->debug("ARG! Couldn't load container class Publisher: $@", ERROR);
409         throw OpenSRF::EX::ERROR ("ARG! Couldn't load container class Publisher: $@");
410 }
411
412
413
414 for my $fmclass ( (Fieldmapper->classes) ) {
415
416         $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
417
418         next if ($fmclass->is_virtual);
419
420         (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
421         (my $class = $cdbi) =~ s/::.*//o;
422         (my $api_class = $cdbi) =~ s/::/./go;
423         my $registration_class = __PACKAGE__ . "::$class";
424         my $api_prefix = 'open-ils.storage.direct.'.$api_class;
425
426         # Create the search methods
427         unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
428                 __PACKAGE__->register_method(
429                         api_name        => $api_prefix.'.search',
430                         method          => 'search',
431                         api_level       => 1,
432                         argc            => 2,
433                         stream          => 1,
434                         cdbi            => $cdbi,
435                         cachable        => 1,
436                 );
437         }
438
439         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_where' ) ) {
440                 __PACKAGE__->register_method(
441                         api_name        => $api_prefix.'.search_where',
442                         method          => 'search_where',
443                         api_level       => 1,
444                         stream          => 1,
445                         argc            => 1,
446                         cdbi            => $cdbi,
447                         cachable        => 1,
448                 );
449         }
450
451         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like' ) ) {
452                 __PACKAGE__->register_method(
453                         api_name        => $api_prefix.'.search_like',
454                         method          => 'search',
455                         api_level       => 1,
456                         stream          => 1,
457                         cdbi            => $cdbi,
458                         cachable        => 1,
459                         argc            => 2,
460                 );
461         }
462
463         if (\&Class::DBI::search_fts) {
464                 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts' ) ) {
465                         __PACKAGE__->register_method(
466                                 api_name        => $api_prefix.'.search_fts',
467                                 method          => 'search',
468                                 api_level       => 1,
469                                 stream          => 1,
470                                 cdbi            => $cdbi,
471                                 cachable        => 1,
472                                 argc            => 2,
473                         );
474                 }
475         }
476
477         if (\&Class::DBI::search_regex) {
478                 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex' ) ) {
479                         __PACKAGE__->register_method(
480                                 api_name        => $api_prefix.'.search_regex',
481                                 method          => 'search',
482                                 api_level       => 1,
483                                 stream          => 1,
484                                 cdbi            => $cdbi,
485                                 cachable        => 1,
486                                 argc            => 2,
487                         );
488                 }
489         }
490
491         if (\&Class::DBI::search_ilike) {
492                 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike' ) ) {
493                         __PACKAGE__->register_method(
494                                 api_name        => $api_prefix.'.search_ilike',
495                                 method          => 'search',
496                                 api_level       => 1,
497                                 stream          => 1,
498                                 cdbi            => $cdbi,
499                                 cachable        => 1,
500                                 argc            => 2,
501                         );
502                 }
503         }
504
505         # Create the retrieve method
506         unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
507                 __PACKAGE__->register_method(
508                         api_name        => $api_prefix.'.retrieve',
509                         method          => 'retrieve_node',
510                         api_level       => 1,
511                         cdbi            => $cdbi,
512                         cachable        => 1,
513                         argc            => 1,
514                 );
515         }
516
517         # Create the batch retrieve method
518         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
519                 __PACKAGE__->register_method(
520                         api_name        => $api_prefix.'.batch.retrieve',
521                         method          => 'retrieve_node',
522                         api_level       => 1,
523                         stream          => 1,
524                         cdbi            => $cdbi,
525                         cachable        => 1,
526                         argc            => 1,
527                 );
528         }
529
530         for my $field ($fmclass->real_fields) {
531                 unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
532                         __PACKAGE__->register_method(
533                                 api_name        => $api_prefix.'.search.'.$field,
534                                 method          => 'search_one_field',
535                                 api_level       => 1,
536                                 cdbi            => $cdbi,
537                                 cachable        => 1,
538                                 stream          => 1,
539                                 argc            => 1,
540                         );
541                 }
542                 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
543                         __PACKAGE__->register_method(
544                                 api_name        => $api_prefix.'.search_like.'.$field,
545                                 method          => 'search_one_field',
546                                 api_level       => 1,
547                                 cdbi            => $cdbi,
548                                 cachable        => 1,
549                                 stream          => 1,
550                                 argc            => 1,
551                         );
552                 }
553                 if (\&Class::DBI::search_fts) {
554                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts.'.$field ) ) {
555                                 __PACKAGE__->register_method(
556                                         api_name        => $api_prefix.'.search_fts.'.$field,
557                                         method          => 'search_one_field',
558                                         api_level       => 1,
559                                         cdbi            => $cdbi,
560                                         cachable        => 1,
561                                         stream          => 1,
562                                         argc            => 1,
563                                 );
564                         }
565                 }
566                 if (\&Class::DBI::search_regex) {
567                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex.'.$field ) ) {
568                                 __PACKAGE__->register_method(
569                                         api_name        => $api_prefix.'.search_regex.'.$field,
570                                         method          => 'search_one_field',
571                                         api_level       => 1,
572                                         cdbi            => $cdbi,
573                                         cachable        => 1,
574                                         stream          => 1,
575                                         argc            => 1,
576                                 );
577                         }
578                 }
579                 if (\&Class::DBI::search_ilike) {
580                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_ilike.'.$field ) ) {
581                                 __PACKAGE__->register_method(
582                                         api_name        => $api_prefix.'.search_ilike.'.$field,
583                                         method          => 'search_one_field',
584                                         api_level       => 1,
585                                         cdbi            => $cdbi,
586                                         cachable        => 1,
587                                         stream          => 1,
588                                         argc            => 1,
589                                 );
590                         }
591                 }
592         }
593
594
595         unless ($fmclass->is_readonly) {
596                 # Create the create method
597                 unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
598                         __PACKAGE__->register_method(
599                                 api_name        => $api_prefix.'.create',
600                                 method          => 'create_node',
601                                 api_level       => 1,
602                                 cdbi            => $cdbi,
603                                 argc            => 1,
604                         );
605                 }
606
607                 # Create the batch create method
608                 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
609                         __PACKAGE__->register_method(
610                                 api_name        => $api_prefix.'.batch.create',
611                                 method          => 'batch_call',
612                                 api_level       => 1,
613                                 cdbi            => $cdbi,
614                                 argc            => 1,
615                         );
616                 }
617
618                 # Create the update method
619                 unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
620                         __PACKAGE__->register_method(
621                                 api_name        => $api_prefix.'.update',
622                                 method          => 'update_node',
623                                 api_level       => 1,
624                                 cdbi            => $cdbi,
625                                 argc            => 1,
626                         );
627                 }
628
629                 # Create the batch update method
630                 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
631                         __PACKAGE__->register_method(
632                                 api_name        => $api_prefix.'.batch.update',
633                                 method          => 'batch_call',
634                                 api_level       => 1,
635                                 cdbi            => $cdbi,
636                                 argc            => 1,
637                         );
638                 }
639
640                 # Create the delete method
641                 unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
642                         __PACKAGE__->register_method(
643                                 api_name        => $api_prefix.'.delete',
644                                 method          => 'delete_node',
645                                 api_level       => 1,
646                                 cdbi            => $cdbi,
647                                 argc            => 1,
648                         );
649                 }
650
651                 # Create the batch delete method
652                 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
653                         __PACKAGE__->register_method(
654                                 api_name        => $api_prefix.'.batch.delete',
655                                 method          => 'batch_call',
656                                 api_level       => 1,
657                                 cdbi            => $cdbi,
658                                 argc            => 1,
659                         );
660                 }
661
662                 # Create the merge method
663                 unless ( __PACKAGE__->is_registered( $api_prefix.'.merge' ) ) {
664                         __PACKAGE__->register_method(
665                                 api_name        => $api_prefix.'.merge',
666                                 method          => 'merge_node',
667                                 api_level       => 1,
668                                 cdbi            => $cdbi,
669                                 argc            => 1,
670                         );
671                 }
672
673                 # Create the batch merge method
674                 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.merge' ) ) {
675                         __PACKAGE__->register_method(
676                                 api_name        => $api_prefix.'.batch.merge',
677                                 method          => 'batch_call',
678                                 unwrap          => 1,
679                                 api_level       => 1,
680                                 cdbi            => $cdbi,
681                                 argc            => 1,
682                         );
683                 }
684
685                 # Create the remote_update method
686                 unless ( __PACKAGE__->is_registered( $api_prefix.'.remote_update' ) ) {
687                         __PACKAGE__->register_method(
688                                 api_name        => $api_prefix.'.remote_update',
689                                 method          => 'remote_update_node',
690                                 api_level       => 1,
691                                 cdbi            => $cdbi,
692                                 argc            => 1,
693                         );
694                 }
695
696                 # Create the batch remote_update method
697                 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.remote_update' ) ) {
698                         __PACKAGE__->register_method(
699                                 api_name        => $api_prefix.'.batch.remote_update',
700                                 method          => 'batch_call',
701                                 api_level       => 1,
702                                 unwrap          => 1,
703                                 cdbi            => $cdbi,
704                                 argc            => 1,
705                         );
706                 }
707
708                 # Create the search-based mass delete method
709                 unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
710                         __PACKAGE__->register_method(
711                                 api_name        => $api_prefix.'.mass_delete',
712                                 method          => 'mass_delete',
713                                 api_level       => 1,
714                                 cdbi            => $cdbi,
715                                 argc            => 1,
716                         );
717                 }
718         }
719 }
720
721 1;