Lines 68-74
This module provides searching functions for Koha's bibliographic databases
Link Here
|
68 |
&searchResults |
68 |
&searchResults |
69 |
&getRecords |
69 |
&getRecords |
70 |
&buildQuery |
70 |
&buildQuery |
71 |
&NZgetRecords |
|
|
72 |
&AddSearchHistory |
71 |
&AddSearchHistory |
73 |
&GetDistinctValues |
72 |
&GetDistinctValues |
74 |
&enabled_staff_search_views |
73 |
&enabled_staff_search_views |
Lines 223-313
$template->param(result=>\@results);
Link Here
|
223 |
sub SimpleSearch { |
222 |
sub SimpleSearch { |
224 |
my ( $query, $offset, $max_results, $servers ) = @_; |
223 |
my ( $query, $offset, $max_results, $servers ) = @_; |
225 |
|
224 |
|
226 |
if ( C4::Context->preference('NoZebra') ) { |
225 |
return ( 'No query entered', undef, undef ) unless $query; |
227 |
my $result = NZorder( NZanalyse($query) )->{'biblioserver'}; |
226 |
# FIXME hardcoded value. See catalog/search.pl & opac-search.pl too. |
228 |
my $search_result = |
227 |
my @servers = defined ( $servers ) ? @$servers : ( 'biblioserver' ); |
229 |
( $result->{hits} |
228 |
my @zoom_queries; |
230 |
&& $result->{hits} > 0 ? $result->{'RECORDS'} : [] ); |
229 |
my @tmpresults; |
231 |
return ( undef, $search_result, scalar($result->{hits}) ); |
230 |
my @zconns; |
232 |
} |
231 |
my $results = []; |
233 |
else { |
232 |
my $total_hits = 0; |
234 |
return ( 'No query entered', undef, undef ) unless $query; |
|
|
235 |
# FIXME hardcoded value. See catalog/search.pl & opac-search.pl too. |
236 |
my @servers = defined ( $servers ) ? @$servers : ( 'biblioserver' ); |
237 |
my @zoom_queries; |
238 |
my @tmpresults; |
239 |
my @zconns; |
240 |
my $results = []; |
241 |
my $total_hits = 0; |
242 |
|
233 |
|
243 |
my $QParser; |
234 |
my $QParser; |
244 |
$QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser') && ! ($query =~ m/\w,\w|\w=\w/)); |
235 |
$QParser = C4::Context->queryparser if (C4::Context->preference('UseQueryParser') && ! ($query =~ m/\w,\w|\w=\w/)); |
245 |
|
236 |
|
246 |
# Initialize & Search Zebra |
237 |
# Initialize & Search Zebra |
247 |
for ( my $i = 0 ; $i < @servers ; $i++ ) { |
238 |
for ( my $i = 0 ; $i < @servers ; $i++ ) { |
248 |
eval { |
239 |
eval { |
249 |
$zconns[$i] = C4::Context->Zconn( $servers[$i], 1 ); |
240 |
$zconns[$i] = C4::Context->Zconn( $servers[$i], 1 ); |
250 |
if ($QParser) { |
241 |
if ($QParser) { |
251 |
$query =~ s/=/:/g; |
242 |
$query =~ s/=/:/g; |
252 |
$QParser->parse( $query ); |
243 |
$QParser->parse( $query ); |
253 |
$query = $QParser->target_syntax($servers[$i]); |
244 |
$query = $QParser->target_syntax($servers[$i]); |
254 |
$zoom_queries[$i] = new ZOOM::Query::PQF( $query, $zconns[$i]); |
245 |
$zoom_queries[$i] = new ZOOM::Query::PQF( $query, $zconns[$i]); |
255 |
} else { |
246 |
} else { |
256 |
$query =~ s/:/=/g; |
247 |
$query =~ s/:/=/g; |
257 |
$zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]); |
248 |
$zoom_queries[$i] = new ZOOM::Query::CCL2RPN( $query, $zconns[$i]); |
258 |
} |
|
|
259 |
$tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] ); |
260 |
|
261 |
# error handling |
262 |
my $error = |
263 |
$zconns[$i]->errmsg() . " (" |
264 |
. $zconns[$i]->errcode() . ") " |
265 |
. $zconns[$i]->addinfo() . " " |
266 |
. $zconns[$i]->diagset(); |
267 |
|
268 |
return ( $error, undef, undef ) if $zconns[$i]->errcode(); |
269 |
}; |
270 |
if ($@) { |
271 |
|
272 |
# caught a ZOOM::Exception |
273 |
my $error = |
274 |
$@->message() . " (" |
275 |
. $@->code() . ") " |
276 |
. $@->addinfo() . " " |
277 |
. $@->diagset(); |
278 |
warn $error." for query: $query"; |
279 |
return ( $error, undef, undef ); |
280 |
} |
249 |
} |
281 |
} |
250 |
$tmpresults[$i] = $zconns[$i]->search( $zoom_queries[$i] ); |
282 |
|
251 |
|
283 |
_ZOOM_event_loop( |
252 |
# error handling |
284 |
\@zconns, |
253 |
my $error = |
285 |
\@tmpresults, |
254 |
$zconns[$i]->errmsg() . " (" |
286 |
sub { |
255 |
. $zconns[$i]->errcode() . ") " |
287 |
my ($i, $size) = @_; |
256 |
. $zconns[$i]->addinfo() . " " |
288 |
my $first_record = defined($offset) ? $offset + 1 : 1; |
257 |
. $zconns[$i]->diagset(); |
289 |
my $hits = $tmpresults[ $i - 1 ]->size(); |
|
|
290 |
$total_hits += $hits; |
291 |
my $last_record = $hits; |
292 |
if ( defined $max_results && $offset + $max_results < $hits ) { |
293 |
$last_record = $offset + $max_results; |
294 |
} |
295 |
|
258 |
|
296 |
for my $j ( $first_record .. $last_record ) { |
259 |
return ( $error, undef, undef ) if $zconns[$i]->errcode(); |
297 |
my $record = |
260 |
}; |
298 |
$tmpresults[ $i - 1 ]->record( $j - 1 )->raw() |
261 |
if ($@) { |
299 |
; # 0 indexed |
262 |
|
300 |
push @{$results}, $record; |
263 |
# caught a ZOOM::Exception |
301 |
} |
264 |
my $error = |
|
|
265 |
$@->message() . " (" |
266 |
. $@->code() . ") " |
267 |
. $@->addinfo() . " " |
268 |
. $@->diagset(); |
269 |
warn $error." for query: $query"; |
270 |
return ( $error, undef, undef ); |
271 |
} |
272 |
} |
273 |
|
274 |
_ZOOM_event_loop( |
275 |
\@zconns, |
276 |
\@tmpresults, |
277 |
sub { |
278 |
my ($i, $size) = @_; |
279 |
my $first_record = defined($offset) ? $offset + 1 : 1; |
280 |
my $hits = $tmpresults[ $i - 1 ]->size(); |
281 |
$total_hits += $hits; |
282 |
my $last_record = $hits; |
283 |
if ( defined $max_results && $offset + $max_results < $hits ) { |
284 |
$last_record = $offset + $max_results; |
302 |
} |
285 |
} |
303 |
); |
|
|
304 |
|
286 |
|
305 |
foreach my $zoom_query (@zoom_queries) { |
287 |
for my $j ( $first_record .. $last_record ) { |
306 |
$zoom_query->destroy(); |
288 |
my $record = |
|
|
289 |
$tmpresults[ $i - 1 ]->record( $j - 1 )->raw() |
290 |
; # 0 indexed |
291 |
push @{$results}, $record; |
292 |
} |
307 |
} |
293 |
} |
|
|
294 |
); |
308 |
|
295 |
|
309 |
return ( undef, $results, $total_hits ); |
296 |
foreach my $zoom_query (@zoom_queries) { |
|
|
297 |
$zoom_query->destroy(); |
310 |
} |
298 |
} |
|
|
299 |
|
300 |
return ( undef, $results, $total_hits ); |
311 |
} |
301 |
} |
312 |
|
302 |
|
313 |
=head2 getRecords |
303 |
=head2 getRecords |
Lines 2171-2772
sub SearchAcquisitions{
Link Here
|
2171 |
$qdataacquisitions->finish; |
2161 |
$qdataacquisitions->finish; |
2172 |
return \@loopacquisitions; |
2162 |
return \@loopacquisitions; |
2173 |
} |
2163 |
} |
2174 |
#---------------------------------------------------------------------- |
|
|
2175 |
# |
2176 |
# Non-Zebra GetRecords# |
2177 |
#---------------------------------------------------------------------- |
2178 |
|
2179 |
=head2 NZgetRecords |
2180 |
|
2181 |
NZgetRecords has the same API as zera getRecords, even if some parameters are not managed |
2182 |
|
2183 |
=cut |
2184 |
|
2185 |
sub NZgetRecords { |
2186 |
my ( |
2187 |
$query, $simple_query, $sort_by_ref, $servers_ref, |
2188 |
$results_per_page, $offset, $expanded_facet, $branches, |
2189 |
$query_type, $scan |
2190 |
) = @_; |
2191 |
warn "query =$query" if $DEBUG; |
2192 |
my $result = NZanalyse($query); |
2193 |
warn "results =$result" if $DEBUG; |
2194 |
return ( undef, |
2195 |
NZorder( $result, @$sort_by_ref[0], $results_per_page, $offset ), |
2196 |
undef ); |
2197 |
} |
2198 |
|
2199 |
=head2 NZanalyse |
2200 |
|
2201 |
NZanalyse : get a CQL string as parameter, and returns a list of biblionumber;title,biblionumber;title,... |
2202 |
the list is built from an inverted index in the nozebra SQL table |
2203 |
note that title is here only for convenience : the sorting will be very fast when requested on title |
2204 |
if the sorting is requested on something else, we will have to reread all results, and that may be longer. |
2205 |
|
2206 |
=cut |
2207 |
|
2208 |
sub NZanalyse { |
2209 |
my ( $string, $server ) = @_; |
2210 |
# warn "---------" if $DEBUG; |
2211 |
warn " NZanalyse" if $DEBUG; |
2212 |
# warn "---------" if $DEBUG; |
2213 |
|
2214 |
# $server contains biblioserver or authorities, depending on what we search on. |
2215 |
#warn "querying : $string on $server"; |
2216 |
$server = 'biblioserver' unless $server; |
2217 |
|
2218 |
# if we have a ", replace the content to discard temporarily any and/or/not inside |
2219 |
my $commacontent; |
2220 |
if ( $string =~ /"/ ) { |
2221 |
$string =~ s/"(.*?)"/__X__/; |
2222 |
$commacontent = $1; |
2223 |
warn "commacontent : $commacontent" if $DEBUG; |
2224 |
} |
2225 |
|
2226 |
# split the query string in 3 parts : X AND Y means : $left="X", $operand="AND" and $right="Y" |
2227 |
# then, call again NZanalyse with $left and $right |
2228 |
# (recursive until we find a leaf (=> something without and/or/not) |
2229 |
# delete repeated operator... Would then go in infinite loop |
2230 |
while ( $string =~ s/( and| or| not| AND| OR| NOT)\1/$1/g ) { |
2231 |
} |
2232 |
|
2233 |
#process parenthesis before. |
2234 |
if ( $string =~ /^\s*\((.*)\)(( and | or | not | AND | OR | NOT )(.*))?/ ) { |
2235 |
my $left = $1; |
2236 |
my $right = $4; |
2237 |
my $operator = lc($3); # FIXME: and/or/not are operators, not operands |
2238 |
warn |
2239 |
"dealing w/parenthesis before recursive sub call. left :$left operator:$operator right:$right" |
2240 |
if $DEBUG; |
2241 |
my $leftresult = NZanalyse( $left, $server ); |
2242 |
if ($operator) { |
2243 |
my $rightresult = NZanalyse( $right, $server ); |
2244 |
|
2245 |
# OK, we have the results for right and left part of the query |
2246 |
# depending of operand, intersect, union or exclude both lists |
2247 |
# to get a result list |
2248 |
if ( $operator eq ' and ' ) { |
2249 |
return NZoperatorAND($leftresult,$rightresult); |
2250 |
} |
2251 |
elsif ( $operator eq ' or ' ) { |
2252 |
|
2253 |
# just merge the 2 strings |
2254 |
return $leftresult . $rightresult; |
2255 |
} |
2256 |
elsif ( $operator eq ' not ' ) { |
2257 |
return NZoperatorNOT($leftresult,$rightresult); |
2258 |
} |
2259 |
} |
2260 |
else { |
2261 |
# this error is impossible, because of the regexp that isolate the operand, but just in case... |
2262 |
return $leftresult; |
2263 |
} |
2264 |
} |
2265 |
warn "string :" . $string if $DEBUG; |
2266 |
my $left = ""; |
2267 |
my $right = ""; |
2268 |
my $operator = ""; |
2269 |
if ($string =~ /(.*?)( and | or | not | AND | OR | NOT )(.*)/) { |
2270 |
$left = $1; |
2271 |
$right = $3; |
2272 |
$operator = lc($2); # FIXME: and/or/not are operators, not operands |
2273 |
} |
2274 |
warn "no parenthesis. left : $left operator: $operator right: $right" |
2275 |
if $DEBUG; |
2276 |
|
2277 |
# it's not a leaf, we have a and/or/not |
2278 |
if ($operator) { |
2279 |
|
2280 |
# reintroduce comma content if needed |
2281 |
$right =~ s/__X__/"$commacontent"/ if $commacontent; |
2282 |
$left =~ s/__X__/"$commacontent"/ if $commacontent; |
2283 |
warn "node : $left / $operator / $right\n" if $DEBUG; |
2284 |
my $leftresult = NZanalyse( $left, $server ); |
2285 |
my $rightresult = NZanalyse( $right, $server ); |
2286 |
warn " leftresult : $leftresult" if $DEBUG; |
2287 |
warn " rightresult : $rightresult" if $DEBUG; |
2288 |
# OK, we have the results for right and left part of the query |
2289 |
# depending of operand, intersect, union or exclude both lists |
2290 |
# to get a result list |
2291 |
if ( $operator eq ' and ' ) { |
2292 |
return NZoperatorAND($leftresult,$rightresult); |
2293 |
} |
2294 |
elsif ( $operator eq ' or ' ) { |
2295 |
|
2296 |
# just merge the 2 strings |
2297 |
return $leftresult . $rightresult; |
2298 |
} |
2299 |
elsif ( $operator eq ' not ' ) { |
2300 |
return NZoperatorNOT($leftresult,$rightresult); |
2301 |
} |
2302 |
else { |
2303 |
|
2304 |
# this error is impossible, because of the regexp that isolate the operand, but just in case... |
2305 |
die "error : operand unknown : $operator for $string"; |
2306 |
} |
2307 |
|
2308 |
# it's a leaf, do the real SQL query and return the result |
2309 |
} |
2310 |
else { |
2311 |
$string =~ s/__X__/"$commacontent"/ if $commacontent; |
2312 |
$string =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|&|\+|\*|\// /g; |
2313 |
#remove trailing blank at the beginning |
2314 |
$string =~ s/^ //g; |
2315 |
warn "leaf:$string" if $DEBUG; |
2316 |
|
2317 |
# parse the string in in operator/operand/value again |
2318 |
my $left = ""; |
2319 |
my $operator = ""; |
2320 |
my $right = ""; |
2321 |
if ($string =~ /(.*)(>=|<=)(.*)/) { |
2322 |
$left = $1; |
2323 |
$operator = $2; |
2324 |
$right = $3; |
2325 |
} else { |
2326 |
$left = $string; |
2327 |
} |
2328 |
# warn "handling leaf... left:$left operator:$operator right:$right" |
2329 |
# if $DEBUG; |
2330 |
unless ($operator) { |
2331 |
if ($string =~ /(.*)(>|<|=)(.*)/) { |
2332 |
$left = $1; |
2333 |
$operator = $2; |
2334 |
$right = $3; |
2335 |
warn |
2336 |
"handling unless (operator)... left:$left operator:$operator right:$right" |
2337 |
if $DEBUG; |
2338 |
} else { |
2339 |
$left = $string; |
2340 |
} |
2341 |
} |
2342 |
my $results; |
2343 |
|
2344 |
# strip adv, zebra keywords, currently not handled in nozebra: wrdl, ext, phr... |
2345 |
$left =~ s/ .*$//; |
2346 |
|
2347 |
# automatic replace for short operators |
2348 |
$left = 'title' if $left =~ '^ti$'; |
2349 |
$left = 'author' if $left =~ '^au$'; |
2350 |
$left = 'publisher' if $left =~ '^pb$'; |
2351 |
$left = 'subject' if $left =~ '^su$'; |
2352 |
$left = 'koha-Auth-Number' if $left =~ '^an$'; |
2353 |
$left = 'keyword' if $left =~ '^kw$'; |
2354 |
$left = 'itemtype' if $left =~ '^mc$'; # Fix for Bug 2599 - Search limits not working for NoZebra |
2355 |
warn "handling leaf... left:$left operator:$operator right:$right" if $DEBUG; |
2356 |
my $dbh = C4::Context->dbh; |
2357 |
if ( $operator && $left ne 'keyword' ) { |
2358 |
#do a specific search |
2359 |
$operator = 'LIKE' if $operator eq '=' and $right =~ /%/; |
2360 |
my $sth = $dbh->prepare( |
2361 |
"SELECT biblionumbers,value FROM nozebra WHERE server=? AND indexname=? AND value $operator ?" |
2362 |
); |
2363 |
warn "$left / $operator / $right\n" if $DEBUG; |
2364 |
|
2365 |
# split each word, query the DB and build the biblionumbers result |
2366 |
#sanitizing leftpart |
2367 |
$left =~ s/^\s+|\s+$//; |
2368 |
foreach ( split / /, $right ) { |
2369 |
my $biblionumbers; |
2370 |
$_ =~ s/^\s+|\s+$//; |
2371 |
next unless $_; |
2372 |
warn "EXECUTE : $server, $left, $_" if $DEBUG; |
2373 |
$sth->execute( $server, $left, $_ ) |
2374 |
or warn "execute failed: $!"; |
2375 |
while ( my ( $line, $value ) = $sth->fetchrow ) { |
2376 |
|
2377 |
# if we are dealing with a numeric value, use only numeric results (in case of >=, <=, > or <) |
2378 |
# otherwise, fill the result |
2379 |
$biblionumbers .= $line |
2380 |
unless ( $right =~ /^\d+$/ && $value =~ /\D/ ); |
2381 |
warn "result : $value " |
2382 |
. ( $right =~ /\d/ ) . "==" |
2383 |
. ( $value =~ /\D/?$line:"" ) if $DEBUG; #= $line"; |
2384 |
} |
2385 |
|
2386 |
# do a AND with existing list if there is one, otherwise, use the biblionumbers list as 1st result list |
2387 |
if ($results) { |
2388 |
warn "NZAND" if $DEBUG; |
2389 |
$results = NZoperatorAND($biblionumbers,$results); |
2390 |
} else { |
2391 |
$results = $biblionumbers; |
2392 |
} |
2393 |
} |
2394 |
} |
2395 |
else { |
2396 |
#do a complete search (all indexes), if index='kw' do complete search too. |
2397 |
my $sth = $dbh->prepare( |
2398 |
"SELECT biblionumbers FROM nozebra WHERE server=? AND value LIKE ?" |
2399 |
); |
2400 |
|
2401 |
# split each word, query the DB and build the biblionumbers result |
2402 |
foreach ( split / /, $string ) { |
2403 |
next if C4::Context->stopwords->{ uc($_) }; # skip if stopword |
2404 |
warn "search on all indexes on $_" if $DEBUG; |
2405 |
my $biblionumbers; |
2406 |
next unless $_; |
2407 |
$sth->execute( $server, $_ ); |
2408 |
while ( my $line = $sth->fetchrow ) { |
2409 |
$biblionumbers .= $line; |
2410 |
} |
2411 |
|
2412 |
# do a AND with existing list if there is one, otherwise, use the biblionumbers list as 1st result list |
2413 |
if ($results) { |
2414 |
$results = NZoperatorAND($biblionumbers,$results); |
2415 |
} |
2416 |
else { |
2417 |
warn "NEW RES for $_ = $biblionumbers" if $DEBUG; |
2418 |
$results = $biblionumbers; |
2419 |
} |
2420 |
} |
2421 |
} |
2422 |
warn "return : $results for LEAF : $string" if $DEBUG; |
2423 |
return $results; |
2424 |
} |
2425 |
warn "---------\nLeave NZanalyse\n---------" if $DEBUG; |
2426 |
} |
2427 |
|
2428 |
sub NZoperatorAND{ |
2429 |
my ($rightresult, $leftresult)=@_; |
2430 |
|
2431 |
my @leftresult = split /;/, $leftresult; |
2432 |
warn " @leftresult / $rightresult \n" if $DEBUG; |
2433 |
|
2434 |
# my @rightresult = split /;/,$leftresult; |
2435 |
my $finalresult; |
2436 |
|
2437 |
# parse the left results, and if the biblionumber exist in the right result, save it in finalresult |
2438 |
# the result is stored twice, to have the same weight for AND than OR. |
2439 |
# example : TWO : 61,61,64,121 (two is twice in the biblio #61) / TOWER : 61,64,130 |
2440 |
# result : 61,61,61,61,64,64 for two AND tower : 61 has more weight than 64 |
2441 |
foreach (@leftresult) { |
2442 |
my $value = $_; |
2443 |
my $countvalue; |
2444 |
( $value, $countvalue ) = ( $1, $2 ) if ($value=~/(.*)-(\d+)$/); |
2445 |
if ( $rightresult =~ /\Q$value\E-(\d+);/ ) { |
2446 |
$countvalue = ( $1 > $countvalue ? $countvalue : $1 ); |
2447 |
$finalresult .= |
2448 |
"$value-$countvalue;$value-$countvalue;"; |
2449 |
} |
2450 |
} |
2451 |
warn "NZAND DONE : $finalresult \n" if $DEBUG; |
2452 |
return $finalresult; |
2453 |
} |
2454 |
|
2455 |
sub NZoperatorOR{ |
2456 |
my ($rightresult, $leftresult)=@_; |
2457 |
return $rightresult.$leftresult; |
2458 |
} |
2459 |
|
2460 |
sub NZoperatorNOT{ |
2461 |
my ($leftresult, $rightresult)=@_; |
2462 |
|
2463 |
my @leftresult = split /;/, $leftresult; |
2464 |
|
2465 |
# my @rightresult = split /;/,$leftresult; |
2466 |
my $finalresult; |
2467 |
foreach (@leftresult) { |
2468 |
my $value=$_; |
2469 |
$value=$1 if $value=~m/(.*)-\d+$/; |
2470 |
unless ($rightresult =~ "$value-") { |
2471 |
$finalresult .= "$_;"; |
2472 |
} |
2473 |
} |
2474 |
return $finalresult; |
2475 |
} |
2476 |
|
2477 |
=head2 NZorder |
2478 |
|
2479 |
$finalresult = NZorder($biblionumbers, $ordering,$results_per_page,$offset); |
2480 |
|
2481 |
TODO :: Description |
2482 |
|
2483 |
=cut |
2484 |
|
2485 |
sub NZorder { |
2486 |
my ( $biblionumbers, $ordering, $results_per_page, $offset ) = @_; |
2487 |
warn "biblionumbers = $biblionumbers and ordering = $ordering\n" if $DEBUG; |
2488 |
|
2489 |
# order title asc by default |
2490 |
# $ordering = '1=36 <i' unless $ordering; |
2491 |
$results_per_page = 20 unless $results_per_page; |
2492 |
$offset = 0 unless $offset; |
2493 |
my $dbh = C4::Context->dbh; |
2494 |
|
2495 |
# |
2496 |
# order by POPULARITY |
2497 |
# |
2498 |
if ( $ordering =~ /popularity/ ) { |
2499 |
my %result; |
2500 |
my %popularity; |
2501 |
|
2502 |
# popularity is not in MARC record, it's builded from a specific query |
2503 |
my $sth = |
2504 |
$dbh->prepare("select sum(issues) from items where biblionumber=?"); |
2505 |
foreach ( split /;/, $biblionumbers ) { |
2506 |
my ( $biblionumber, $title ) = split /,/, $_; |
2507 |
$result{$biblionumber} = GetMarcBiblio($biblionumber); |
2508 |
$sth->execute($biblionumber); |
2509 |
my $popularity = $sth->fetchrow || 0; |
2510 |
|
2511 |
# hint : the key is popularity.title because we can have |
2512 |
# many results with the same popularity. In this case, sub-ordering is done by title |
2513 |
# we also have biblionumber to avoid bug for 2 biblios with the same title & popularity |
2514 |
# (un-frequent, I agree, but we won't forget anything that way ;-) |
2515 |
$popularity{ sprintf( "%10d", $popularity ) . $title |
2516 |
. $biblionumber } = $biblionumber; |
2517 |
} |
2518 |
|
2519 |
# sort the hash and return the same structure as GetRecords (Zebra querying) |
2520 |
my $result_hash; |
2521 |
my $numbers = 0; |
2522 |
if ( $ordering eq 'popularity_dsc' ) { # sort popularity DESC |
2523 |
foreach my $key ( sort { $b cmp $a } ( keys %popularity ) ) { |
2524 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2525 |
$result{ $popularity{$key} }->as_usmarc(); |
2526 |
} |
2527 |
} |
2528 |
else { # sort popularity ASC |
2529 |
foreach my $key ( sort ( keys %popularity ) ) { |
2530 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2531 |
$result{ $popularity{$key} }->as_usmarc(); |
2532 |
} |
2533 |
} |
2534 |
my $finalresult = (); |
2535 |
$result_hash->{'hits'} = $numbers; |
2536 |
$finalresult->{'biblioserver'} = $result_hash; |
2537 |
return $finalresult; |
2538 |
|
2539 |
# |
2540 |
# ORDER BY author |
2541 |
# |
2542 |
} |
2543 |
elsif ( $ordering =~ /author/ ) { |
2544 |
my %result; |
2545 |
foreach ( split /;/, $biblionumbers ) { |
2546 |
my ( $biblionumber, $title ) = split /,/, $_; |
2547 |
my $record = GetMarcBiblio($biblionumber); |
2548 |
my $author; |
2549 |
if ( C4::Context->preference('marcflavour') eq 'UNIMARC' ) { |
2550 |
$author = $record->subfield( '200', 'f' ); |
2551 |
$author = $record->subfield( '700', 'a' ) unless $author; |
2552 |
} |
2553 |
else { |
2554 |
$author = $record->subfield( '100', 'a' ); |
2555 |
} |
2556 |
|
2557 |
# hint : the result is sorted by title.biblionumber because we can have X biblios with the same title |
2558 |
# and we don't want to get only 1 result for each of them !!! |
2559 |
$result{ $author . $biblionumber } = $record; |
2560 |
} |
2561 |
|
2562 |
# sort the hash and return the same structure as GetRecords (Zebra querying) |
2563 |
my $result_hash; |
2564 |
my $numbers = 0; |
2565 |
if ( $ordering eq 'author_za' || $ordering eq 'author_dsc' ) { # sort by author desc |
2566 |
foreach my $key ( sort { $b cmp $a } ( keys %result ) ) { |
2567 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2568 |
$result{$key}->as_usmarc(); |
2569 |
} |
2570 |
} |
2571 |
else { # sort by author ASC |
2572 |
foreach my $key ( sort ( keys %result ) ) { |
2573 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2574 |
$result{$key}->as_usmarc(); |
2575 |
} |
2576 |
} |
2577 |
my $finalresult = (); |
2578 |
$result_hash->{'hits'} = $numbers; |
2579 |
$finalresult->{'biblioserver'} = $result_hash; |
2580 |
return $finalresult; |
2581 |
|
2582 |
# |
2583 |
# ORDER BY callnumber |
2584 |
# |
2585 |
} |
2586 |
elsif ( $ordering =~ /callnumber/ ) { |
2587 |
my %result; |
2588 |
foreach ( split /;/, $biblionumbers ) { |
2589 |
my ( $biblionumber, $title ) = split /,/, $_; |
2590 |
my $record = GetMarcBiblio($biblionumber); |
2591 |
my $callnumber; |
2592 |
my $frameworkcode = GetFrameworkCode($biblionumber); |
2593 |
my ( $callnumber_tag, $callnumber_subfield ) = GetMarcFromKohaField( 'items.itemcallnumber', $frameworkcode); |
2594 |
( $callnumber_tag, $callnumber_subfield ) = GetMarcFromKohaField('biblioitems.callnumber', $frameworkcode) |
2595 |
unless $callnumber_tag; |
2596 |
if ( C4::Context->preference('marcflavour') eq 'UNIMARC' ) { |
2597 |
$callnumber = $record->subfield( '200', 'f' ); |
2598 |
} else { |
2599 |
$callnumber = $record->subfield( '100', 'a' ); |
2600 |
} |
2601 |
|
2602 |
# hint : the result is sorted by title.biblionumber because we can have X biblios with the same title |
2603 |
# and we don't want to get only 1 result for each of them !!! |
2604 |
$result{ $callnumber . $biblionumber } = $record; |
2605 |
} |
2606 |
|
2607 |
# sort the hash and return the same structure as GetRecords (Zebra querying) |
2608 |
my $result_hash; |
2609 |
my $numbers = 0; |
2610 |
if ( $ordering eq 'call_number_dsc' ) { # sort by title desc |
2611 |
foreach my $key ( sort { $b cmp $a } ( keys %result ) ) { |
2612 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2613 |
$result{$key}->as_usmarc(); |
2614 |
} |
2615 |
} |
2616 |
else { # sort by title ASC |
2617 |
foreach my $key ( sort { $a cmp $b } ( keys %result ) ) { |
2618 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2619 |
$result{$key}->as_usmarc(); |
2620 |
} |
2621 |
} |
2622 |
my $finalresult = (); |
2623 |
$result_hash->{'hits'} = $numbers; |
2624 |
$finalresult->{'biblioserver'} = $result_hash; |
2625 |
return $finalresult; |
2626 |
} |
2627 |
elsif ( $ordering =~ /pubdate/ ) { #pub year |
2628 |
my %result; |
2629 |
foreach ( split /;/, $biblionumbers ) { |
2630 |
my ( $biblionumber, $title ) = split /,/, $_; |
2631 |
my $record = GetMarcBiblio($biblionumber); |
2632 |
my ( $publicationyear_tag, $publicationyear_subfield ) = |
2633 |
GetMarcFromKohaField( 'biblioitems.publicationyear', '' ); |
2634 |
my $publicationyear = |
2635 |
$record->subfield( $publicationyear_tag, |
2636 |
$publicationyear_subfield ); |
2637 |
|
2638 |
# hint : the result is sorted by title.biblionumber because we can have X biblios with the same title |
2639 |
# and we don't want to get only 1 result for each of them !!! |
2640 |
$result{ $publicationyear . $biblionumber } = $record; |
2641 |
} |
2642 |
|
2643 |
# sort the hash and return the same structure as GetRecords (Zebra querying) |
2644 |
my $result_hash; |
2645 |
my $numbers = 0; |
2646 |
if ( $ordering eq 'pubdate_dsc' ) { # sort by pubyear desc |
2647 |
foreach my $key ( sort { $b cmp $a } ( keys %result ) ) { |
2648 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2649 |
$result{$key}->as_usmarc(); |
2650 |
} |
2651 |
} |
2652 |
else { # sort by pub year ASC |
2653 |
foreach my $key ( sort ( keys %result ) ) { |
2654 |
$result_hash->{'RECORDS'}[ $numbers++ ] = |
2655 |
$result{$key}->as_usmarc(); |
2656 |
} |
2657 |
} |
2658 |
my $finalresult = (); |
2659 |
$result_hash->{'hits'} = $numbers; |
2660 |
$finalresult->{'biblioserver'} = $result_hash; |
2661 |
return $finalresult; |
2662 |
|
2663 |
# |
2664 |
# ORDER BY title |
2665 |
# |
2666 |
} |
2667 |
elsif ( $ordering =~ /title/ ) { |
2668 |
|
2669 |
# the title is in the biblionumbers string, so we just need to build a hash, sort it and return |
2670 |
my %result; |
2671 |
foreach ( split /;/, $biblionumbers ) { |
2672 |
my ( $biblionumber, $title ) = split /,/, $_; |
2673 |
|
2674 |
# hint : the result is sorted by title.biblionumber because we can have X biblios with the same title |
2675 |
# and we don't want to get only 1 result for each of them !!! |
2676 |
# hint & speed improvement : we can order without reading the record |
2677 |
# so order, and read records only for the requested page ! |
2678 |
$result{ $title . $biblionumber } = $biblionumber; |
2679 |
} |
2680 |
|
2681 |
# sort the hash and return the same structure as GetRecords (Zebra querying) |
2682 |
my $result_hash; |
2683 |
my $numbers = 0; |
2684 |
if ( $ordering eq 'title_az' ) { # sort by title desc |
2685 |
foreach my $key ( sort ( keys %result ) ) { |
2686 |
$result_hash->{'RECORDS'}[ $numbers++ ] = $result{$key}; |
2687 |
} |
2688 |
} |
2689 |
else { # sort by title ASC |
2690 |
foreach my $key ( sort { $b cmp $a } ( keys %result ) ) { |
2691 |
$result_hash->{'RECORDS'}[ $numbers++ ] = $result{$key}; |
2692 |
} |
2693 |
} |
2694 |
|
2695 |
# limit the $results_per_page to result size if it's more |
2696 |
$results_per_page = $numbers - 1 if $numbers < $results_per_page; |
2697 |
|
2698 |
# for the requested page, replace biblionumber by the complete record |
2699 |
# speed improvement : avoid reading too much things |
2700 |
for ( |
2701 |
my $counter = $offset ; |
2702 |
$counter <= $offset + $results_per_page ; |
2703 |
$counter++ |
2704 |
) |
2705 |
{ |
2706 |
$result_hash->{'RECORDS'}[$counter] = |
2707 |
GetMarcBiblio( $result_hash->{'RECORDS'}[$counter] )->as_usmarc; |
2708 |
} |
2709 |
my $finalresult = (); |
2710 |
$result_hash->{'hits'} = $numbers; |
2711 |
$finalresult->{'biblioserver'} = $result_hash; |
2712 |
return $finalresult; |
2713 |
} |
2714 |
else { |
2715 |
|
2716 |
# |
2717 |
# order by ranking |
2718 |
# |
2719 |
# we need 2 hashes to order by ranking : the 1st one to count the ranking, the 2nd to order by ranking |
2720 |
my %result; |
2721 |
my %count_ranking; |
2722 |
foreach ( split /;/, $biblionumbers ) { |
2723 |
my ( $biblionumber, $title ) = split /,/, $_; |
2724 |
$title =~ /(.*)-(\d)/; |
2725 |
|
2726 |
# get weight |
2727 |
my $ranking = $2; |
2728 |
|
2729 |
# note that we + the ranking because ranking is calculated on weight of EACH term requested. |
2730 |
# if we ask for "two towers", and "two" has weight 2 in biblio N, and "towers" has weight 4 in biblio N |
2731 |
# biblio N has ranking = 6 |
2732 |
$count_ranking{$biblionumber} += $ranking; |
2733 |
} |
2734 |
|
2735 |
# build the result by "inverting" the count_ranking hash |
2736 |
# hing : as usual, we don't order by ranking only, to avoid having only 1 result for each rank. We build an hash on concat(ranking,biblionumber) instead |
2737 |
# warn "counting"; |
2738 |
foreach ( keys %count_ranking ) { |
2739 |
$result{ sprintf( "%10d", $count_ranking{$_} ) . '-' . $_ } = $_; |
2740 |
} |
2741 |
|
2742 |
# sort the hash and return the same structure as GetRecords (Zebra querying) |
2743 |
my $result_hash; |
2744 |
my $numbers = 0; |
2745 |
foreach my $key ( sort { $b cmp $a } ( keys %result ) ) { |
2746 |
$result_hash->{'RECORDS'}[ $numbers++ ] = $result{$key}; |
2747 |
} |
2748 |
|
2749 |
# limit the $results_per_page to result size if it's more |
2750 |
$results_per_page = $numbers - 1 if $numbers < $results_per_page; |
2751 |
|
2752 |
# for the requested page, replace biblionumber by the complete record |
2753 |
# speed improvement : avoid reading too much things |
2754 |
for ( |
2755 |
my $counter = $offset ; |
2756 |
$counter <= $offset + $results_per_page ; |
2757 |
$counter++ |
2758 |
) |
2759 |
{ |
2760 |
$result_hash->{'RECORDS'}[$counter] = |
2761 |
GetMarcBiblio( $result_hash->{'RECORDS'}[$counter] )->as_usmarc |
2762 |
if $result_hash->{'RECORDS'}[$counter]; |
2763 |
} |
2764 |
my $finalresult = (); |
2765 |
$result_hash->{'hits'} = $numbers; |
2766 |
$finalresult->{'biblioserver'} = $result_hash; |
2767 |
return $finalresult; |
2768 |
} |
2769 |
} |
2770 |
|
2164 |
|
2771 |
=head2 enabled_staff_search_views |
2165 |
=head2 enabled_staff_search_views |
2772 |
|
2166 |
|