|
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 |
|