]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/authority.pm
Whitespace. gah.
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher / authority.pm
1 package OpenILS::Application::Storage::Publisher::authority;
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 OpenILS::Utils::Normalize qw( naco_normalize );
8 use OpenSRF::Utils::Logger qw/:level/;
9 use OpenSRF::Utils::Cache;
10 use Data::Dumper;
11 use Digest::MD5 qw/md5_hex/;
12 use XML::LibXML;
13 use Time::HiRes qw/time sleep/;
14 use Unicode::Normalize;
15
16 my $log = 'OpenSRF::Utils::Logger';
17
18 $VERSION = 1;
19
20 my $parser = XML::LibXML->new;
21
22 sub validate_tag {
23         my $self = shift;
24         my $client = shift;
25         my %args = @_;
26         
27         my @tags = @{$args{tags}};
28         my @searches = @{$args{searches}};
29
30         my $search_table = authority::full_rec->table;
31
32         my @values;
33         my @selects;
34         for my $t ( @tags ) {
35                 for my $search ( @searches ) {
36                         my $sf = $$search{subfield};
37                         my $term = naco_normalize($$search{term}, $sf);
38
39                         $tag = [$tag] if (!ref($tag));
40
41                         push @values, $t, $sf, $term;
42
43                         push @selects,
44                                 "SELECT record FROM $search_table ".
45                                 "WHERE tag = ? AND subfield = ? AND value = ?";
46                 }
47
48                 my $sql;
49                 if ($self->api_name =~ /id_list/) {
50                         $sql = 'SELECT DISTINCT record FROM (';
51                 } else {
52                         $sql = 'SELECT COUNT(DISTINCT record) FROM (';
53                 }
54                 $sql .= 'SELECT record FROM (('.join(') INTERSECT (', @selects).')) AS x ';
55                 $sql .= "JOIN $search_table recheck USING (record) WHERE recheck.tag = ? ";
56                 $sql .= "GROUP BY 1 HAVING (COUNT(recheck.id) - ?) = 0) AS foo;";
57
58                 if ($self->api_name =~ /id_list/) {
59                         my $id_list = authority::full_rec->db_Main->selectcol_arrayref( $sql, {}, @values, $t, scalar(@searches) );
60                         return $id_list;
61                 } else {
62                         my $count = authority::full_rec->db_Main->selectcol_arrayref( $sql, {}, @values, $t, scalar(@searches) )->[0];
63                         return $count if ($count > 0);
64                 }
65         }
66
67         return 0;
68 }
69 __PACKAGE__->register_method(
70         api_name        => "open-ils.storage.authority.validate.tag",
71         method          => 'validate_tag',
72         api_level       => 1,
73 );
74
75 __PACKAGE__->register_method(
76         api_name        => "open-ils.storage.authority.validate.tag.id_list",
77         method          => 'validate_tag',
78         api_level       => 1,
79 );
80
81
82 sub find_authority_marc {
83         my $self = shift;
84         my $client = shift;
85         my %args = @_;
86         
87         my $term = NFD(lc($args{term}));
88         my $tag = $args{tag};
89         my $subfield = $args{subfield};
90         my $limit = $args{limit} || 100;
91         my $offset = $args{offset} || 0;
92
93         if ($limit) {
94                 $limit = "LIMIT $limit";
95         } else {
96                 $limit = '';
97         }
98
99         if ($offset) {
100                 $offset = "OFFSET $offset";
101         } else {
102                 $offset = '';
103         }
104
105         my $tag_where = "AND f.tag LIKE '$tag'";
106         if (ref $tag) {
107                 $tag_where = "AND f.tag IN ('".join("','",@$tag)."')";
108         }
109
110         my $sf_where = "AND f.subfield = '$subfield'";
111         if (ref $subfield) {
112                 $sf_where = "AND f.subfield IN ('".join("','",@$subfield)."')";
113         }
114
115         my $search_table = authority::full_rec->table;
116         my $marc_table = authority::record_entry->table;
117
118         my ($index_col) = authority::full_rec->columns('FTS');
119         $index_col ||= 'value';
120
121         my $fts = OpenILS::Application::Storage::FTS->compile(default => $term, 'f.value', "f.$index_col");
122
123         $term =~ s/\W+$//gso;
124         $term =~ s/'/''/gso;
125         $term =~ s/\pM//gso;
126
127         my $fts_where = $fts->sql_where_clause;
128         my $fts_words = join '%', $fts->words;
129
130     return undef unless ($fts_words);
131
132         my $fts_words_where = "f.value LIKE '$fts_words\%'";
133         my $fts_start_where = "f.value LIKE '$term\%'";
134         my $fts_eq_where = "f.value = '$term'";
135
136         my $fts_rank = join '+', $fts->fts_rank;
137
138         my $select = <<"        SQL";
139                 SELECT  a.marc, sum($fts_rank), count(f.record), first(f.value)
140                 FROM    $search_table f,
141                         $marc_table a
142                 WHERE   $fts_start_where
143                         $tag_where
144                         $sf_where
145                         AND a.id = f.record
146                         GROUP BY 1
147                         ORDER BY 2 desc, 3 desc, 4
148                         $limit
149                         $offset
150                         
151         SQL
152
153         $log->debug("Authority Search SQL :: [$select]",DEBUG);
154
155         my $recs = authority::full_rec->db_Main->selectcol_arrayref( $select );
156         
157         $log->debug("Search yielded ".scalar(@$recs)." results.",DEBUG);
158
159         $client->respond($_) for (@$recs);
160         return undef;
161 }
162 __PACKAGE__->register_method(
163         api_name        => "open-ils.storage.authority.search.marc",
164         method          => 'find_authority_marc',
165         api_level       => 1,
166         stream          => 1,
167         cachable        => 1,
168 );
169
170 sub _empty_check {
171         my $term = shift;
172         my $class = shift || 'metabib::full_rec';
173
174         my $table = $class->table;
175
176         my ($index_col) = $class->columns('FTS');
177         $index_col ||= 'value';
178
179         my $fts = OpenILS::Application::Storage::FTS->compile(default => $term, 'm.value', "m.$index_col");
180         my $fts_where = $fts->sql_where_clause;
181
182         my $sql = <<"   SQL";
183                 SELECT  TRUE
184                 FROM    $table m
185                 WHERE   $fts_where
186                 LIMIT 1
187         SQL
188
189         return $class->db_Main->selectcol_arrayref($sql)->[0];
190 }
191
192 my $prevtime;
193
194 sub find_see_from_controlled {
195         my $self = shift;
196         my $client = shift;
197         my $term = shift;
198         my $limit = shift;
199         my $offset = shift;
200
201         $prevtime = time;
202
203         (my $class = $self->api_name) =~ s/^.+authority.([^\.]+)\.see.+$/$1/o;
204         my $sf = 'a';
205         $sf = 't' if ($class eq 'title');
206
207         my @marc = $self->method_lookup('open-ils.storage.authority.search.marc')
208                         ->run( term => $term, tag => [400,410,411,430,450,455], subfield => $sf, limit => $limit, offset => $offset );
209
210         
211         for my $m ( @marc ) {
212                 my $doc = $parser->parse_string($m);
213                 my @nodes = $doc->documentElement->findnodes('//*[substring(@tag,1,1)="1"]/*[@code="a" or @code="d" or @code="x"]');
214                 my $list = [ map { $_->textContent } @nodes ];
215                 $client->respond( $list ) if (_empty_check(join(' ',@$list), "metabib::${class}_field_entry"));
216         }
217         return undef;
218 }
219 for my $class ( qw/title author subject keyword series identifier/ ) {
220         __PACKAGE__->register_method(
221                 api_name        => "open-ils.storage.authority.$class.see_from.controlled",
222                 method          => 'find_see_from_controlled',
223                 api_level       => 1,
224                 stream          => 1,
225                 cachable        => 1,
226         );
227 }
228
229 sub find_see_also_from_controlled {
230         my $self = shift;
231         my $client = shift;
232         my $term = shift;
233         my $limit = shift;
234         my $offset = shift;
235
236         (my $class = $self->api_name) =~ s/^.+authority.([^\.]+)\.see.+$/$1/o;
237         my $sf = 'a';
238         $sf = 't' if ($class eq 'title');
239
240         my @marc = $self->method_lookup('open-ils.storage.authority.search.marc')
241                         ->run( term => $term, tag => [500,510,511,530,550,555], subfield => $sf, limit => $limit, offset => $offset );
242         for my $m ( @marc ) {
243                 my $doc = $parser->parse_string($m);
244                 my @nodes = $doc->documentElement->findnodes('//*[substring(@tag,1,1)="1"]/*[@code="a" or @code="d" or @code="x"]');
245                 my $list = [ map { $_->textContent } @nodes ];
246                 $client->respond( $list ) if (_empty_check(join(' ',@$list), "metabib::${class}_field_entry"));
247         }
248         return undef;
249 }
250 for my $class ( qw/title author subject keyword series identifier/ ) {
251         __PACKAGE__->register_method(
252                 api_name        => "open-ils.storage.authority.$class.see_also_from.controlled",
253                 method          => 'find_see_also_from_controlled',
254                 api_level       => 1,
255                 stream          => 1,
256                 cachable        => 1,
257         );
258 }
259
260
261 1;