]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Search/Biblio.pm
added actor for its logic. updated biblio to conform to new search method titles
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Search / Biblio.pm
1 package OpenILS::Application::Search::Biblio;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
4
5 use JSON;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::ModsParser;
8 use OpenSRF::Utils::SettingsClient;
9
10 use OpenILS::Application::AppUtils;
11
12 use JSON;
13
14 use Time::HiRes qw(time);
15 use OpenSRF::EX qw(:try);
16 use Digest::MD5 qw(md5_hex);
17
18 # Houses biblio search utilites 
19
20 __PACKAGE__->register_method(
21         method  => "biblio_search_marc",
22         api_name        => "open-ils.search.biblio.marc",
23         argc            => 1, 
24         note            => "Searches biblio information by marc tag",
25 );
26
27 sub biblio_search_marc {
28
29         my( $self, $client, $search_hash, $string ) = @_;
30
31         warn "Building biblio marc session\n";
32         my $session = OpenSRF::AppSession->create("open-ils.storage");
33
34         warn "Sending biblio marc request\n";
35         my $request = $session->request( 
36                         "open-ils.storage.cachable.direct.metabib.full_rec.search_fts.index_vector", 
37                         restrict => $search_hash, 
38                         term            => $string );
39
40         warn "Waiting complete\n";
41         $request->wait_complete();
42
43         warn "Calling recv\n";
44         my $response = $request->recv(20);
45
46         warn "out of recv\n";
47         if($response and UNIVERSAL::isa($response,"OpenSRF::EX")) {
48                 throw $response ($response->stringify);
49         }
50
51
52         my $data = [];
53         if($response and UNIVERSAL::can($response,"content")) {
54                 $data = $response->content;
55         }
56         warn "finishing request\n";
57
58         $request->finish();
59         $session->finish();
60         $session->disconnect();
61
62         return $data;
63
64 }
65
66
67
68 # ---------------------------------------------------------------------------
69 # takes a list of record id's and turns the docs into friendly 
70 # mods structures. Creates one MODS structure for each doc id.
71 # ---------------------------------------------------------------------------
72 sub _records_to_mods {
73         my @ids = @_;
74         
75         my @results;
76         my @marcxml_objs;
77
78         my $session = OpenSRF::AppSession->create("open-ils.storage");
79         my $request = $session->request(
80                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve",  @ids );
81
82         my $last_content = undef;
83
84         while( my $response = $request->recv() ) {
85
86                 if( $last_content ) {
87                         my $u = OpenILS::Utils::ModsParser->new();
88                         $u->start_mods_batch( $last_content->marc );
89                         my $mods = $u->finish_mods_batch();
90                         $mods->{doc_id} = $last_content->id();
91                         warn "Turning doc " . $mods->{doc_id} . " into MODS\n";
92                         $last_content = undef;
93                         push @results, $mods;
94                 }
95
96                 next unless $response;
97
98                 if($response->isa("OpenSRF::EX")) {
99                         throw $response ($response->stringify);
100                 }
101
102                 $last_content = $response->content;
103
104         }
105
106         if( $last_content ) {
107                 my $u = OpenILS::Utils::ModsParser->new();
108                 $u->start_mods_batch( $last_content->marc );
109                 my $mods = $u->finish_mods_batch();
110                 $mods->{doc_id} = $last_content->id();
111                 push @results, $mods;
112         }
113
114         $request->finish();
115         $session->finish();
116         $session->disconnect();
117
118         return \@results;
119
120 }
121
122 __PACKAGE__->register_method(
123         method  => "record_id_to_mods",
124         api_name        => "open-ils.search.biblio.record.mods.retrieve",
125         argc            => 1, 
126         note            => "Provide ID, we provide the mods"
127 );
128
129 # converts a record into a mods object with copy counts attached
130 sub record_id_to_mods {
131
132         my( $self, $client, $org_id, $id ) = @_;
133
134         my $mods_list = _records_to_mods( $id );
135         my $mods_obj = $mods_list->[0];
136         my $cmethod = $self->method_lookup(
137                         "open-ils.search.biblio.record.copy_count");
138         my ($count) = $cmethod->run($org_id, $id);
139         $mods_obj->{copy_count} = $count;
140
141         return $mods_obj;
142 }
143
144
145 __PACKAGE__->register_method(
146         method  => "record_id_to_mods_slim",
147         api_name        => "open-ils.search.biblio.record.mods_slim.retrieve",
148         argc            => 1, 
149         note            => "Provide ID, we provide the mods"
150 );
151
152 # converts a record into a mods object with NO copy counts attached
153 sub record_id_to_mods_slim {
154
155         my( $self, $client, $id ) = @_;
156         warn "Retrieving MODS object for record $id\n";
157         return undef unless(defined $id);
158
159         my $mods_list = _records_to_mods( $id );
160         my $mods_obj = $mods_list->[0];
161         return $mods_obj;
162 }
163
164
165 # Returns the number of copies attached to a record based on org location
166 __PACKAGE__->register_method(
167         method  => "record_id_to_copy_count",
168         api_name        => "open-ils.search.biblio.record.copy_count",
169         argc            => 2, 
170         note            => "Provide ID, we provide the copy count"
171 );
172
173 sub record_id_to_copy_count {
174         my( $self, $client, $org_id, $record_id ) = @_;
175
176         my $session = OpenSRF::AppSession->create("open-ils.storage");
177         warn "copy_count retrieve $record_id\n";
178         return undef unless(defined $record_id);
179
180         my $request = $session->request(
181                 "open-ils.storage.direct.biblio.record_copy_count",  $org_id, $record_id );
182
183         warn "copy_count wait $record_id\n";
184         $request->wait_complete;
185
186         warn "copy_count recv $record_id\n";
187         my $response = $request->recv();
188         return undef unless $response;
189
190         warn "copy_count after recv $record_id\n";
191
192         if( $response and UNIVERSAL::isa($response, "Error")) {
193                 throw $response ($response->stringify);
194         }
195
196         my $count = $response->content;
197
198         $request->finish();
199         $session->finish();
200         $session->disconnect();
201
202         return $count;
203 }
204
205
206 # used for cat search classes
207 my $cat_search_hash =  {
208
209         author => [ 
210                 { tag => "100", subfield => "a"} ,
211                 { tag => "700", subfield => "a"}, 
212         ],
213
214         title => [ 
215                 { tag => "245", subfield => "a"},
216                 { tag => "242", subfield => "a"}, 
217                 { tag => "240", subfield => "a"},
218                 { tag => "210", subfield => "a"},
219         ],
220
221         subject => [ 
222                 { tag => "650", subfield => "_" }, 
223         ],
224
225         tcn     => [
226                 { tag => "035", subfield => "_" },
227         ],
228
229         isbn    => [
230                 { tag => "020", subfield => "a" },
231         ],
232
233 };
234
235
236 __PACKAGE__->register_method(
237         method  => "biblio_search_tcn",
238         api_name        => "open-ils.search.biblio.tcn",
239         argc            => 3, 
240         note            => "Retrieve a record by TCN",
241 );
242
243 sub biblio_search_tcn {
244
245         my( $self, $client, $tcn ) = @_;
246
247         $tcn =~ s/.*?(\w+)\s*$/$1/o;
248         warn "Searching TCN $tcn\n";
249
250         my $session = OpenSRF::AppSession->create( "open-ils.storage" );
251         my $request = $session->request( 
252                         "open-ils.storage.direct.biblio.record_entry.search.tcn_value", $tcn );
253         warn "tcn going into recv\n";
254         my $response = $request->recv();
255
256
257         unless ($response) { return []; }
258
259         if(UNIVERSAL::isa($response,"OpenSRF::EX")) {
260                 warn "Received exception for tcn search\n";
261                 throw $response ($response->stringify);
262         }
263
264         my $record_entry = $response->content;
265         my @ids;
266         for my $record (@$record_entry) {
267                 push @ids, $record->id;
268         }
269
270         warn "received ID's for tcn search @ids\n";
271
272         my $size = @ids;
273         return { count => $size, ids => \@ids };
274
275 }
276
277
278 # --------------------------------------------------------------------------------
279 # ISBN
280
281 __PACKAGE__->register_method(
282         method  => "biblio_search_isbn",
283         api_name        => "open-ils.search.biblio.isbn",
284 );
285
286 sub biblio_search_isbn { 
287         my( $self, $client, $isbn ) = @_;
288         throw OpenSRF::EX::InvalidArg 
289
290                 ("biblio_search_isbn needs an ISBN to search")
291                         unless defined $isbn;
292
293         warn "biblio search for ISBN $isbn\n";
294         my $method = $self->method_lookup("open-ils.search.biblio.marc");
295         my ($records) = $method->run( $cat_search_hash->{isbn}, $isbn );
296         my @ids;
297         for my $i (@$records) { 
298                 if( ref($i) and defined($i->[0])) { 
299                         push @ids, $i->[0]; 
300                 }
301         }
302
303         my $size = @ids;
304         return { count => $size, ids => \@ids };
305 }
306
307 # XXX make me work
308 __PACKAGE__->register_method(
309         method  => "biblio_search_barcode",
310         api_name        => "open-ils.search.biblio.barcode",
311 );
312
313 sub biblio_search_barcode { 
314         my( $self, $client, $barcode ) = @_;
315         throw OpenSRF::EX::InvalidArg 
316
317                 ("biblio_search_barcode needs an ISBN to search")
318                         unless defined $barcode;
319
320         warn "biblio search for ISBN $barcode\n";
321         my $records = OpenILS::Application::AppUtils->simple_scalar_request(
322                         "open-ils.storage", "open-ils.storage.direct.asset.copy.search.barcode",
323                         $barcode );
324
325         my @ids;
326         for my $i (@$records) { 
327                 if( ref($i) and defined($i->[0])) { 
328                         push @ids, $i->[0]; 
329                 }
330         }
331
332         my $size = @ids;
333         return { count => $size, ids => \@ids };
334 }
335
336
337
338 # --------------------------------------------------------------------------------
339
340 __PACKAGE__->register_method(
341         method  => "cat_biblio_search_class",
342         api_name        => "open-ils.search.cat.biblio.class",
343         argc            => 3, 
344         note            => "Searches biblio information by search class",
345 );
346
347 sub cat_biblio_search_class {
348
349         my( $self, $client, $org_id, $class, $sort, $string ) = @_;
350
351         throw OpenSRF::EX::InvalidArg 
352                 ("Not enough args to open-ils.search.cat.biblio.class")
353                         unless( defined($org_id) and $class and $sort and $string );
354
355
356         my $search_hash;
357
358         my $method = $self->method_lookup("open-ils.search.biblio.marc");
359         if(!$method) {
360                 throw OpenSRF::EX::PANIC 
361                         ("Can't lookup method 'open-ils.search.biblio.marc'");
362         }
363
364         my ($records) = $method->run( $cat_search_hash->{$class}, $string );
365
366         my @ids;
367         for my $i (@$records) { push @ids, $i->[0]; }
368
369         my $mods_list = _records_to_mods( @ids );
370         return undef unless (ref($mods_list) eq "ARRAY");
371
372         # ---------------------------------------------------------------
373         # append copy count information to the mods objects
374         my $session = OpenSRF::AppSession->create("open-ils.storage");
375
376         my $request = $session->request(
377                 "open-ils.storage.direct.biblio.record_copy_count.batch",  $org_id, @ids );
378
379         for my $id (@ids) {
380
381                 warn "receiving copy counts for doc $id\n";
382
383                 my $response = $request->recv();
384                 next unless $response;
385
386                 if( $response and UNIVERSAL::isa($response, "Error")) {
387                         throw $response ($response->stringify);
388                 }
389
390                 my $count = $response->content;
391                 my $mods_obj = undef;
392                 for my $m (@$mods_list) {
393                         $mods_obj = $m if ($m->{doc_id} == $id)
394                 }
395                 if($mods_obj) {
396                         $mods_obj->{copy_count} = $count;
397                 }
398
399                 $client->respond( $mods_obj );
400
401         }       
402         $request->finish();
403
404         $session->finish();
405         $session->disconnect();
406         $session->kill_me();
407         # ---------------------------------------------------------------
408
409         return undef;
410 }
411
412
413
414 __PACKAGE__->register_method(
415         method  => "cat_biblio_search_class_id",
416         api_name        => "open-ils.search.cat.biblio.class.id",
417         argc            => 3, 
418         note            => "Searches biblio information by search class and returns the IDs",
419 );
420
421 sub cat_biblio_search_class_id {
422
423         my( $self, $client, $org_id, $class, $string, $limit, $offset ) = @_;
424
425         $offset ||= 0;
426         $limit  ||= 100;
427         $limit -= 1;
428
429
430         $string = OpenILS::Application::Search->filter_search($string);
431         if(!$string) { return undef; }
432
433         warn "Searching cat.biblio.class.id string: $string offset: $offset limit: $limit\n";
434
435         throw OpenSRF::EX::InvalidArg 
436                 ("Not enough args to open-ils.search.cat.biblio.class")
437                         unless( defined($org_id) and $class and $string );
438
439
440         my $search_hash;
441
442         my $cache_key = md5_hex( $org_id . $class . $string );
443         my $id_array = OpenILS::Application::SearchCache->get_cache($cache_key);
444
445         if(ref($id_array)) {
446                 warn "Return search from cache\n";
447                 my $size = @$id_array;
448                 my @ids = @$id_array[ $offset..($offset+$limit) ];
449                 warn "Returning cat.biblio.class.id $string\n";
450                 return { count => $size, ids => \@ids };
451         }
452
453         my $method = $self->method_lookup("open-ils.search.biblio.marc");
454         if(!$method) {
455                 throw OpenSRF::EX::PANIC 
456                         ("Can't lookup method 'open-ils.search.biblio.marc'");
457         }
458
459         my ($records) = $method->run( $cat_search_hash->{$class}, $string );
460
461         my @cache_ids;
462
463         for my $i (@$records) { 
464                 if(defined($i->[0])) {
465                         push @cache_ids, $i->[0]; 
466                 }
467         }
468
469         my @ids = @cache_ids[ $offset..($offset+$limit) ];
470         my $size = @$records;
471
472         OpenILS::Application::SearchCache->put_cache( 
473                         $cache_key, \@cache_ids, $size );
474
475         warn "Returning cat.biblio.class.id $string\n";
476         return { count =>$size, ids => \@ids };
477
478 }
479
480
481 __PACKAGE__->register_method(
482         method  => "biblio_search_class",
483         api_name        => "open-ils.search.biblio.class",
484         argc            => 3, 
485         note            => "Searches biblio information by search class and returns the IDs",
486 );
487
488 sub biblio_search_class {
489
490         my( $self, $client, $class, $string, 
491                         $org_id, $org_type, $limit, $offset ) = @_;
492
493         warn "$org_id : $org_type : $limit :  $offset\n";
494
495         $offset         ||= 0;
496         $limit          = 100 unless defined($limit and $limit > 0 );
497         $org_id         = "1" unless defined($org_id); # xxx
498         $org_type       = 0     unless defined($org_type);
499
500         warn "$org_id : $org_type : $limit :  $offset\n";
501         warn "Searching biblio.class.id string: $string offset: $offset limit: $limit\n";
502
503         $string = OpenILS::Application::Search->filter_search($string);
504         if(!$string) { return undef; }
505
506         if( !defined($org_id) or !$class or !$string ) {
507                 warn "not enbough args to metarecord search\n";
508                 throw OpenSRF::EX::InvalidArg 
509                         ("Not enough args to open-ils.search.cat.biblio.class")
510         }
511
512         $class =~ s/\s+//g;
513
514         if( ($class ne "title") and ($class ne "author") and 
515                 ($class ne "subject") and ($class ne "keyword") ) {
516                 warn "Invalid search class: $class\n";
517                 throw OpenSRF::EX::InvalidArg ("Not a valid search class: $class")
518         }
519
520         # grab the mr id's from storage
521
522         my $method = "open-ils.storage.cachable.metabib.$class.search_fts.metarecord_count";
523         warn "Performing count method $method\n";
524         my $session = OpenSRF::AppSession->create('open-ils.storage');
525
526         my $request = $session->request( $method, 
527                         term => $string, 
528                         org_unit => $org_id, 
529                         depth =>$org_type );
530
531         my $response = $request->recv();
532
533         if(UNIVERSAL::isa($response, "OpenSRF::EX")) {
534                 throw $response ($response->stringify);
535         }
536
537         my $count = $response->content;
538         warn "Received count $count\n";
539         # XXX check count size and respond accordingly
540
541         $request->finish();
542         warn "performing mr search\n";
543         $request = $session->request(   
544                 "open-ils.storage.cachable.metabib.$class.search_fts.metarecord",
545                 term            => $string, 
546                 org_unit => $org_id, 
547                 depth           => $org_type, 
548                 limit           => $limit,
549                 offset  => $offset,
550                 );
551
552         warn "a\n";
553         $response = $request->recv();
554
555         if(UNIVERSAL::isa($response, "OpenSRF::EX")) {
556                 warn "Recieved Exception from storage: " . $response->stringify . "\n";
557                 $response->{'msg'} = $response->stringify();
558                 throw $response ($response->stringify);
559         }
560
561         warn "b\n";
562
563         my $records = $response->content;
564
565         my @all_ids;
566
567         for my $i (@$records) { 
568                 if(defined($i->[0])) {
569                         push @all_ids, $i->[0]; 
570                 }
571         }
572
573         #my @ids = @all_ids[ $offset..($offset+$limit) ];
574         my @ids = @all_ids;
575         @ids = grep { defined($_) } @ids;
576
577         $request->finish();
578         $session->finish();
579         $session->disconnect();
580
581         warn "Returning biblio.class $string\n";
582         return { count =>$count, ids => \@ids };
583
584 }
585
586
587
588
589 __PACKAGE__->register_method(
590         method  => "biblio_mrid_to_modsbatch",
591         api_name        => "open-ils.search.biblio.metarecord.mods_slim.retrieve",
592 );
593
594 sub biblio_mrid_to_modsbatch {
595         my( $self, $client, $mrid ) = @_;
596
597         throw OpenSRF::EX::InvalidArg 
598                 ("search.biblio.metarecord_to_mods requires mr id")
599                         unless defined( $mrid );
600
601
602         my $metarecord = OpenILS::Application::AppUtils->simple_scalar_request( "open-ils.storage", 
603                         "open-ils.storage.direct.metabib.metarecord.retrieve", $mrid );
604         my $master_id = $metarecord->master_record();
605
606
607         # check for existing mods
608         if($metarecord->mods()){
609                 warn "We already have mods for " . $metarecord->id . "\n";
610                 return JSON->JSON2perl($metarecord->mods());
611         }
612
613
614
615         warn "Creating mods batch for metarecord $mrid\n";
616         my $id_hash = biblio_mrid_to_record_ids( undef, undef,  $mrid );
617         my @ids = @{$id_hash->{ids}};
618
619         warn "IDS @ids\n";
620
621         if(@ids < 1) { return undef; }
622
623
624         warn "Master ID is $master_id\n";
625         # grab the master record to start the mods batch 
626
627         my $record = OpenILS::Application::AppUtils->simple_scalar_request( "open-ils.storage", 
628                         "open-ils.storage.direct.biblio.record_entry.retrieve", $master_id );
629
630         if(!$record) {
631                 throw OpenSRF::EX::ERROR 
632                         ("No record returned with id $master_id");
633         }
634
635         my $u = OpenILS::Utils::ModsParser->new();
636         $u->start_mods_batch( $record->marc );
637         my $main_doc_id = $record->id();
638
639         @ids = grep { $_ ne $master_id } @ids;
640
641         warn "NON-Master IDs are @ids\n";
642
643         # now we have to collect all of the marc objects and push them into a mods batch
644         my $session = OpenSRF::AppSession->create("open-ils.storage");
645         my $request = $session->request(
646                         "open-ils.storage.direct.biblio.record_entry.batch.retrieve",  @ids );
647
648         while( my $response = $request->recv() ) {
649
650                 next unless $response;
651                 if(UNIVERSAL::isa( $response,"OpenSRF::EX")) {
652                         throw $response ($response->stringify);
653                 }
654
655                 my $content = $response->content;
656
657                 if( $content ) {
658                         $u->push_mods_batch( $content->marc );
659                 }
660         }
661
662         my $mods = $u->finish_mods_batch();
663         $mods->{doc_id} = $mrid;
664         $request->finish();
665
666         $client->respond_complete($mods);
667
668         $metarecord->mods(JSON->perl2JSON($mods));
669
670         my $req = $session->request( 
671                         "open-ils.storage.direct.metabib.metarecord.update", 
672                         $metarecord );
673         $req->wait_complete;
674         my $resp = $req->recv;
675         throw $req->failed if $req->failed;
676
677         $session->finish();
678         $session->disconnect();
679
680         return undef;
681
682 }
683
684
685
686 # converts a mr id into a list of record ids
687
688 __PACKAGE__->register_method(
689         method  => "biblio_mrid_to_record_ids",
690         api_name        => "open-ils.search.biblio.metarecord_to_records",
691 );
692
693 sub biblio_mrid_to_record_ids {
694         my( $self, $client, $mrid ) = @_;
695
696         throw OpenSRF::EX::InvalidArg 
697                 ("search.biblio.metarecord_to_record_ids requires mr id")
698                         unless defined( $mrid );
699
700         warn "Searching for record for MR $mrid\n";
701
702         my $mrmaps = OpenILS::Application::AppUtils->simple_scalar_request( "open-ils.storage", 
703                         "open-ils.storage.direct.metabib.metarecord_source_map.search.metarecord", $mrid );
704
705         my @ids;
706         for my $map (@$mrmaps) { push @ids, $map->source(); }
707
708         warn "Recovered id's [@ids] for mr $mrid\n";
709
710         my $size = @ids;
711         warn "Size: $size\n";
712
713         return { count => $size, ids => \@ids };
714
715 }
716
717
718
719 1;