]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/metabib.pm
hide completely unavailable copies from the opac
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher / metabib.pm
1 package OpenILS::Application::Storage::Publisher::metabib;
2 use base qw/OpenILS::Application::Storage::Publisher/;
3 use vars qw/$VERSION/;
4 use OpenSRF::EX qw/:try/;
5 use OpenILS::Application::Storage::FTS;
6 use OpenILS::Utils::Fieldmapper;
7 use OpenSRF::Utils::Logger qw/:level/;
8 use OpenSRF::Utils::Cache;
9 use Data::Dumper;
10 use Digest::MD5 qw/md5_hex/;
11
12
13 my $log = 'OpenSRF::Utils::Logger';
14
15 $VERSION = 1;
16
17 # need to order record IDs by:
18 #  1) format - text, movie, sound, software, images, maps, mixed, music, 3d
19 #  2) proximity --- XXX Can't do it cheap...
20 #  3) count
21 sub ordered_records_from_metarecord {
22         my $self = shift;
23         my $client = shift;
24         my $mr = shift;
25
26         my $copies_visible = 'AND cp.opac_visible IS TRUE AND cs.holdable IS TRUE';
27         $copies_visible = '' if ($self->api_name =~ /staff/o);
28
29         my $sm_table = metabib::metarecord_source_map->table;
30         my $rd_table = metabib::record_descriptor->table;
31         my $cn_table = asset::call_number->table;
32         my $cp_table = asset::copy->table;
33         my $cs_table = config::copy_status->table;
34         my $out_table = actor::org_unit_type->table;
35
36         my $sql = <<"   SQL";
37          SELECT *
38            FROM (
39                 SELECT  cn.record,
40                         rd.item_type,
41                         sum((SELECT     count(cp.id)
42                                FROM     $cp_table cp
43                                         JOIN $cs_table cs ON (cp.status = cs.id)
44                                WHERE    cn.id = cp.call_number
45                                         $copies_visible
46                           )) AS count
47                   FROM  $cn_table cn,
48                         $sm_table sm,
49                         $rd_table rd
50                   WHERE cn.record = sm.source
51                         AND cn.record = rd.record
52                         AND sm.metarecord = ?
53                   GROUP BY cn.record, rd.item_type
54                   ORDER BY
55                         CASE
56                                 WHEN rd.item_type IS NULL -- default
57                                         THEN 0
58                                 WHEN rd.item_type = '' -- default
59                                         THEN 0
60                                 WHEN rd.item_type IN ('a','t') -- books
61                                         THEN 1
62                                 WHEN rd.item_type = 'g' -- movies
63                                         THEN 2
64                                 WHEN rd.item_type IN ('i','j') -- sound recordings
65                                         THEN 3
66                                 WHEN rd.item_type = 'm' -- software
67                                         THEN 4
68                                 WHEN rd.item_type = 'k' -- images
69                                         THEN 5
70                                 WHEN rd.item_type IN ('e','f') -- maps
71                                         THEN 6
72                                 WHEN rd.item_type IN ('o','p') -- mixed
73                                         THEN 7
74                                 WHEN rd.item_type IN ('c','d') -- music
75                                         THEN 8
76                                 WHEN rd.item_type = 'r' -- 3d
77                                         THEN 9
78                         END,
79                         count DESC
80                 ) x
81           WHERE x.count > 0
82         SQL
83
84         my $sth = metabib::metarecord_source_map->db_Main->prepare_cached($sql);
85         $sth->execute("$mr");
86         while ( my $row = $sth->fetchrow_arrayref ) {
87                 $client->respond( $$row[0] );
88         }
89         return undef;
90
91 }
92 __PACKAGE__->register_method(
93         api_name        => 'open-ils.storage.ordered.metabib.metarecord.records',
94         method          => 'ordered_records_from_metarecord',
95         api_level       => 1,
96         stream          => 1,
97         cachable        => 1,
98 );
99 __PACKAGE__->register_method(
100         api_name        => 'open-ils.storage.ordered.metabib.metarecord.records.staff',
101         method          => 'ordered_records_from_metarecord',
102         api_level       => 1,
103         stream          => 1,
104         cachable        => 1,
105 );
106
107
108 sub metarecord_copy_count {
109         my $self = shift;
110         my $client = shift;
111
112         my %args = @_;
113
114         my $sm_table = metabib::metarecord_source_map->table;
115         my $cn_table = asset::call_number->table;
116         my $cp_table = asset::copy->table;
117         my $cs_table = config::copy_status->table;
118         my $out_table = actor::org_unit_type->table;
119         my $descendants = "actor.org_unit_descendants(u.id)";
120         my $ancestors = "actor.org_unit_ancestors(?)";
121
122         my $copies_visible = 'AND cp.opac_visible IS TRUE AND cs.holdable IS TRUE';
123         $copies_visible = '' if ($self->api_name =~ /staff/o);
124
125         my $sql = <<"   SQL";
126                 SELECT  t.depth,
127                         u.id AS org_unit,
128                         sum(
129                                 (SELECT count(cp.id)
130                                   FROM  $sm_table r
131                                         JOIN $cn_table cn ON (cn.record = r.source)
132                                         JOIN $cp_table cp ON (cn.id = cp.call_number)
133                                         JOIN $cs_table cs ON (cp.status = cs.id)
134                                         JOIN $descendants a ON (cp.circ_lib = a.id)
135                                   WHERE r.metarecord = ?
136                                         $copies_visible
137                                 )
138                         ) AS count,
139                         sum(
140                                 (SELECT count(cp.id)
141                                   FROM  $sm_table r
142                                         JOIN $cn_table cn ON (cn.record = r.source)
143                                         JOIN $cp_table cp ON (cn.id = cp.call_number)
144                                         JOIN $cs_table cs ON (cp.status = cs.id)
145                                         JOIN $descendants a ON (cp.circ_lib = a.id)
146                                   WHERE r.metarecord = ?
147                                         AND cp.status = 0
148                                         $copies_visible
149                                 )
150                         ) AS available
151
152                   FROM  $ancestors u
153                         JOIN $out_table t ON (u.ou_type = t.id)
154                   GROUP BY 1,2
155         SQL
156
157         my $sth = metabib::metarecord_source_map->db_Main->prepare_cached($sql);
158         $sth->execute(''.$args{metarecord}, ''.$args{metarecord}, ''.$args{org_unit});
159         while ( my $row = $sth->fetchrow_hashref ) {
160                 $client->respond( $row );
161         }
162         return undef;
163 }
164 __PACKAGE__->register_method(
165         api_name        => 'open-ils.storage.metabib.metarecord.copy_count',
166         method          => 'metarecord_copy_count',
167         api_level       => 1,
168         stream          => 1,
169         cachable        => 1,
170 );
171 __PACKAGE__->register_method(
172         api_name        => 'open-ils.storage.metabib.metarecord.copy_count.staff',
173         method          => 'metarecord_copy_count',
174         api_level       => 1,
175         stream          => 1,
176         cachable        => 1,
177 );
178
179 sub search_full_rec {
180         my $self = shift;
181         my $client = shift;
182
183         my %args = @_;
184         
185         my $term = $args{term};
186         my $limiters = $args{restrict};
187
188         my ($index_col) = metabib::full_rec->columns('FTS');
189         $index_col ||= 'value';
190         my $search_table = metabib::full_rec->table;
191
192         my $fts = OpenILS::Application::Storage::FTS->compile($term, 'value',"$index_col");
193
194         my $fts_where = $fts->sql_where_clause();
195         my @fts_ranks = $fts->fts_rank;
196
197         my $rank = join(' + ', @fts_ranks);
198
199         my @binds;
200         my @wheres;
201         for my $limit (@$limiters) {
202                 push @wheres, "( tag = ? AND subfield LIKE ? AND $fts_where )";
203                 push @binds, $$limit{tag}, $$limit{subfield};
204                 $log->debug("Limiting query using { tag => $$limit{tag}, subfield => $$limit{subfield} }", DEBUG);
205         }
206         my $where = join(' OR ', @wheres);
207
208         my $select = "SELECT record, sum($rank) FROM $search_table WHERE $where GROUP BY 1 ORDER BY 2 DESC;";
209
210         $log->debug("Search SQL :: [$select]",DEBUG);
211
212         my $recs = metabib::full_rec->db_Main->selectall_arrayref($select, {}, @binds);
213         $log->debug("Search yielded ".scalar(@$recs)." results.",DEBUG);
214
215         $client->respond($_) for (@$recs);
216         return undef;
217 }
218 __PACKAGE__->register_method(
219         api_name        => 'open-ils.storage.direct.metabib.full_rec.search_fts.value',
220         method          => 'search_full_rec',
221         api_level       => 1,
222         stream          => 1,
223         cachable        => 1,
224 );
225 __PACKAGE__->register_method(
226         api_name        => 'open-ils.storage.direct.metabib.full_rec.search_fts.index_vector',
227         method          => 'search_full_rec',
228         api_level       => 1,
229         stream          => 1,
230         cachable        => 1,
231 );
232
233
234 # XXX factored most of the PG dependant stuff out of here... need to find a way to do "dependants".
235 sub search_class_fts {
236         my $self = shift;
237         my $client = shift;
238         my %args = @_;
239         
240         my $term = $args{term};
241         my $ou = $args{org_unit};
242         my $ou_type = $args{depth};
243         my $limit = $args{limit};
244         my $offset = $args{offset};
245
246         my $limit_clause = '';
247         my $offset_clause = '';
248
249         $limit_clause = "LIMIT $limit" if (defined $limit and int($limit) > 0);
250         $offset_clause = "OFFSET $offset" if (defined $offset and int($offset) > 0);
251
252
253         my $descendants = defined($ou_type) ?
254                                 "actor.org_unit_descendants($ou, $ou_type)" :
255                                 "actor.org_unit_descendants($ou)";
256
257         my $class = $self->{cdbi};
258         my $search_table = $class->table;
259
260         my $metabib_metarecord = metabib::metarecord->table;
261         my $metabib_full_rec = metabib::full_rec->table;
262         my $metabib_metarecord_source_map_table = metabib::metarecord_source_map->table;
263         my $asset_call_number_table = asset::call_number->table;
264         my $asset_copy_table = asset::copy->table;
265         my $cs_table = config::copy_status->table;
266
267         my ($index_col) = $class->columns('FTS');
268         $index_col ||= 'value';
269
270         my $fts = OpenILS::Application::Storage::FTS->compile($term, 'f.value', "f.$index_col");
271
272         my $fts_where = $fts->sql_where_clause;
273         my @fts_ranks = $fts->fts_rank;
274
275         my $rank = join(' + ', @fts_ranks);
276
277         my $has_vols = 'AND cn.owning_lib = d.id';
278         my $has_copies = 'AND cp.call_number = cn.id';
279         my $copies_visible = 'AND cp.opac_visible IS TRUE AND cs.holdable IS TRUE';
280
281         my $visible_count = ', count(DISTINCT cp.id)';
282         my $visible_count_test = 'HAVING count(DISTINCT cp.id) > 0';
283
284         if ($self->api_name =~ /staff/o) {
285                 $copies_visible = '';
286                 $visible_count_test = '';
287                 $has_copies = '' if ($ou_type == 0);
288                 $has_vols = '' if ($ou_type == 0);
289         }
290
291         my $rank_calc = ", sum($rank + CASE WHEN f.value ILIKE ? THEN 1 ELSE 0 END)/count(m.source)";
292         $rank_calc = ', 1' if ($self->api_name =~ /unordered/o);
293
294         my $select = <<"        SQL";
295                 SELECT  m.metarecord $rank_calc $visible_count
296                   FROM  $search_table f,
297                         $metabib_metarecord_source_map_table m,
298                         $asset_call_number_table cn,
299                         $asset_copy_table cp,
300                         $cs_table cs,
301                         $descendants d
302                   WHERE $fts_where
303                         AND m.source = f.source
304                         AND cn.record = m.source
305                         AND cp.status = cs.id
306                         $has_vols
307                         $has_copies
308                         $copies_visible
309                   GROUP BY m.metarecord $visible_count_test
310                   ORDER BY 2 DESC
311                   $limit_clause $offset_clause
312         SQL
313
314         $log->debug("Field Search SQL :: [$select]",DEBUG);
315
316         my $string = '%'.join('%',$fts->words).'%';
317         my $recs = ($self->api_name =~ /unordered/o) ? 
318                         $class->db_Main->selectall_arrayref($select) :
319                         $class->db_Main->selectall_arrayref($select, {}, lc($string));
320         
321         $log->debug("Search yielded ".scalar(@$recs)." results.",DEBUG);
322
323         $client->respond($_) for (@$recs);
324         return undef;
325 }
326
327 for my $class ( qw/title author subject keyword series/ ) {
328         __PACKAGE__->register_method(
329                 api_name        => "open-ils.storage.metabib.$class.search_fts.metarecord",
330                 method          => 'search_class_fts',
331                 api_level       => 1,
332                 stream          => 1,
333                 cdbi            => "metabib::${class}_field_entry",
334                 cachable        => 1,
335         );
336         __PACKAGE__->register_method(
337                 api_name        => "open-ils.storage.metabib.$class.search_fts.metarecord.unordered",
338                 method          => 'search_class_fts',
339                 api_level       => 1,
340                 stream          => 1,
341                 cdbi            => "metabib::${class}_field_entry",
342                 cachable        => 1,
343         );
344         __PACKAGE__->register_method(
345                 api_name        => "open-ils.storage.metabib.$class.search_fts.metarecord.staff",
346                 method          => 'search_class_fts',
347                 api_level       => 1,
348                 stream          => 1,
349                 cdbi            => "metabib::${class}_field_entry",
350                 cachable        => 1,
351         );
352         __PACKAGE__->register_method(
353                 api_name        => "open-ils.storage.metabib.$class.search_fts.metarecord.staff.unordered",
354                 method          => 'search_class_fts',
355                 api_level       => 1,
356                 stream          => 1,
357                 cdbi            => "metabib::${class}_field_entry",
358                 cachable        => 1,
359         );
360 }
361
362 # XXX factored most of the PG dependant stuff out of here... need to find a way to do "dependants".
363 sub search_class_fts_count {
364         my $self = shift;
365         my $client = shift;
366         my %args = @_;
367         
368         my $term = $args{term};
369         my $ou = $args{org_unit};
370         my $ou_type = $args{depth};
371         my $limit = $args{limit} || 100;
372         my $offset = $args{offset} || 0;
373
374         my $descendants = defined($ou_type) ?
375                                 "actor.org_unit_descendants($ou, $ou_type)" :
376                                 "actor.org_unit_descendants($ou)";
377                 
378
379         (my $search_class = $self->api_name) =~ s/.*metabib.(\w+).search_fts.*/$1/o;
380
381         my $class = $self->{cdbi};
382         my $search_table = $class->table;
383
384         my $metabib_metarecord_source_map_table = metabib::metarecord_source_map->table;
385         my $asset_call_number_table = asset::call_number->table;
386         my $asset_copy_table = asset::copy->table;
387         my $cs_table = config::copy_status->table;
388
389         my ($index_col) = $class->columns('FTS');
390         $index_col ||= 'value';
391
392         my $fts = OpenILS::Application::Storage::FTS->compile($term, 'value',"$index_col");
393
394         my $fts_where = $fts->sql_where_clause;
395
396         my $has_vols = 'AND cn.owning_lib = d.id';
397         my $has_copies = 'AND cp.call_number = cn.id';
398         my $copies_visible = 'AND cp.opac_visible IS TRUE AND cs.holdable IS TRUE';
399         if ($self->api_name =~ /staff/o) {
400                 $copies_visible = '';
401                 $has_vols = '' if ($ou_type == 0);
402                 $has_copies = '' if ($ou_type == 0);
403         }
404
405         # XXX test an "EXISTS version of descendant checking...
406         my $select = <<"        SQL";
407                 SELECT  count(distinct  m.metarecord)
408                   FROM  $search_table f,
409                         $metabib_metarecord_source_map_table m,
410                         $asset_call_number_table cn,
411                         $asset_copy_table cp,
412                         $cs_table cs,
413                         $descendants d
414                   WHERE $fts_where
415                         AND m.source = f.source
416                         AND cn.record = m.source
417                         AND cp.status = cs.id
418                         $has_vols
419                         $has_copies
420                         $copies_visible
421         SQL
422
423         $log->debug("Field Search Count SQL :: [$select]",DEBUG);
424
425         my $recs = $class->db_Main->selectrow_arrayref($select)->[0];
426         
427         $log->debug("Count Search yielded $recs results.",DEBUG);
428
429         return $recs;
430
431 }
432 for my $class ( qw/title author subject keyword series/ ) {
433         __PACKAGE__->register_method(
434                 api_name        => "open-ils.storage.metabib.$class.search_fts.metarecord_count",
435                 method          => 'search_class_fts_count',
436                 api_level       => 1,
437                 stream          => 1,
438                 cdbi            => "metabib::${class}_field_entry",
439                 cachable        => 1,
440         );
441         __PACKAGE__->register_method(
442                 api_name        => "open-ils.storage.metabib.$class.search_fts.metarecord_count.staff",
443                 method          => 'search_class_fts_count',
444                 api_level       => 1,
445                 stream          => 1,
446                 cdbi            => "metabib::${class}_field_entry",
447                 cachable        => 1,
448         );
449 }
450
451
452 1;