1 package OpenILS::Application::Search::Authority;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
5 use OpenILS::Utils::Fieldmapper;
6 use OpenILS::Application::AppUtils;
9 use OpenILS::Utils::CStoreEditor q/:funcs/;
10 use OpenSRF::Utils::Logger qw/$logger/;
12 use OpenSRF::Utils::JSON;
14 use Time::HiRes qw(time);
15 use OpenSRF::EX qw(:try);
16 use Digest::MD5 qw(md5_hex);
21 sub validate_authority {
25 my $session = OpenSRF::AppSession->create("open-ils.storage");
26 return $session->request( 'open-ils.storage.authority.validate.tag' => @_ )->gather(1);
28 __PACKAGE__->register_method(
29 method => "validate_authority",
30 api_name => "open-ils.search.authority.validate.tag",
32 note => "Validates authority data from existing controlled terms",
35 sub validate_authority_return_records_by_id {
39 my $session = OpenSRF::AppSession->create("open-ils.storage");
40 return $session->request( 'open-ils.storage.authority.validate.tag.id_list' => @_ )->gather(1);
42 __PACKAGE__->register_method(
43 method => "validate_authority_return_records_by_id",
44 api_name => "open-ils.search.authority.validate.tag.id_list",
46 note => "Validates authority data from existing controlled terms",
49 sub search_authority {
53 my $session = OpenSRF::AppSession->create("open-ils.storage");
54 return $session->request( 'open-ils.storage.authority.search.marc.atomic' => @_ )->gather(1);
56 __PACKAGE__->register_method(
57 method => "search_authority",
58 api_name => "open-ils.search.authority.fts",
60 note => "Searches authority data for existing controlled terms and crossrefs",
63 sub search_authority_by_simple_normalize_heading {
67 my $controlset = shift;
69 my $norm_heading_query = {
70 from => [ 'authority.simple_normalize_heading' => $marcxml ]
74 my $norm_heading = $e->json_query($norm_heading_query)->[0]->{'authority.simple_normalize_heading'};
76 unless (defined($norm_heading) && $norm_heading != '') {
77 return OpenILS::Event->new('BAD_PARAMS', note => 'Heading normalized to null or empty string');
81 select => { are => ['id'] },
86 'startwith' => $norm_heading
88 defined($controlset) ? ( control_set => $controlset ) : ()
92 $client->respond($_->{id}) for @{ $e->json_query( $query ) };
93 $client->respond_complete;
95 __PACKAGE__->register_method(
96 method => "search_authority_by_simple_normalize_heading",
97 api_name => "open-ils.search.authority.simple_heading.from_xml",
100 note => "Searches authority data by main entry using marcxml, returning 'are' ids; params are marcxml and optional control-set-id",
103 sub search_authority_batch_by_simple_normalize_heading {
106 my $search_set = [@_];
108 my $m = $self->method_lookup('open-ils.search.authority.simple_heading.from_xml.atomic');
110 for my $s ( @$search_set ) {
111 for my $k ( keys %$s ) {
112 $client->respond( { $k => $m->run( $s->{$k}, $k ) } );
116 $client->respond_complete;
118 __PACKAGE__->register_method(
119 method => "search_authority_batch_by_simple_normalize_heading",
120 api_name => "open-ils.search.authority.simple_heading.from_xml.batch",
123 note => "Searches authority data by main entry using marcxml, in control-set batches, returning 'are' ids; params are hashes of { control-set-id => marcxml }",
127 sub crossref_authority {
132 my $limit = shift || 10;
134 my $session = OpenSRF::AppSession->create("open-ils.storage");
136 # Avoid generating spurious errors for more granular indexes, like author|personal
137 $class =~ s/^(.*?)\|.*?$/$1/;
139 $logger->info("authority xref search for $class=$term, limit=$limit");
140 my $fr = $session->request(
141 "open-ils.storage.authority.$class.see_from.controlled.atomic",$term, $limit)->gather(1);
142 my $al = $session->request(
143 "open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, $limit)->gather(1);
145 my $data = _auth_flatten( $term, $fr, $al, 1 );
160 last unless ($$x[$i]);
161 if ($string =~ /\W$/o) {
162 $string .= ' '.$$x[$i];
164 $string .= ' -- '.$$x[$i];
167 next if (lc($string) eq lc($term));
169 $hash{$string}++ if (lc($$x[0]) eq lc($term));
171 my $from = [keys %hash]; #[ sort { $hash{$b} <=> $hash{$a} || $a cmp $b } keys %hash ];
173 # $from = [ @$from[0..4] ] if $limit;
179 last unless ($$x[$i]);
180 if ($string =~ /\W$/o) {
181 $string .= ' '.$$x[$i];
183 $string .= ' -- '.$$x[$i];
186 next if (lc($string) eq lc($term));
188 $hash{$string}++ if (lc($$x[0]) eq lc($term));
190 my $also = [keys %hash]; #[ sort { $hash{$b} <=> $hash{$a} || $a cmp $b } keys %hash ];
192 # $also = [ @$also[0..4] ] if $limit;
194 #warn Dumper( { from => $from, also => $also } );
196 return { from => $from, also => $also };
199 __PACKAGE__->register_method(
200 method => "crossref_authority",
201 api_name => "open-ils.search.authority.crossref",
203 note => "Searches authority data for existing controlled terms and crossrefs",
206 __PACKAGE__->register_method(
207 #method => "new_crossref_authority_batch",
208 method => "crossref_authority_batch2",
209 api_name => "open-ils.search.authority.crossref.batch",
212 Takes an array of class,term pair sub-arrays and performs an authority lookup for each
214 PARAMS( [ ["subject", "earth"], ["author","shakespeare"] ] );
216 Returns an object like so:
219 "term" : { "from" : [ ...], "also" : [...] }
220 "term2" : { "from" : [ ...], "also" : [...] }
225 sub new_crossref_authority_batch {
226 my( $self, $client, $reqs ) = @_;
230 my $session = OpenSRF::AppSession->create("open-ils.storage");
232 for my $req (@$reqs) {
234 my $class = $req->[0];
235 my $term = $req->[1];
236 next unless $class and $term;
237 $logger->info("Sending authority request for $class : $term");
238 my $fr = $session->request("open-ils.storage.authority.$class.see_from.controlled.atomic",$term, 10)->gather(1);
239 my $al = $session->request("open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, 10)->gather(1);
241 $response->{$class} = {} unless exists $response->{$class};
242 $response->{$class}->{$term} = _auth_flatten( $term, $fr, $al, 1 );
246 #warn Dumper( $response );
250 sub crossref_authority_batch {
251 my( $self, $client, $reqs ) = @_;
255 my $session = OpenSRF::AppSession->create("open-ils.storage");
257 for my $req (@$reqs) {
259 my $class = $req->[0];
260 my $term = $req->[1];
261 next unless $class and $term;
262 $logger->info("Sending authority request for $class : $term");
263 my $freq = $session->request("open-ils.storage.authority.$class.see_from.controlled.atomic",$term, 10);
264 my $areq = $session->request("open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, 10);
266 if( $lastr->[0] ) { #process old data while waiting on new data
267 my $cls = $lastr->[0];
268 my $trm = $lastr->[1];
269 my $fr = $lastr->[2];
270 my $al = $lastr->[3];
271 $response->{$cls} = {} unless exists $response->{$cls};
272 $response->{$cls}->{$trm} = _auth_flatten( $trm, $fr, $al, 1 );
275 $lastr->[0] = $class;
277 $lastr->[2] = $freq->gather(1);
278 $lastr->[3] = $areq->gather(1);
281 if( $lastr->[0] ) { #process old data while waiting on new data
282 my $cls = $lastr->[0];
283 my $trm = $lastr->[1];
284 my $fr = $lastr->[2];
285 my $al = $lastr->[3];
286 $response->{$cls} = {} unless exists $response->{$cls};
287 $response->{$cls}->{$trm} = _auth_flatten( $trm, $fr, $al, 1);
296 sub crossref_authority_batch2 {
297 my( $self, $client, $reqs ) = @_;
301 my $session = OpenSRF::AppSession->create("open-ils.storage");
303 $cache = OpenSRF::Utils::Cache->new('global') unless $cache;
305 for my $req (@$reqs) {
307 my $class = $req->[0];
308 my $term = $req->[1];
309 next unless $class and $term;
313 my $cdata = $cache->get_cache("oils_authority_${class}_$t");
316 $logger->debug("returning authority response from cache..");
317 $response->{$class} = {} unless exists $response->{$class};
318 $response->{$class}->{$term} = $cdata;
322 $logger->debug("authority data not found in cache.. fetching from storage");
324 $logger->info("Sending authority request for $class : $term");
325 my $freq = $session->request("open-ils.storage.authority.$class.see_from.controlled.atomic",$term, 10);
326 my $areq = $session->request("open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, 10);
327 my $fr = $freq->gather(1);
328 my $al = $areq->gather(1);
329 $response->{$class} = {} unless exists $response->{$class};
330 my $auth = _auth_flatten( $term, $fr, $al, 1 );
332 my $timeout = 7200; #two hours
333 $timeout = 300 if @{$auth->{from}} or @{$auth->{also}}; # 5 minutes
334 $response->{$class}->{$term} = $auth;
335 $logger->debug("adding authority lookup to cache with timeout $timeout");
336 $cache->put_cache("oils_authority_${class}_$t", $auth, $timeout);