]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Authority.pm
LP#1253163: Materialize authority headings
[Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / Search / Authority.pm
1 package OpenILS::Application::Search::Authority;
2 use base qw/OpenILS::Application/;
3 use strict; use warnings;
4
5 use OpenILS::Utils::Fieldmapper;
6 use OpenILS::Application::AppUtils;
7 use XML::LibXML;
8 use XML::LibXSLT;
9 use OpenILS::Utils::CStoreEditor q/:funcs/;
10 use OpenSRF::Utils::Logger qw/$logger/;
11
12 use OpenSRF::Utils::JSON;
13
14 use Time::HiRes qw(time);
15 use OpenSRF::EX qw(:try);
16 use Digest::MD5 qw(md5_hex);
17
18 my $cache;
19
20
21 sub validate_authority {
22     my $self = shift;
23     my $client = shift;
24
25     my $session = OpenSRF::AppSession->create("open-ils.storage");
26     return $session->request( 'open-ils.storage.authority.validate.tag' => @_ )->gather(1);
27 }
28 __PACKAGE__->register_method(
29         method      => "validate_authority",
30         api_name    => "open-ils.search.authority.validate.tag",
31         argc        => 4, 
32         note        => "Validates authority data from existing controlled terms",
33 );              
34
35 sub validate_authority_return_records_by_id {
36     my $self = shift;
37     my $client = shift;
38
39     my $session = OpenSRF::AppSession->create("open-ils.storage");
40     return $session->request( 'open-ils.storage.authority.validate.tag.id_list' => @_ )->gather(1);
41 }
42 __PACKAGE__->register_method(
43         method      => "validate_authority_return_records_by_id",
44         api_name    => "open-ils.search.authority.validate.tag.id_list",
45         argc        => 4, 
46         note        => "Validates authority data from existing controlled terms",
47 );              
48
49 sub search_authority {
50     my $self = shift;
51     my $client = shift;
52
53     my $session = OpenSRF::AppSession->create("open-ils.storage");
54     return $session->request( 'open-ils.storage.authority.search.marc.atomic' => @_ )->gather(1);
55 }
56 __PACKAGE__->register_method(
57         method      => "search_authority",
58         api_name    => "open-ils.search.authority.fts",
59         argc        => 2, 
60         note        => "Searches authority data for existing controlled terms and crossrefs",
61 );              
62
63 sub search_authority_by_simple_normalize_heading {
64     my $self = shift;
65     my $client = shift;
66     my $marcxml = shift;
67     my $controlset = shift;
68
69     my $query = {
70         select => { are => ['id'] },
71         from   => 'are',
72         where  => {
73             deleted => 'f',
74             simple_heading => {
75                 'startwith' => [ 'authority.simple_normalize_heading' => $marcxml ]
76             },
77             defined($controlset) ? ( control_set => $controlset ) : ()
78         }
79     };
80
81     $client->respond($_->{id}) for @{ new_editor()->json_query( $query ) };
82     $client->respond_complete;
83 }
84 __PACKAGE__->register_method(
85         method      => "search_authority_by_simple_normalize_heading",
86         api_name    => "open-ils.search.authority.simple_heading.from_xml",
87         argc        => 1, 
88         stream      => 1,
89         note        => "Searches authority data by main entry using marcxml, returning 'are' ids; params are marcxml and optional control-set-id",
90 );
91
92 sub search_authority_batch_by_simple_normalize_heading {
93     my $self = shift;
94     my $client = shift;
95     my $search_set = [@_];
96
97     my $m = $self->method_lookup('open-ils.search.authority.simple_heading.from_xml.atomic');
98
99     for my $s ( @$search_set ) {
100         for my $k ( keys %$s ) {
101             $client->respond( { $k => $m->run( $s->{$k}, $k ) } );
102         }
103     }
104
105     $client->respond_complete;
106 }
107 __PACKAGE__->register_method(
108         method      => "search_authority_batch_by_simple_normalize_heading",
109         api_name    => "open-ils.search.authority.simple_heading.from_xml.batch",
110         argc        => 1, 
111         stream      => 1,
112         note        => "Searches authority data by main entry using marcxml, in control-set batches, returning 'are' ids; params are hashes of { control-set-id => marcxml }",
113 );
114
115
116 sub crossref_authority {
117     my $self = shift;
118     my $client = shift;
119     my $class = shift;
120     my $term = shift;
121     my $limit = shift || 10;
122
123     my $session = OpenSRF::AppSession->create("open-ils.storage");
124
125     # Avoid generating spurious errors for more granular indexes, like author|personal
126     $class =~ s/^(.*?)\|.*?$/$1/;
127
128     $logger->info("authority xref search for $class=$term, limit=$limit");
129     my $fr = $session->request(
130         "open-ils.storage.authority.$class.see_from.controlled.atomic",$term, $limit)->gather(1);
131     my $al = $session->request(
132         "open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, $limit)->gather(1);
133
134     my $data = _auth_flatten( $term, $fr, $al, 1 );
135
136     return $data;
137 }
138
139 sub _auth_flatten {
140     my $term = shift;
141     my $fr = shift;
142     my $al = shift;
143     my $limit = shift;
144
145     my %hash = ();
146     for my $x (@$fr) {
147         my $string = $$x[0];
148         for my $i (1..10) {
149             last unless ($$x[$i]);
150             if ($string =~ /\W$/o) {
151                 $string .= ' '.$$x[$i];
152             } else {
153                 $string .= ' -- '.$$x[$i];
154             }
155         }
156         next if (lc($string) eq lc($term));
157         $hash{$string}++;
158         $hash{$string}++ if (lc($$x[0]) eq lc($term));
159     }
160     my $from = [keys %hash]; #[ sort { $hash{$b} <=> $hash{$a} || $a cmp $b } keys %hash ];
161
162 #   $from = [ @$from[0..4] ] if $limit;
163
164     %hash = ();
165     for my $x (@$al) {
166         my $string = $$x[0];
167         for my $i (1..10) {
168             last unless ($$x[$i]);
169             if ($string =~ /\W$/o) {
170                 $string .= ' '.$$x[$i];
171             } else {
172                 $string .= ' -- '.$$x[$i];
173             }
174         }
175         next if (lc($string) eq lc($term));
176         $hash{$string}++;
177         $hash{$string}++ if (lc($$x[0]) eq lc($term));
178     }
179     my $also = [keys %hash]; #[ sort { $hash{$b} <=> $hash{$a} || $a cmp $b } keys %hash ];
180
181 #   $also = [ @$also[0..4] ] if $limit;
182
183     #warn Dumper( { from => $from, also => $also } );
184
185     return { from => $from, also => $also };
186 }
187
188 __PACKAGE__->register_method(
189         method      => "crossref_authority",
190         api_name    => "open-ils.search.authority.crossref",
191         argc        => 2, 
192         note        => "Searches authority data for existing controlled terms and crossrefs",
193 );              
194
195 __PACKAGE__->register_method(
196     #method     => "new_crossref_authority_batch",
197     method      => "crossref_authority_batch2",
198     api_name    => "open-ils.search.authority.crossref.batch",
199     argc        => 1, 
200     note        => <<"    NOTE");
201     Takes an array of class,term pair sub-arrays and performs an authority lookup for each
202
203     PARAMS( [ ["subject", "earth"], ["author","shakespeare"] ] );
204
205     Returns an object like so:
206     {
207         "classname" : {
208             "term" : { "from" : [ ...], "also" : [...] }
209             "term2" : { "from" : [ ...], "also" : [...] }
210         }
211     }
212     NOTE
213
214 sub new_crossref_authority_batch {
215     my( $self, $client, $reqs ) = @_;
216
217     my $response = {};
218     my $lastr = [];
219     my $session = OpenSRF::AppSession->create("open-ils.storage");
220
221     for my $req (@$reqs) {
222
223         my $class = $req->[0];
224         my $term = $req->[1];
225         next unless $class and $term;
226         $logger->info("Sending authority request for $class : $term");
227         my $fr = $session->request("open-ils.storage.authority.$class.see_from.controlled.atomic",$term, 10)->gather(1);
228         my $al = $session->request("open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, 10)->gather(1);
229
230         $response->{$class} = {} unless exists $response->{$class};
231         $response->{$class}->{$term} = _auth_flatten( $term, $fr, $al, 1 );
232
233     }
234
235     #warn Dumper( $response );
236     return $response;
237 }
238
239 sub crossref_authority_batch {
240     my( $self, $client, $reqs ) = @_;
241
242     my $response = {};
243     my $lastr = [];
244     my $session = OpenSRF::AppSession->create("open-ils.storage");
245
246     for my $req (@$reqs) {
247
248         my $class = $req->[0];
249         my $term = $req->[1];
250         next unless $class and $term;
251         $logger->info("Sending authority request for $class : $term");
252         my $freq = $session->request("open-ils.storage.authority.$class.see_from.controlled.atomic",$term, 10);
253         my $areq = $session->request("open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, 10);
254
255         if( $lastr->[0] ) { #process old data while waiting on new data
256             my $cls = $lastr->[0];
257             my $trm = $lastr->[1];
258             my $fr  = $lastr->[2];
259             my $al  = $lastr->[3];
260             $response->{$cls} = {} unless exists $response->{$cls};
261             $response->{$cls}->{$trm} = _auth_flatten( $trm, $fr, $al, 1 );
262         }
263
264         $lastr->[0] = $class;
265         $lastr->[1] = $term; 
266         $lastr->[2] = $freq->gather(1);
267         $lastr->[3] = $areq->gather(1);
268     }
269
270     if( $lastr->[0] ) { #process old data while waiting on new data
271         my $cls = $lastr->[0];
272         my $trm = $lastr->[1];
273         my $fr  = $lastr->[2];
274         my $al  = $lastr->[3];
275         $response->{$cls} = {} unless exists $response->{$cls};
276         $response->{$cls}->{$trm} = _auth_flatten( $trm, $fr, $al, 1);
277     }
278
279     return $response;
280 }
281
282
283
284
285 sub crossref_authority_batch2 {
286     my( $self, $client, $reqs ) = @_;
287
288     my $response = {};
289     my $lastr = [];
290     my $session = OpenSRF::AppSession->create("open-ils.storage");
291
292     $cache = OpenSRF::Utils::Cache->new('global') unless $cache;
293
294     for my $req (@$reqs) {
295
296         my $class = $req->[0];
297         my $term = $req->[1];
298         next unless $class and $term;
299
300         my $t = $term;
301         $t =~ s/\s//og;
302         my $cdata = $cache->get_cache("oils_authority_${class}_$t");
303
304         if( $cdata ) {
305             $logger->debug("returning authority response from cache..");
306             $response->{$class} = {} unless exists $response->{$class};
307             $response->{$class}->{$term} = $cdata;
308             next;
309         }
310
311         $logger->debug("authority data not found in cache.. fetching from storage");
312
313         $logger->info("Sending authority request for $class : $term");
314         my $freq = $session->request("open-ils.storage.authority.$class.see_from.controlled.atomic",$term, 10);
315         my $areq = $session->request("open-ils.storage.authority.$class.see_also_from.controlled.atomic",$term, 10);
316         my $fr = $freq->gather(1);  
317         my $al = $areq->gather(1);
318         $response->{$class} = {} unless exists $response->{$class};
319         my $auth = _auth_flatten( $term, $fr, $al, 1 );
320
321         my $timeout = 7200; #two hours
322         $timeout = 300 if @{$auth->{from}} or @{$auth->{also}}; # 5 minutes
323         $response->{$class}->{$term} = $auth;
324         $logger->debug("adding authority lookup to cache with timeout $timeout");
325         $cache->put_cache("oils_authority_${class}_$t", $auth, $timeout);
326     }
327     return $response;
328 }
329
330
331
332 1;