Lines 56-64
sub new {
Link Here
|
56 |
} |
56 |
} |
57 |
|
57 |
|
58 |
sub handle_hit { |
58 |
sub handle_hit { |
59 |
my ( $self, $index, $server, $marcrecord ) = @_; |
59 |
my ( $self, $index, $server, $hit ) = @_; |
60 |
|
60 |
|
61 |
my $record = Koha::MetadataRecord->new( { schema => 'marc', record => $marcrecord } ); |
61 |
my $record = Koha::MetadataRecord->new( { schema => 'marc', record => $hit->{record} } ); |
62 |
|
62 |
|
63 |
my %fetch = ( |
63 |
my %fetch = ( |
64 |
title => 'biblio.title', |
64 |
title => 'biblio.title', |
Lines 79-93
sub handle_hit {
Link Here
|
79 |
$metadata->{date} //= $metadata->{date2}; |
79 |
$metadata->{date} //= $metadata->{date2}; |
80 |
|
80 |
|
81 |
push @{ $self->{results} }, { |
81 |
push @{ $self->{results} }, { |
|
|
82 |
%$hit, |
82 |
server => $server, |
83 |
server => $server, |
83 |
index => $index, |
84 |
index => $index, |
84 |
record => $marcrecord, |
|
|
85 |
metadata => $metadata, |
85 |
metadata => $metadata, |
86 |
}; |
86 |
}; |
87 |
} |
87 |
} |
88 |
|
88 |
|
89 |
sub search { |
89 |
sub search { |
90 |
my ( $self, $server_ids, $query ) = @_; |
90 |
my ( $self, $server_ids, $terms ) = @_; |
91 |
|
91 |
|
92 |
my $resultset_expiry = 300; |
92 |
my $resultset_expiry = 300; |
93 |
|
93 |
|
Lines 142-148
sub search {
Link Here
|
142 |
} |
142 |
} |
143 |
} |
143 |
} |
144 |
|
144 |
|
145 |
$select->add( $self->_start_worker( $server, $query ) ); |
145 |
$select->add( $self->_start_worker( $server, $terms ) ); |
146 |
} |
146 |
} |
147 |
|
147 |
|
148 |
# Handle these while the servers are searching |
148 |
# Handle these while the servers are searching |
Lines 182-189
sub search {
Link Here
|
182 |
return $stats; |
182 |
return $stats; |
183 |
} |
183 |
} |
184 |
|
184 |
|
|
|
185 |
our $_pqf_mapping = { |
186 |
author => '1=1004', |
187 |
'cn-dewey' => '1=13', |
188 |
'cn-lc' => '1=16', |
189 |
date => '1=30', |
190 |
isbn => '1=7', |
191 |
issn => '1=8', |
192 |
keyword => '1=1016', |
193 |
lccn => '1=9', |
194 |
'local-number' => '1=12', |
195 |
'music-identifier' => '1=51', |
196 |
'standard-identifier' => '1=1007', |
197 |
subject => '1=21', |
198 |
title => '1=4', |
199 |
}; |
200 |
|
201 |
our $_batch_db_mapping = { |
202 |
author => 'import_biblios.author', |
203 |
isbn => 'import_biblios.isbn', |
204 |
issn => 'import_biblios.issn', |
205 |
'local-number' => 'import_biblios.control_number', |
206 |
title => 'import_biblios.title', |
207 |
}; |
208 |
|
209 |
our $_batch_db_text_columns = { |
210 |
author => 1, |
211 |
keyword => 1, |
212 |
title => 1, |
213 |
}; |
214 |
|
215 |
sub _pqf_query_from_terms { |
216 |
my ( $terms ) = @_; |
217 |
|
218 |
my $query; |
219 |
|
220 |
while ( my ( $index, $value ) = each %$terms ) { |
221 |
$value =~ s/"/\\"/; |
222 |
|
223 |
my $term = '@attr ' . $_pqf_mapping->{$index} . ' "' . $value . '"'; |
224 |
|
225 |
if ( $query ) { |
226 |
$query = '@and ' . $query . ' ' . $term; |
227 |
} else { |
228 |
$query = $term; |
229 |
} |
230 |
} |
231 |
|
232 |
return $query; |
233 |
} |
234 |
|
235 |
sub _db_query_get_match_conditions { |
236 |
my ( $index, $value ) = @_; |
237 |
|
238 |
if ( $value =~ /\*/ ) { |
239 |
$value =~ s/\*/%/; |
240 |
return { -like => $value }; |
241 |
} elsif ( $_batch_db_text_columns->{$index} ) { |
242 |
return map +{ -regexp => '[[:<:]]' . $_ . '[[:>:]]' }, split( /\s+/, $value ); |
243 |
} else { |
244 |
return $value; |
245 |
} |
246 |
} |
247 |
|
248 |
sub _batch_db_query_from_terms { |
249 |
my ( $import_batch_id, $terms ) = @_; |
250 |
|
251 |
my @db_terms; |
252 |
|
253 |
while ( my ( $index, $value ) = each %$terms ) { |
254 |
if ( $index eq 'keyword' ) { |
255 |
my @term; |
256 |
|
257 |
while ( my ( $index, $db_column ) = each %$_batch_db_mapping ) { |
258 |
push @term, { $db_column => [ _db_query_get_match_conditions( $index, $value ) ] }; |
259 |
} |
260 |
|
261 |
# These are implicitly joined with OR because the arrayref doesn't start with -and |
262 |
push @db_terms, \@term; |
263 |
} elsif ( $_batch_db_mapping->{$index} ) { |
264 |
push @db_terms, $_batch_db_mapping->{$index} => [ -and => _db_query_get_match_conditions( $index, $value ) ]; |
265 |
} else { |
266 |
# No such index, we should fail |
267 |
return undef; |
268 |
} |
269 |
} |
270 |
|
271 |
return { |
272 |
-and => [ |
273 |
import_batch_id => $import_batch_id, |
274 |
@db_terms |
275 |
], |
276 |
}, |
277 |
} |
278 |
|
185 |
sub _start_worker { |
279 |
sub _start_worker { |
186 |
my ( $self, $server, $query ) = @_; |
280 |
my ( $self, $server, $terms ) = @_; |
187 |
pipe my $readfh, my $writefh; |
281 |
pipe my $readfh, my $writefh; |
188 |
|
282 |
|
189 |
# Accessing the cache or Koha database after the fork is risky, so get any resources we need |
283 |
# Accessing the cache or Koha database after the fork is risky, so get any resources we need |
Lines 207-212
sub _start_worker {
Link Here
|
207 |
my $connection; |
301 |
my $connection; |
208 |
my ( $num_hits, $num_fetched, $hits, $results ); |
302 |
my ( $num_hits, $num_fetched, $hits, $results ); |
209 |
|
303 |
|
|
|
304 |
my $pqf_query = _pqf_query_from_terms( $terms ); |
305 |
|
210 |
eval { |
306 |
eval { |
211 |
if ( $server->{type} eq 'z3950' ) { |
307 |
if ( $server->{type} eq 'z3950' ) { |
212 |
my $zoptions = ZOOM::Options->new(); |
308 |
my $zoptions = ZOOM::Options->new(); |
Lines 220-285
sub _start_worker {
Link Here
|
220 |
$connection = ZOOM::Connection->create($zoptions); |
316 |
$connection = ZOOM::Connection->create($zoptions); |
221 |
|
317 |
|
222 |
$connection->connect( $server->{host}, $server->{port} ); |
318 |
$connection->connect( $server->{host}, $server->{port} ); |
223 |
$results = $connection->search_pqf( $query ); # Starts the search |
319 |
$results = $connection->search_pqf( $pqf_query ); # Starts the search |
224 |
} elsif ( $server->{type} eq 'koha' ) { |
320 |
} elsif ( $server->{type} eq 'koha' ) { |
225 |
$connection = C4::Context->Zconn( $server->{extra} ); |
321 |
$connection = C4::Context->Zconn( $server->{extra} ); |
226 |
$results = $connection->search_pqf( $query ); # Starts the search |
322 |
$results = $connection->search_pqf( $pqf_query ); # Starts the search |
227 |
} elsif ( $server->{type} eq 'batch' ) { |
323 |
} elsif ( $server->{type} eq 'batch' ) { |
228 |
$server->{encoding} = 'utf-8'; |
324 |
$server->{encoding} = 'utf-8'; |
229 |
} |
325 |
} |
230 |
}; |
326 |
}; |
231 |
if ($@) { |
327 |
if ($@) { |
232 |
store_fd { |
328 |
store_fd { |
233 |
error => $connection ? $connection->exception() : $@, |
329 |
error => $connection ? $connection->exception()->message() : $@, |
234 |
server => $server, |
330 |
server => $server, |
235 |
}, $writefh; |
331 |
}, $writefh; |
236 |
exit; |
332 |
exit; |
237 |
} |
333 |
} |
238 |
|
334 |
|
|
|
335 |
my $error; |
239 |
if ( $server->{type} eq 'batch' ) { |
336 |
if ( $server->{type} eq 'batch' ) { |
240 |
# TODO: actually handle PQF |
|
|
241 |
$query =~ s/@\w+ (?:\d+=\d+ )?//g; |
242 |
$query =~ s/"//g; |
243 |
|
244 |
my $schema = Koha::Database->new->schema; |
337 |
my $schema = Koha::Database->new->schema; |
245 |
$schema->storage->debug(1); |
338 |
my $db_query = _batch_db_query_from_terms( $server->{extra}, $terms ); |
246 |
my $match_condition = [ map +{ -like => '%' . $_ . '%' }, split( /\s+/, $query ) ]; |
339 |
|
247 |
$hits = [ $schema->resultset('ImportRecord')->search( |
340 |
if ( $db_query ) { |
248 |
{ |
341 |
$hits = [ $schema->resultset('ImportRecord')->search( |
249 |
import_batch_id => $server->{extra}, |
342 |
$db_query, |
250 |
-or => [ |
343 |
{ |
251 |
{ 'import_biblios.title' => $match_condition }, |
344 |
columns => [ 'import_record_id', { record => 'marc' } ], |
252 |
{ 'import_biblios.author' => $match_condition }, |
345 |
join => [ 'import_biblios' ], |
253 |
{ 'import_biblios.isbn' => $match_condition }, |
346 |
offset => $self->{offset}, |
254 |
{ 'import_biblios.issn' => $match_condition }, |
347 |
result_class => 'DBIx::Class::ResultClass::HashRefInflator', |
255 |
], |
348 |
rows => $self->{fetch}, |
256 |
}, |
349 |
} |
257 |
{ |
350 |
)->all ]; |
258 |
join => [ qw( import_biblios ) ], |
351 |
|
259 |
rows => $self->{fetch}, |
352 |
$num_hits = $num_fetched = scalar @$hits; |
260 |
} |
353 |
} else { |
261 |
)->get_column( 'marc' )->all ]; |
354 |
$error = "Invalid search"; |
262 |
|
355 |
} |
263 |
$num_hits = $num_fetched = scalar @$hits; |
|
|
264 |
} else { |
356 |
} else { |
265 |
$num_hits = $results->size; |
357 |
$num_hits = $results->size; |
266 |
$num_fetched = ( $self->{offset} + $self->{fetch} ) < $num_hits ? $self->{fetch} : $num_hits; |
358 |
$num_fetched = ( $self->{offset} + $self->{fetch} ) < $num_hits ? $self->{fetch} : $num_hits; |
267 |
|
359 |
|
268 |
$hits = [ map { $_->raw() } @{ $results->records( $self->{offset}, $num_fetched, 1 ) } ]; |
360 |
$hits = [ map +{ record => $_->raw() }, @{ $results->records( $self->{offset}, $num_fetched, 1 ) } ]; |
|
|
361 |
|
362 |
$error = $connection->exception()->message() if ( !@$hits && $connection && $connection->exception() ); |
269 |
} |
363 |
} |
270 |
|
364 |
|
271 |
if ( !@$hits && $connection && $connection->exception() ) { |
365 |
if ( $error ) { |
272 |
store_fd { |
366 |
store_fd { |
273 |
error => $connection->exception(), |
367 |
error => $error, |
274 |
server => $server, |
368 |
server => $server, |
275 |
}, $writefh; |
369 |
}, $writefh; |
276 |
exit; |
370 |
exit; |
277 |
} |
371 |
} |
278 |
|
372 |
|
279 |
if ( $server->{type} eq 'koha' ) { |
373 |
if ( $server->{type} eq 'koha' ) { |
280 |
$hits = [ map { C4::Search::new_record_from_zebra( $server->{extra}, $_ ) } @$hits ]; |
374 |
$hits = [ map +{ %$_, record => C4::Search::new_record_from_zebra( $server->{extra}, $_->{record} ) }, @$hits ]; |
281 |
} else { |
375 |
} else { |
282 |
$hits = [ map { $self->_import_record( $_, $marcflavour, $server->{encoding} ? $server->{encoding} : "iso-5426" ) } @$hits ]; |
376 |
$hits = [ map +{ %$_, record => $self->_import_record( $_->{record}, $marcflavour, $server->{encoding} ? $server->{encoding} : "iso-5426" ) }, @$hits ]; |
283 |
} |
377 |
} |
284 |
|
378 |
|
285 |
store_fd { |
379 |
store_fd { |