1 package OpenILS::Application::Storage::Publisher;
2 use base qw/OpenILS::Application::Storage/;
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;
10 my $log = 'OpenSRF::Utils::Logger';
18 $class = ref($class) || $class;
20 $args{package} ||= $class;
21 __PACKAGE__->SUPER::register_method( %args );
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 );
35 sub cachable_wrapper {
44 cache_page_size => 1000,
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' ) {
58 $cache_args{$args[$key_ind]} = $args[$value_ind];
59 $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
62 $key_string .= $args[$ind];
63 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
64 push @real_args, $args[$ind];
67 my $cache_page = int($cache_args{offset} / $cache_args{cache_page_size});
70 $cache_key = md5_hex($key_string.$cache_page);
73 $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
74 $log->debug("Cache page is $cache_page", DEBUG);
76 my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
77 if (defined $cached_res) {
78 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
79 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
80 my $start = int($cache_args{offset} - ($cache_page * $cache_args{cache_page_size}));
81 my $end = int($start + $cache_args{limit} - 1);
82 $log->debug("Responding with values from ".$start.' to '.$end,DEBUG);
83 $client->respond( $_ ) for ( grep { defined } @$cached_res[ $start .. $end ]);
87 my $method = $self->method_lookup($self->{real_api_name});
88 my @res = $method->run(@real_args);
91 $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
93 $log->debug("Saving values from ".int($cache_page * $cache_args{cache_page_size})." to ".
94 int(($cache_page + 1) * $cache_args{cache_page_size}). "to the cache", INTERNAL);
96 OpenSRF::Utils::Cache->new->put_cache(
98 [@res[int($cache_page * $cache_args{cache_page_size}) .. int(($cache_page + 1) * $cache_args{cache_page_size}) ]] =>
103 $log->error("Cache seems to be down, $e");
114 my $cdbi = $self->{cdbi};
116 for my $id ( @ids ) {
119 my ($rec) = $cdbi->fast_fieldmapper($id);
120 if ($self->api_name !~ /batch/o) {
121 return $rec if ($rec);
123 $client->respond($rec);
131 my $searches = shift;
134 my $cdbi = $self->{cdbi};
136 (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
138 $log->debug("Searching $cdbi for { ".
139 join(',', map { "$_ => $$searches{$_}" } keys %$searches).
140 " } using $search_type",DEBUG);
142 for my $obj ($cdbi->$search_type($searches, $options)) {
143 warn "$obj -> ".ref($obj);
144 next unless ref($obj);
145 $client->respond( $obj->to_fieldmapper );
150 sub search_one_field {
155 (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
156 (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
157 my $cdbi = $self->{cdbi};
160 $like = 1 if ($search_type =~ /like$/o);
161 $like = 2 if ($search_type =~ /fts$/o);
162 $like = 3 if ($search_type =~ /regex$/o);
164 for my $term (@terms) {
165 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
167 return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
169 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
180 my $cdbi = $self->{cdbi};
184 my $rec = $cdbi->create($node);
185 $success = $rec->id if ($rec);
198 my $cdbi = $self->{cdbi};
200 return $cdbi->update($node);
208 my $where = 'WHERE ';
210 my $cdbi = $self->{cdbi};
211 my $table = $cdbi->table;
213 my @keys = sort keys %$search;
217 for my $col ( @keys ) {
218 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
219 push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
220 push @binds, map { "$_" } @{ $$search{$col} };
222 push @wheres, "$col = ?";
223 push @binds, $$search{$col};
226 $where .= join ' AND ', @wheres;
228 my $delete = "DELETE FROM $table $where";
230 $log->debug("Performing MASS deletion : $delete",DEBUG);
232 my $dbh = $cdbi->db_Main;
235 my $sth = $dbh->prepare($delete);
236 $sth->execute( @binds );
238 $log->debug("MASS Delete succeeded",DEBUG);
240 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
251 my $cdbi = $self->{cdbi};
255 $success = $cdbi->delete($node);
267 my $cdbi = $self->{cdbi};
268 my $api_name = $self->api_name;
269 (my $single_call_api_name = $api_name) =~ s/batch\.//o;
271 $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
272 my $method = $self->method_lookup($single_call_api_name);
275 while ( my $node = shift(@nodes) ) {
276 my ($res) = $method->run( $node );
277 push(@success, 1) if ($res >= 0);
280 my $insert_total = 0;
281 $insert_total += $_ for (@success);
283 return $insert_total;
287 use OpenILS::Application::Storage::Publisher::actor;
288 use OpenILS::Application::Storage::Publisher::action;
289 use OpenILS::Application::Storage::Publisher::asset;
290 use OpenILS::Application::Storage::Publisher::biblio;
291 use OpenILS::Application::Storage::Publisher::config;
292 use OpenILS::Application::Storage::Publisher::metabib;
293 use OpenILS::Application::Storage::Publisher::permission;
296 for my $fmclass ( (Fieldmapper->classes) ) {
298 next if ($fmclass->is_virtual);
300 $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
302 (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
303 (my $class = $cdbi) =~ s/::.*//o;
304 (my $api_class = $cdbi) =~ s/::/./go;
305 my $registration_class = __PACKAGE__ . "::$class";
306 my $api_prefix = 'open-ils.storage.direct.'.$api_class;
308 # Create the search methods
309 unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
310 __PACKAGE__->register_method(
311 api_name => $api_prefix.'.search',
320 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like' ) ) {
321 __PACKAGE__->register_method(
322 api_name => $api_prefix.'.search_like',
331 if (\&Class::DBI::search_fts) {
332 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts' ) ) {
333 __PACKAGE__->register_method(
334 api_name => $api_prefix.'.search_fts',
344 if (\&Class::DBI::search_regex) {
345 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex' ) ) {
346 __PACKAGE__->register_method(
347 api_name => $api_prefix.'.search_regex',
357 # Create the retrieve method
358 unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
359 __PACKAGE__->register_method(
360 api_name => $api_prefix.'.retrieve',
361 method => 'retrieve_node',
368 # Create the batch retrieve method
369 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
370 __PACKAGE__->register_method(
371 api_name => $api_prefix.'.batch.retrieve',
372 method => 'retrieve_node',
380 for my $field ($fmclass->real_fields) {
381 unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
382 __PACKAGE__->register_method(
383 api_name => $api_prefix.'.search.'.$field,
384 method => 'search_one_field',
390 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
391 __PACKAGE__->register_method(
392 api_name => $api_prefix.'.search_like.'.$field,
393 method => 'search_one_field',
399 if (\&Class::DBI::search_fts) {
400 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_fts.'.$field ) ) {
401 __PACKAGE__->register_method(
402 api_name => $api_prefix.'.search_fts.'.$field,
403 method => 'search_one_field',
410 if (\&Class::DBI::search_regex) {
411 unless ( __PACKAGE__->is_registered( $api_prefix.'.search_regex.'.$field ) ) {
412 __PACKAGE__->register_method(
413 api_name => $api_prefix.'.search_regex.'.$field,
414 method => 'search_one_field',
424 # Create the create method
425 unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
426 __PACKAGE__->register_method(
427 api_name => $api_prefix.'.create',
428 method => 'create_node',
434 # Create the batch create method
435 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
436 __PACKAGE__->register_method(
437 api_name => $api_prefix.'.batch.create',
438 method => 'batch_call',
444 # Create the update method
445 unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
446 __PACKAGE__->register_method(
447 api_name => $api_prefix.'.update',
448 method => 'update_node',
454 # Create the batch update method
455 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
456 __PACKAGE__->register_method(
457 api_name => $api_prefix.'.batch.update',
458 method => 'batch_call',
464 # Create the delete method
465 unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
466 __PACKAGE__->register_method(
467 api_name => $api_prefix.'.delete',
468 method => 'delete_node',
474 # Create the batch delete method
475 unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
476 __PACKAGE__->register_method(
477 api_name => $api_prefix.'.batch.delete',
478 method => 'batch_call',
484 # Create the search-based mass delete method
485 unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
486 __PACKAGE__->register_method(
487 api_name => $api_prefix.'.mass_delete',
488 method => 'mass_delete',