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