add call to doc-head-open.inc and doc-head-close.inc
[koha.git] / C4 / SearchBiblio.pm
1 package C4::SearchBiblio;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along with
17 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
18 # Suite 330, Boston, MA  02111-1307 USA
19
20 use strict;
21 require Exporter;
22 use DBI;
23 use C4::Context;
24 use C4::Biblio;
25 use C4::Date;
26 use Date::Manip;
27
28 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
29
30 # set the version for version checking
31 $VERSION = 0.02;
32
33 =head1 NAME
34
35 C4::Search - Functions for searching the Koha MARC catalog
36
37 =head1 FUNCTIONS
38
39 This module provides the searching facilities for the Koha MARC catalog
40
41 =cut
42
43 @ISA = qw(Exporter);
44 @EXPORT = qw(&catalogsearch1 &catalogsearch &findseealso &findsuggestion &getMARCnotes &getMARCsubjects);
45
46 =head1 findsuggestion($dbh,$values);
47
48 =head2 $dbh is a link to the DB handler.
49
50 use C4::Context;
51 my $dbh =C4::Context->dbh;
52
53 =head2 $values is a word
54
55 Searches words with the same soundex, ordered by frequency of use.
56 Useful to suggest other searches to the users.
57
58 =cut
59
60 sub findsuggestion {
61         my ($dbh,$values) = @_;
62         my $sth = $dbh->prepare("SELECT count( * ) AS total, word FROM marc_word WHERE sndx_word = soundex( ? ) AND word <> ? GROUP BY word ORDER BY total DESC");
63         my @results;
64         for(my $i = 0 ; $i <= $#{$values} ; $i++) {
65                 if (length(@$values[$i]) >=5) {
66                         $sth->execute(@$values[$i],@$values[$i]);
67                         my $resfound = 1;
68                         my @resline;
69                         while ((my ($count,$word) = $sth->fetchrow) and $resfound <=10) {
70                                 push @results, "@$values[$i]|$word|$count";
71 #                               $results{@$values[$i]} = \@resline;
72                                 $resfound++;
73                         }
74                 }
75         }
76         return \@results;
77 }
78
79 =head1 findseealso($dbh,$fields);
80
81 =head2 $dbh is a link to the DB handler.
82
83 use C4::Context;
84 my $dbh =C4::Context->dbh;
85
86 =head2 $fields is a reference to the fields array
87
88 This function modify the @$fields array and add related fields to search on.
89
90 =cut
91
92 sub findseealso {
93         my ($dbh, $fields) = @_;
94         my $tagslib = MARCgettagslib ($dbh,1);
95         for (my $i=0;$i<=$#{$fields};$i++) {
96                 my ($tag) =substr(@$fields[$i],1,3);
97                 my ($subfield) =substr(@$fields[$i],4,1);
98                 @$fields[$i].=','.$tagslib->{$tag}->{$subfield}->{seealso} if ($tagslib->{$tag}->{$subfield}->{seealso});
99         }
100 }
101
102 =head1  my ($count, @results) = catalogsearch($dbh, $tags, $and_or, $excluding, $operator, $value, $offset,$length,$orderby,$sqlstring);
103
104 =head2 $dbh is a link to the DB handler.
105
106 use C4::Context;
107 my $dbh =C4::Context->dbh;
108
109 $tags,$and_or, $excluding, $operator, $value are references to array
110
111 =head2 $tags
112
113 contains the list of tags+subfields (for example : $@tags[0] = '200a')
114 A field can be a list of fields : '200f','700a','700b','701a','701b'
115
116 Example
117
118 =head2 $and_or
119
120 contains  a list of strings containing and or or. The 1st value is useless.
121
122 =head2 $excluding
123
124 contains 0 or 1. If 1, then the request is negated.
125
126 =head2 $operator
127
128 contains contains,=,start,>,>=,<,<= the = and start work on the complete subfield. The contains operator works on every word in the subfield.
129
130 examples :
131 contains home, search home anywhere.
132 = home, search a string being home.
133
134 =head2 $value
135
136 contains the value to search
137 If it contains a * or a %, then the search is partial.
138
139 =head2 $offset and $length
140
141 returns $length results, beginning at $offset
142
143 =head2 $orderby
144
145 define the field used to order the request. Any field in the biblio/biblioitem tables can be used. DESC is possible too
146
147 (for example title, title DESC,...)
148
149 =head2 $sqlstring
150
151 optional argument containing an sql string to be used in the 'where' statement. see usage in opac-search.pl.
152
153 =head2 RETURNS
154
155 returns an array containing hashes. The hash contains all biblio & biblioitems fields and a reference to an item hash. The "item hash contains one line for each callnumber & the number of items related to the callnumber.
156
157 =cut
158
159 =head2 my $marcnotesarray = &getMARCnotes($dbh,$bibid,$marcflavour);
160
161 Returns a reference to an array containing all the notes stored in the MARC database for the given bibid.
162 $marcflavour ("MARC21" or "UNIMARC") determines which tags are used for retrieving subjects.
163
164 =head2 my $marcsubjctsarray = &getMARCsubjects($dbh,$bibid,$marcflavour);
165
166 Returns a reference to an array containing all the subjects stored in the MARC database for the given bibid.
167 $marcflavour ("MARC21" or "UNIMARC") determines which tags are used for retrieving subjects.
168
169 =cut
170
171 sub catalogsearch1 {
172         my ($dbh, $tags, $and_or, $excluding, $operator, $value, $offset,$length,$orderby,$desc_or_asc,$sqlstring) = @_;
173 #    warn "==================";
174 #    warn "
175 #                  db: $dbh,
176 #                  tags_array: @$tags,
177 #                  andor_array: @$and_or,
178 #                  excludes_array: @$excluding, 
179 #                  operator_array: @$operator, 
180 #                  value_array: @$value,
181 #                  start: $offset,
182 #                  resultsperpage: $length,
183 #                  orderby: $orderby,
184 #                  order: $desc_or_asc,  
185 #                  sqlstring: $sqlstring)\n";
186 #    warn "==================\n";
187
188     my @cols = ('biblionumber','author','title','unititle','notes','serial','seriestitle',
189                 'copyrightdate','timestamp','abstract','illus','biblioitemnumber','marc',
190                 'url','isbn','volumeddesc','classification','publicationyear','pages','number',
191                 'itemtype','place','issn','size','dewey','publishercode','lccn','volume',
192                 'subclass', 'volumedate','subtitle','bibid','notforloan',);
193                 # missing 'CN', 'description', 'odd', 'bn', 'norequests', 'totitem', 
194     my @valarray = @$value;
195 #    warn "@$value\n";
196 #    warn "$valarray[0]\n";
197     my $sql = "
198       SELECT biblio.biblionumber, biblio.author, biblio.title, biblio.unititle,
199         biblio.notes, biblio.serial, biblio.seriestitle, biblio.copyrightdate,
200         biblio.timestamp, biblio.abstract,
201         biblioitems.illus, biblioitems.biblioitemnumber, biblioitems.marc,
202         biblioitems.url, biblioitems.isbn, biblioitems.volumeddesc,
203         biblioitems.classification, biblioitems.publicationyear,
204         biblioitems.pages, biblioitems.number, biblioitems.itemtype,
205         biblioitems.place, biblioitems.issn, biblioitems.size,
206         biblioitems.dewey, biblioitems.publishercode, biblioitems.lccn,
207         biblioitems.volume, biblioitems.subclass, biblioitems.volumedate,
208         bibliosubtitle.subtitle,
209         marc_biblio.bibid,
210         items.notforloan, 
211         MATCH(biblio.title,biblio.author,biblio.unititle,biblio.seriestitle) 
212         AGAINST ('$$value[0]' IN BOOLEAN MODE) as Relevance
213       FROM biblio
214         LEFT JOIN biblioitems ON biblioitems.biblionumber=biblio.biblionumber
215         LEFT JOIN bibliosubtitle ON bibliosubtitle.biblionumber=biblio.biblionumber
216         LEFT JOIN marc_biblio ON marc_biblio.biblionumber=biblio.biblionumber
217         LEFT JOIN items ON items.biblionumber=biblio.biblionumber
218       WHERE MATCH(biblio.title,biblio.author,biblio.unititle,biblio.seriestitle) 
219         AGAINST ('$$value[0]' IN BOOLEAN MODE)
220       ORDER BY Relevance DESC;";
221     warn "$sql\n";
222     my $sth = $dbh->prepare($sql);
223         $sth->execute;
224     my @biblioArray=();
225     my $numBooks=0;
226     while (my @vals = $sth->fetchrow) {
227       my $numcols = $#vals;
228       my %biblioEntryHash=();
229       for(my $i=0; $i<$numcols; $i++) {
230         $biblioEntryHash{$cols[$i]} = $vals[$i];
231       }
232       $biblioEntryHash{odd} = ((($numBooks+1) % 2) > 0) ? 1 : ""; 
233       #FIXME
234       $biblioEntryHash{notforloan} = "";
235       #warn "\$biblioEntryHash{odd}  = .$biblioEntryHash{odd}.\n";
236       push(@biblioArray,\%biblioEntryHash);
237       $numBooks++;
238     }
239
240
241 # CN: ARRAY(0x89d1540)?  branch + location + callnumber + status
242 #                       CDI SL (N8KIM) (2) (if several, group them)
243 # description: ?
244 # odd: 1 ?
245 # bn: 501? biblionumber?
246 # norequests: 0? 
247 # totitem: 1?
248
249 #    my ($res,$numres) = catalogsearch(@_);
250 #    my @results = @$res;
251 #    warn "==================\n";
252 #    warn "\n\tres: @$res:,\n\tnumres: $numres\n";
253 #    while ( (my ($key, $value) = each(%{$results[0]})) && (my ($key1, $value1) = each(%{$biblioArray[0]})) ) {
254 #      warn "\t$key => $value\t$key1 => $value1\n";
255 #    }
256 #    warn "a. " . $results[0]->{odd} . "\t" . $biblioArray[0]->{odd}. "\n";
257 #    warn "b. " . $results[1]->{odd} . "\t" . $biblioArray[1]->{odd}. "\n";
258 #    warn "==================\n";
259     #return ($res,$numres);
260     return (\@biblioArray,$numBooks);
261 }
262
263 sub catalogsearch {
264         my ($dbh, $tags, $and_or, $excluding, $operator, $value, $offset,$length,$orderby,$desc_or_asc,$sqlstring) = @_;
265         # build the sql request. She will look like :
266         # select m1.bibid
267         #               from marc_subfield_table as m1, marc_subfield_table as m2
268         #               where m1.bibid=m2.bibid and
269         #               (m1.subfieldvalue like "Des%" and m2.subfieldvalue like "27%")
270
271         # last minute stripping out of stuff
272         # doesn't work @$value =~ s/\'/ /;
273         # @$value = map { $_ =~ s/\'/ /g } @$value;
274         
275         # "Normal" statements
276         my @normal_tags = ();
277         my @normal_and_or = ();
278         my @normal_operator = ();
279         my @normal_value = ();
280         # Extracts the NOT statements from the list of statements
281         my @not_tags = ();
282         my @not_and_or = ();
283         my @not_operator = ();
284         my @not_value = ();
285         my $any_not = 0;
286         $orderby = "biblio.title" unless $orderby;
287         $desc_or_asc = "ASC" unless $desc_or_asc;
288         #last minute stripping out of ' and ,
289 # paul : quoting, it's done a few lines lated.
290 #       foreach $_ (@$value) {
291 #               $_=~ s/\'/ /g;
292 #               $_=~ s/\,/ /g;
293 #       }
294
295 # the item.notforloan contains an integer. Every value <>0 means "book unavailable for loan".
296 # but each library can have it's own table of meaning for each value. Get them
297 # 1st search if there is a list of authorised values connected to items.notforloan
298         my $sth = $dbh->prepare('select authorised_value from marc_subfield_structure where kohafield="items.notforloan"');
299         $sth->execute;
300         my %notforloanstatus;
301         my ($authorised_valuecode) = $sth->fetchrow;
302         if ($authorised_valuecode) {
303                 $sth = $dbh->prepare("select authorised_value,lib from authorised_values where category=?");
304                 $sth->execute($authorised_valuecode);
305                 while (my ($authorised_value,$lib) = $sth->fetchrow) {
306                         $notforloanstatus{$authorised_value} = $lib?$lib:$authorised_value;
307                 }
308         }
309         for(my $i = 0 ; $i <= $#{$value} ; $i++)
310         {
311                 # replace * by %
312                 @$value[$i] =~ s/\*/%/g;
313                 # remove % at the beginning
314                 @$value[$i] =~ s/^%//g;
315             @$value[$i] =~ s/(\.|\?|\:|\!|\'|,|\-|\"|\(|\)|\[|\]|\{|\}|\/)/ /g if @$operator[$i] eq "contains";
316                 if(@$excluding[$i])     # NOT statements
317                 {
318                         $any_not = 1;
319                         if(@$operator[$i] eq "contains")
320                         {
321                                 foreach my $word (split(/ /, @$value[$i]))      # if operator is contains, splits the words in separate requests
322                                 {
323                                         # remove the "%" for small word (3 letters. (note : the >4 is due to the % at the end)
324 #                                       warn "word : $word";
325                                         $word =~ s/%//g unless length($word)>4;
326                                         unless (C4::Context->stopwords->{uc($word)} or length($word)==1) {      #it's NOT a stopword => use it. Otherwise, ignore
327                                                 push @not_tags, @$tags[$i];
328                                                 push @not_and_or, "or"; # as request is negated, finds "foo" or "bar" if final request is NOT "foo" and "bar"
329                                                 push @not_operator, @$operator[$i];
330                                                 push @not_value, $word;
331                                         }
332                                 }
333                         }
334                         else
335                         {
336                                 push @not_tags, @$tags[$i];
337                                 push @not_and_or, "or"; # as request is negated, finds "foo" or "bar" if final request is NOT "foo" and "bar"
338                                 push @not_operator, @$operator[$i];
339                                 push @not_value, @$value[$i];
340                         }
341                 }
342                 else    # NORMAL statements
343                 {
344                         if(@$operator[$i] eq "contains") # if operator is contains, splits the words in separate requests
345                         {
346                                 foreach my $word (split(/ /, @$value[$i]))
347                                 {
348                                         # remove the "%" for small word (3 letters. (note : the >4 is due to the % at the end)
349 #                                       warn "word : $word";
350                                         $word =~ s/%//g unless length($word)>4;
351                                         unless (C4::Context->stopwords->{uc($word)} or length($word)==1) {      #it's NOT a stopword => use it. Otherwise, ignore
352                                                 push @normal_tags, @$tags[$i];
353                                                 push @normal_and_or, "and";     # assumes "foo" and "bar" if "foo bar" is entered
354                                                 push @normal_operator, @$operator[$i];
355                                                 push @normal_value, $word;
356                                         }
357                                 }
358                         }
359                         else
360                         {
361                                 push @normal_tags, @$tags[$i];
362                                 push @normal_and_or, @$and_or[$i];
363                                 push @normal_operator, @$operator[$i];
364                                 push @normal_value, @$value[$i];
365                         }
366                 }
367         }
368
369         # Finds the basic results without the NOT requests
370         my ($sql_tables, $sql_where1, $sql_where2) = create_request($dbh,\@normal_tags, \@normal_and_or, \@normal_operator, \@normal_value);
371   $sql_where1 .=" ". $sqlstring;
372         $sql_where1 .= "and TO_DAYS( NOW( ) ) - TO_DAYS( biblio.timestamp ) <30" if $orderby =~ "biblio.timestamp";
373         my $sth;
374         if ($sql_where2) {
375                 $sth = $dbh->prepare("select distinct m1.bibid from biblio,biblioitems,items,marc_biblio,$sql_tables where biblio.biblionumber=marc_biblio.biblionumber and biblio.biblionumber=biblioitems.biblionumber and m1.bibid=marc_biblio.bibid and $sql_where2 and ($sql_where1) order by $orderby $desc_or_asc");
376                 warn "Q2 : select distinct m1.bibid from biblio,biblioitems,items,marc_biblio,$sql_tables where biblio.biblionumber=marc_biblio.biblionumber and biblio.biblionumber=biblioitems.biblionumber and m1.bibid=marc_biblio.bibid and $sql_where2 and ($sql_where1) order by $orderby $desc_or_asc term is  @$value";
377         } else {
378                 $sth = $dbh->prepare("select distinct m1.bibid from biblio,biblioitems,items,marc_biblio,$sql_tables where biblio.biblionumber=marc_biblio.biblionumber and biblio.biblionumber=biblioitems.biblionumber and m1.bibid=marc_biblio.bibid and $sql_where1 order by $orderby $desc_or_asc");
379                 warn "Q : select distinct m1.bibid from biblio,biblioitems,items,marc_biblio,$sql_tables where biblio.biblionumber=marc_biblio.biblionumber and biblio.biblionumber=biblioitems.biblionumber and m1.bibid=marc_biblio.bibid and $sql_where1 order by $orderby $desc_or_asc";
380         }
381         $sth->execute();
382         my @result = ();
383         my $subtitle; # Added by JF for Subtitles
384
385         # Processes the NOT if any and there are results
386         my ($not_sql_tables, $not_sql_where1, $not_sql_where2);
387
388         if( ($sth->rows) && $any_not )  # some results to tune up and some NOT statements
389         {
390                 ($not_sql_tables, $not_sql_where1, $not_sql_where2) = create_request($dbh,\@not_tags, \@not_and_or, \@not_operator, \@not_value);
391
392                 my @tmpresult;
393
394                 while (my ($bibid) = $sth->fetchrow) {
395                         push @tmpresult,$bibid;
396                 }
397                 my $sth_not;
398                 warn "NOT : select distinct m1.bibid from $not_sql_tables where $not_sql_where2 and ($not_sql_where1)";
399                 if ($not_sql_where2) {
400                         $sth_not = $dbh->prepare("select distinct m1.bibid from $not_sql_tables where $not_sql_where2 and ($not_sql_where1)");
401                 } else {
402                         $sth_not = $dbh->prepare("select distinct m1.bibid from $not_sql_tables where $not_sql_where1");
403                 }
404                 $sth_not->execute();
405
406                 if($sth_not->rows)
407                 {
408                         my %not_bibids = ();
409                         while(my $bibid = $sth_not->fetchrow()) {
410                                 $not_bibids{$bibid} = 1;        # populates the hashtable with the bibids matching the NOT statement
411                         }
412
413                         foreach my $bibid (@tmpresult)
414                         {
415                                 if(!$not_bibids{$bibid})
416                                 {
417                                         push @result, $bibid;
418                                 }
419                         }
420                 }
421                 $sth_not->finish();
422         }
423         else    # no NOT statements
424         {
425                 while (my ($bibid) = $sth->fetchrow) {
426                         push @result,$bibid;
427                 }
428         }
429
430         # we have bibid list. Now, loads title and author from [offset] to [offset]+[length]
431         my $counter = $offset;
432         # HINT : biblionumber as bn is important. The hash is fills biblionumber with items.biblionumber.
433         # so if you dont' has an item, you get a not nice empty value.
434         $sth = $dbh->prepare("SELECT biblio.biblionumber as bn,biblio.*, biblioitems.*,marc_biblio.bibid,itemtypes.notforloan,itemtypes.description
435                                                         FROM biblio, marc_biblio 
436                                                         LEFT JOIN biblioitems on biblio.biblionumber = biblioitems.biblionumber
437                                                         LEFT JOIN itemtypes on itemtypes.itemtype=biblioitems.itemtype
438                                                         WHERE biblio.biblionumber = marc_biblio.biblionumber AND bibid = ?");
439         my $sth_subtitle = $dbh->prepare("SELECT subtitle FROM bibliosubtitle WHERE biblionumber=?"); # Added BY JF for Subtitles
440         my @finalresult = ();
441         my @CNresults=();
442         my $totalitems=0;
443         my $oldline;
444         my ($oldbibid, $oldauthor, $oldtitle);
445         my $sth_itemCN = $dbh->prepare("select items.* from items where biblionumber=?");
446         my $sth_issue = $dbh->prepare("select date_due,returndate from issues where itemnumber=?");
447         # parse all biblios between start & end.
448         while (($counter <= $#result) && ($counter <= ($offset + $length))) {
449                 # search & parse all items & note itemcallnumber
450                 $sth->execute($result[$counter]);
451                 my $continue=1;
452                 my $line = $sth->fetchrow_hashref;
453                 my $biblionumber=$line->{bn};
454         # Return subtitles first ADDED BY JF
455                 $sth_subtitle->execute($biblionumber);
456                 my $subtitle_here.= $sth_subtitle->fetchrow." ";
457                 chop $subtitle_here;
458                 $subtitle = $subtitle_here;
459 #               warn "Here's the Biblionumber ".$biblionumber;
460 #                warn "and here's the subtitle: ".$subtitle_here;
461
462         # /ADDED BY JF
463
464 #               $continue=0 unless $line->{bn};
465 #               my $lastitemnumber;
466                 $sth_itemCN->execute($biblionumber);
467                 my @CNresults = ();
468                 my $notforloan=1; # to see if there is at least 1 item that can be issued
469                 while (my $item = $sth_itemCN->fetchrow_hashref) {
470                         # parse the result, putting holdingbranch & itemcallnumber in separate array
471                         # then all other fields in the main array
472                         
473                         # search if item is on loan
474                         my $date_due;
475                         $sth_issue->execute($item->{itemnumber});
476                         while (my $loan = $sth_issue->fetchrow_hashref) {
477                                 if ($loan->{date_due} and !$loan->{returndate}) {
478                                         $date_due = $loan->{date_due};
479                                 }
480                         }
481                         # store this item
482                         my %lineCN;
483                         $lineCN{holdingbranch} = $item->{holdingbranch};
484                         $lineCN{itemcallnumber} = $item->{itemcallnumber};
485                         $lineCN{location} = $item->{location};
486                         $lineCN{date_due} = format_date($date_due);
487                         $lineCN{notforloan} = $notforloanstatus{$line->{notforloan}} if ($line->{notforloan}); # setting not forloan if itemtype is not for loan
488                         $lineCN{notforloan} = $notforloanstatus{$item->{notforloan}} if ($item->{notforloan}); # setting not forloan it this item is not for loan
489                         $notforloan=0 unless ($item->{notforloan} or $item->{wthdrawn} or $item->{itemlost});
490                         push @CNresults,\%lineCN;
491                         $totalitems++;
492                 }
493                 # save the biblio in the final array, with item and item issue status
494                 my %newline;
495                 %newline = %$line;
496                 $newline{totitem} = $totalitems;
497                 # if $totalitems == 0, check if it's being ordered.
498                 if ($totalitems == 0) {
499                         my $sth = $dbh->prepare("select count(*) from aqorders where biblionumber=? and datecancellationprinted is NULL");
500                         $sth->execute($biblionumber);
501                         my ($ordered) = $sth->fetchrow;
502                         $newline{onorder} = 1 if $ordered;
503                 }
504                 $newline{biblionumber} = $biblionumber;
505                 $newline{norequests} = 0;
506                 $newline{norequests} = 1 if ($line->{notforloan}); # itemtype not issuable
507                 $newline{norequests} = 1 if (!$line->{notforloan} && $notforloan); # itemtype issuable but all items not issuable for instance
508                 $newline{subtitle} = $subtitle;  # put the subtitle in ADDED BY JF
509
510                 my @CNresults2= @CNresults;
511                 $newline{CN} = \@CNresults2;
512                 $newline{'even'} = 1 if $#finalresult % 2 == 0;
513                 $newline{'odd'} = 1 if $#finalresult % 2 == 1;
514                 $newline{'timestamp'} = format_date($newline{timestamp});
515                 @CNresults = ();
516                 push @finalresult, \%newline;
517                 $totalitems=0;
518                 $counter++;
519         }
520         my $nbresults = $#result+1;
521         return (\@finalresult, $nbresults);
522 }
523
524 # Creates the SQL Request
525
526 sub create_request {
527         my ($dbh,$tags, $and_or, $operator, $value) = @_;
528
529         my $sql_tables; # will contain marc_subfield_table as m1,...
530         my $sql_where1; # will contain the "true" where
531         my $sql_where2 = "("; # will contain m1.bibid=m2.bibid
532         my $nb_active=0; # will contain the number of "active" entries. an entry is active if a value is provided.
533         my $nb_table=1; # will contain the number of table. ++ on each entry EXCEPT when an OR  is provided.
534
535         my $maxloop=8; # the maximum number of words to avoid a too complex search.
536         $maxloop = @$value if @$value<$maxloop;
537         
538         for(my $i=0; $i<=$maxloop;$i++) {
539                 if (@$value[$i]) {
540                         $nb_active++;
541                         if ($nb_active==1) {
542                                 if (@$operator[$i] eq "start") {
543                                         $sql_tables .= "marc_subfield_table as m$nb_table,";
544                                         $sql_where1 .= "(m1.subfieldvalue like ".$dbh->quote("@$value[$i]%");
545                                         if (@$tags[$i]) {
546                                                 $sql_where1 .=" and concat(m1.tag,m1.subfieldcode) in (@$tags[$i])";
547                                         }
548                                         $sql_where1.=")";
549                                 } elsif (@$operator[$i] eq "contains") {
550                                         $sql_tables .= "marc_word as m$nb_table,";
551                                         $sql_where1 .= "(m1.word  like ".$dbh->quote("@$value[$i]");
552                                         if (@$tags[$i]) {
553                                                  $sql_where1 .=" and m1.tagsubfield in (@$tags[$i])";
554                                         }
555                                         $sql_where1.=")";
556                                 } else {
557                                         $sql_tables .= "marc_subfield_table as m$nb_table,";
558                                         $sql_where1 .= "(m1.subfieldvalue @$operator[$i] ".$dbh->quote("@$value[$i]");
559                                         if (@$tags[$i]) {
560                                                  $sql_where1 .=" and concat(m1.tag,m1.subfieldcode) in (@$tags[$i])";
561                                         }
562                                         $sql_where1.=")";
563                                 }
564                         } else {
565                                 if (@$operator[$i] eq "start") {
566                                         $nb_table++;
567                                         $sql_tables .= "marc_subfield_table as m$nb_table,";
568                                         $sql_where1 .= "@$and_or[$i] (m$nb_table.subfieldvalue like ".$dbh->quote("@$value[$i]%");
569                                         if (@$tags[$i]) {
570                                                 $sql_where1 .=" and concat(m$nb_table.tag,m$nb_table.subfieldcode) in (@$tags[$i])";
571                                         }
572                                         $sql_where1.=")";
573                                         $sql_where2 .= "m1.bibid=m$nb_table.bibid and ";
574                                 } elsif (@$operator[$i] eq "contains") {
575                                         if (@$and_or[$i] eq 'and') {
576                                                 $nb_table++;
577                                                 $sql_tables .= "marc_word as m$nb_table,";
578                                                 $sql_where1 .= "@$and_or[$i] (m$nb_table.word like ".$dbh->quote("@$value[$i]");
579                                                 if (@$tags[$i]) {
580                                                         $sql_where1 .=" and m$nb_table.tagsubfield in(@$tags[$i])";
581                                                 }
582                                                 $sql_where1.=")";
583                                                 $sql_where2 .= "m1.bibid=m$nb_table.bibid and ";
584                                         } else {
585                                                 $sql_where1 .= "@$and_or[$i] (m$nb_table.word like ".$dbh->quote("@$value[$i]");
586                                                 if (@$tags[$i]) {
587                                                         $sql_where1 .="  and m$nb_table.tagsubfield in (@$tags[$i])";
588                                                 }
589                                                 $sql_where1.=")";
590                                                 $sql_where2 .= "m1.bibid=m$nb_table.bibid and ";
591                                         }
592                                 } else {
593                                         $nb_table++;
594                                         $sql_tables .= "marc_subfield_table as m$nb_table,";
595                                         $sql_where1 .= "@$and_or[$i] (m$nb_table.subfieldvalue @$operator[$i] ".$dbh->quote(@$value[$i]);
596                                         if (@$tags[$i]) {
597                                                 $sql_where1 .="  and concat(m$nb_table.tag,m$nb_table.subfieldcode) in (@$tags[$i])";
598                                         }
599                                         $sql_where2 .= "m1.bibid=m$nb_table.bibid and ";
600                                         $sql_where1.=")";
601                                 }
602                         }
603                 }
604         }
605
606         if($sql_where2 ne "(")  # some datas added to sql_where2, processing
607         {
608                 $sql_where2 = substr($sql_where2, 0, (length($sql_where2)-5)); # deletes the trailing ' and '
609                 $sql_where2 .= ")";
610         }
611         else    # no sql_where2 statement, deleting '('
612         {
613                 $sql_where2 = "";
614         }
615         chop $sql_tables;       # deletes the trailing ','
616         return ($sql_tables, $sql_where1, $sql_where2);
617 }
618
619 sub getMARCnotes {
620         my ($dbh, $bibid, $marcflavour) = @_;
621         my ($mintag, $maxtag);
622         if ($marcflavour eq "MARC21") {
623                 $mintag = "500";
624                 $maxtag = "599";
625         } else {           # assume unimarc if not marc21
626                 $mintag = "300";
627                 $maxtag = "399";
628         }
629
630         my $sth=$dbh->prepare("SELECT subfieldvalue,tag FROM marc_subfield_table WHERE bibid=? AND tag BETWEEN ? AND ? ORDER BY tagorder");
631
632         $sth->execute($bibid,$mintag,$maxtag);
633
634         my @marcnotes;
635         my $note = "";
636         my $tag = "";
637         my $marcnote;
638
639         while (my $data=$sth->fetchrow_arrayref) {
640                 my $value=$data->[0];
641                 my $thistag=$data->[1];
642                 if ($value=~/\.$/) {
643                         $value=$value . "  ";
644                 }
645                 if ($thistag ne $tag && $note ne "") {
646                         $marcnote = {marcnote => $note,};
647                         push @marcnotes, $marcnote;
648                         $note=$value;
649                         $tag=$thistag;
650                 }
651                 if ($note ne $value) {
652                         $note = $note." ".$value;
653                 }
654         }
655
656         if ($note) {
657                 $marcnote = {marcnote => $note};
658                 push @marcnotes, $marcnote;   #load last tag into array
659         }
660
661         $sth->finish;
662         $dbh->disconnect;
663
664         my $marcnotesarray=\@marcnotes;
665         return $marcnotesarray;
666 }  # end getMARCnotes
667
668
669 sub getMARCsubjects {
670     my ($dbh, $bibid, $marcflavour) = @_;
671         my ($mintag, $maxtag);
672         if ($marcflavour eq "MARC21") {
673                 $mintag = "600";
674                 $maxtag = "699";
675         } else {           # assume unimarc if not marc21
676                 $mintag = "600";
677                 $maxtag = "619";
678         }
679         my $sth=$dbh->prepare("SELECT subfieldvalue,subfieldcode FROM marc_subfield_table WHERE bibid=? AND tag BETWEEN ? AND ? ORDER BY tagorder");
680
681         $sth->execute($bibid,$mintag,$maxtag);
682
683         my @marcsubjcts;
684         my $subjct = "";
685         my $subfield = "";
686         my $marcsubjct;
687
688         while (my $data=$sth->fetchrow_arrayref) {
689                 my $value = $data->[0];
690                 my $subfield = $data->[1];
691                 if ($subfield eq "a" && $value ne $subjct) {
692                         $marcsubjct = {MARCSUBJCT => $value,};
693                         push @marcsubjcts, $marcsubjct;
694                         $subjct = $value;
695                 }
696         }
697
698         $sth->finish;
699         $dbh->disconnect;
700
701         my $marcsubjctsarray=\@marcsubjcts;
702         return $marcsubjctsarray;
703 }  #end getMARCsubjects
704
705 END { }       # module clean-up code here (global destructor)
706
707 1;
708 __END__
709
710 =back
711
712 =head1 AUTHOR
713
714 Koha Developement team <info@koha.org>
715
716 =cut