]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher.pm
removing unused fields andd adding paged caching
[working/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::Logger qw/:level/;
8 use OpenILS::Utils::Fieldmapper;
9
10 my $log = 'OpenSRF::Utils::Logger';
11
12
13 sub register_method {
14         my $class = shift;
15         my %args = @_;
16         my %dup_args = %args;
17
18         $class = ref($class) || $class;
19
20         $args{package} ||= $class;
21         __PACKAGE__->SUPER::register_method( %args );
22
23         if (exists($dup_args{cachable}) and $dup_args{cachable}) {
24                 (my $name = $dup_args{api_name}) =~ s/^open-ils\.storage/open-ils.storage.cachable/o;
25                 if ($name ne $dup_args{api_name}) {
26                         $dup_args{real_api_name} = $dup_args{api_name};
27                         $dup_args{method} = 'cachable_wrapper';
28                         $dup_args{api_name} = $name;
29                         $dup_args{package} = __PACKAGE__;
30                         __PACKAGE__->SUPER::register_method( %dup_args );
31                 }
32         }
33 }
34
35 sub cachable_wrapper {
36         my $self = shift;
37         my $client = shift;
38         my @args = @_;
39
40         my %cache_args = (
41                 limit           => 100,
42                 offset          => 0,
43                 timeout         => 300,
44                 cache_page_size => 1000,
45         );
46
47         my @real_args;
48         my $key_string = $self->api_name;
49         for (my $ind = 0; $ind < scalar(@args); $ind++) {
50                 if (    $args[$ind] eq 'limit' ||
51                         $args[$ind] eq 'offset' ||
52                         $args[$ind] eq 'cache_page_size' ||
53                         $args[$ind] eq 'timeout' ) {
54
55                         my $key_ind = $ind;
56                         $ind++;
57                         my $value_ind = $ind;
58                         $cache_args{$args[$key_ind]} = $args[$value_ind];
59                         $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
60                         next;
61                 }
62                 $key_string .= $args[$ind];
63                 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
64                 push @real_args, $args[$ind];
65         }
66
67         my $cache_page = int($cache_args{offset} / $cache_args{cache_page_size});
68         my $cache_key = md5_hex($key_string.$cache_page);
69
70         $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
71
72         my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
73         if (defined $cached_res) {
74                 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
75                 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
76                 my $start = int($cache_args{offset} - ($cache_page * $cache_args{cache_page_size}));
77                 my $end = int(($cache_args{offset} + $cache_args{limit} - 1) - (($cache_page + 1) * $cache_args{cache_page_size}));
78                 $client->respond( $_ ) for ( grep { defined } @$cached_res[ $start .. $end ]);
79                 return undef;
80         }
81
82         my $method = $self->method_lookup($self->{real_api_name});
83         my @res = $method->run(@real_args);
84
85
86         $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
87
88         OpenSRF::Utils::Cache->new->put_cache(
89                 $cache_key =>
90                 [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
91                 $cache_args{timeout}
92         );
93
94         return undef;
95 }
96
97 sub retrieve_node {
98         my $self = shift;
99         my $client = shift;
100         my @ids = @_;
101
102         my $cdbi = $self->{cdbi};
103
104         for my $id ( @ids ) {
105                 next unless ($id);
106
107                 my ($rec) = $cdbi->fast_fieldmapper($id);
108                 if ($self->api_name !~ /batch/o) {
109                         return $rec if ($rec);
110                 }
111                 $client->respond($rec);
112         }
113         return undef;
114 }
115
116 sub search {
117         my $self = shift;
118         my $client = shift;
119         my $searches = shift;
120
121         my $cdbi = $self->{cdbi};
122
123         $log->debug("Searching $cdbi for { ".join(',', map { "$_ => $$searches{$_}" } keys %$searches).' }',DEBUG);
124
125         for my $obj ($cdbi->search($searches)) {
126                 $client->respond( $obj->to_fieldmapper );
127         }
128         return undef;
129 }
130
131 sub search_one_field {
132         my $self = shift;
133         my $client = shift;
134         my @terms = @_;
135
136         (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
137         (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
138         my $cdbi = $self->{cdbi};
139
140         my $like = 0;
141         $like = 1 if ($search_type =~ /like$/o);
142
143         for my $term (@terms) {
144                 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
145                 if (@terms == 1) {
146                         return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
147                 }
148                 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
149         }
150         return undef;
151 }
152
153
154 sub create_node {
155         my $self = shift;
156         my $client = shift;
157         my $node = shift;
158
159         my $cdbi = $self->{cdbi};
160
161         my $success;
162         try {
163                 my $rec = $cdbi->create($node);
164                 $success = $rec->id if ($rec);
165         } catch Error with {
166                 $success = 0;
167         };
168
169         return $success;
170 }
171
172 sub update_node {
173         my $self = shift;
174         my $client = shift;
175         my $node = shift;
176
177         my $cdbi = $self->{cdbi};
178
179         return $cdbi->update($node);
180 }
181
182 sub mass_delete {
183         my $self = shift;
184         my $client = shift;
185         my $search = shift;
186
187         my $where = 'WHERE ';
188
189         my $cdbi = $self->{cdbi};
190         my $table = $cdbi->table;
191
192         my @keys = sort keys %$search;
193         
194         my @binds;
195         my @wheres;
196         for my $col ( @keys ) {
197                 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
198                         push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
199                         push @binds, map { "$_" } @{ $$search{$col} };
200                 } else {
201                         push @wheres, "$col = ?";
202                         push @binds, $$search{$col};
203                 }
204         }
205         $where .= join ' AND ', @wheres;
206
207         my $delete = "DELETE FROM $table $where";
208
209         $log->debug("Performing MASS deletion : $delete",DEBUG);
210
211         my $dbh = $cdbi->db_Main;
212         my $success = 1;
213         try {
214                 my $sth = $dbh->prepare($delete);
215                 $sth->execute( @binds );
216                 $sth->finish;
217                 $log->debug("MASS Delete succeeded",DEBUG);
218         } catch Error with {
219                 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
220                 $success = 0;
221         };
222         return $success;
223 }
224
225 sub delete_node {
226         my $self = shift;
227         my $client = shift;
228         my $node = shift;
229
230         my $cdbi = $self->{cdbi};
231
232         my $success = 1;
233         try {
234                 $success = $cdbi->delete($node);
235         } catch Error with {
236                 $success = 0;
237         };
238         return $success;
239 }
240
241 sub batch_call {
242         my $self = shift;
243         my $client = shift;
244         my @nodes = @_;
245
246         my $cdbi = $self->{cdbi};
247         my $api_name = $self->api_name;
248         (my $single_call_api_name = $api_name) =~ s/batch\.//o;
249
250         $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
251         my $method = $self->method_lookup($single_call_api_name);
252
253         my @success;
254         while ( my $node = shift(@nodes) ) {
255                 my ($res) = $method->run( $node ); 
256                 push(@success, 1) if ($res >= 0);
257         }
258
259         my $insert_total = 0;
260         $insert_total += $_ for (@success);
261
262         return $insert_total;
263 }
264
265 eval '
266 use OpenILS::Application::Storage::Publisher::actor;
267 use OpenILS::Application::Storage::Publisher::action;
268 use OpenILS::Application::Storage::Publisher::asset;
269 use OpenILS::Application::Storage::Publisher::biblio;
270 use OpenILS::Application::Storage::Publisher::config;
271 use OpenILS::Application::Storage::Publisher::metabib;
272 ';
273
274 for my $fmclass ( (Fieldmapper->classes) ) {
275         $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
276
277         (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
278         (my $class = $cdbi) =~ s/::.*//o;
279         (my $api_class = $cdbi) =~ s/::/./go;
280         my $registration_class = __PACKAGE__ . "::$class";
281         my $api_prefix = 'open-ils.storage.direct.'.$api_class;
282
283         # Create the search method
284         unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
285                 __PACKAGE__->register_method(
286                         api_name        => $api_prefix.'.search',
287                         method          => 'search',
288                         api_level       => 1,
289                         stream          => 1,
290                         cdbi            => $cdbi,
291                         cachable        => 1,
292                 );
293         }
294
295         # Create the retrieve method
296         unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
297                 __PACKAGE__->register_method(
298                         api_name        => $api_prefix.'.retrieve',
299                         method          => 'retrieve_node',
300                         api_level       => 1,
301                         cdbi            => $cdbi,
302                         cachable        => 1,
303                 );
304         }
305
306         # Create the batch retrieve method
307         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
308                 __PACKAGE__->register_method(
309                         api_name        => $api_prefix.'.batch.retrieve',
310                         method          => 'retrieve_node',
311                         api_level       => 1,
312                         stream          => 1,
313                         cdbi            => $cdbi,
314                         cachable        => 1,
315                 );
316         }
317
318         unless ($fmclass->is_virtual) {
319                 for my $field ($fmclass->real_fields) {
320                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
321                                 __PACKAGE__->register_method(
322                                         api_name        => $api_prefix.'.search.'.$field,
323                                         method          => 'search_one_field',
324                                         api_level       => 1,
325                                         cdbi            => $cdbi,
326                                         cachable        => 1,
327                                 );
328                         }
329                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
330                                 __PACKAGE__->register_method(
331                                         api_name        => $api_prefix.'.search_like.'.$field,
332                                         method          => 'search_one_field',
333                                         api_level       => 1,
334                                         cdbi            => $cdbi,
335                                         cachable        => 1,
336                                 );
337                         }
338                 }
339         }
340
341
342         # Create the create method
343         unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
344                 __PACKAGE__->register_method(
345                         api_name        => $api_prefix.'.create',
346                         method          => 'create_node',
347                         api_level       => 1,
348                         cdbi            => $cdbi,
349                 );
350         }
351
352         # Create the batch create method
353         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
354                 __PACKAGE__->register_method(
355                         api_name        => $api_prefix.'.batch.create',
356                         method          => 'batch_call',
357                         api_level       => 1,
358                         cdbi            => $cdbi,
359                 );
360         }
361
362         # Create the update method
363         unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
364                 __PACKAGE__->register_method(
365                         api_name        => $api_prefix.'.update',
366                         method          => 'update_node',
367                         api_level       => 1,
368                         cdbi            => $cdbi,
369                 );
370         }
371
372         # Create the batch update method
373         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
374                 __PACKAGE__->register_method(
375                         api_name        => $api_prefix.'.batch.update',
376                         method          => 'batch_call',
377                         api_level       => 1,
378                         cdbi            => $cdbi,
379                 );
380         }
381
382         # Create the delete method
383         unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
384                 __PACKAGE__->register_method(
385                         api_name        => $api_prefix.'.delete',
386                         method          => 'delete_node',
387                         api_level       => 1,
388                         cdbi            => $cdbi,
389                 );
390         }
391
392         # Create the batch delete method
393         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
394                 __PACKAGE__->register_method(
395                         api_name        => $api_prefix.'.batch.delete',
396                         method          => 'batch_call',
397                         api_level       => 1,
398                         cdbi            => $cdbi,
399                 );
400         }
401
402         # Create the search-based mass delete method
403         unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
404                 __PACKAGE__->register_method(
405                         api_name        => $api_prefix.'.mass_delete',
406                         method          => 'mass_delete',
407                         api_level       => 1,
408                         cdbi            => $cdbi,
409                 );
410         }
411
412 }
413
414 1;