Assigning bug 1835 : change password would never log password change.
[koha_gimpoz] / C4 / Search.pm
index 4885a2d..d5a44ac 100644 (file)
@@ -21,6 +21,8 @@ use C4::Context;
 use C4::Biblio;    # GetMarcFromKohaField
 use C4::Koha;      # getFacets
 use Lingua::Stem;
+use C4::Search::PazPar2;
+use XML::Simple;
 use C4::Dates qw(format_date);
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
@@ -28,7 +30,7 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
 # set the version for version checking
 BEGIN {
     $VERSION = 3.01;
-    $DEBUG = ( $ENV{DEBUG} ) ? 1 : 0;
+    $DEBUG = ($ENV{DEBUG}) ? 1 : 0;
 }
 
 =head1 NAME
@@ -255,7 +257,7 @@ sub SimpleSearch {
                 return ( $error, undef );
             }
         }
-        my $hits;
+        my $hits = 0;
         my $ev;
         while ( ( my $i = ZOOM::event( \@zconns ) ) != 0 ) {
             $ev = $zconns[ $i - 1 ]->last_event();
@@ -268,6 +270,7 @@ sub SimpleSearch {
                     push @results, $record;
                 }
             }
+            $hits = 0;
         }
         return ( undef, \@results );
     }
@@ -464,15 +467,16 @@ sub getRecords {
                                 a => $term,
                                 f => $occ
                             );
+                            $tmprecord->append_fields($tmptitle);
                         }
                         else {
                             $tmptitle =
                               MARC::Field->new( '245', ' ', ' ', a => $term, );
                             $tmpauthor =
                               MARC::Field->new( '100', ' ', ' ', a => $occ, );
+                            $tmprecord->append_fields($tmptitle);
+                            $tmprecord->append_fields($tmpauthor);
                         }
-                        $tmprecord->append_fields($tmptitle);
-                        $tmprecord->append_fields($tmpauthor);
                         $results_hash->{'RECORDS'}[$j] =
                           $tmprecord->as_usmarc();
                     }
@@ -610,6 +614,79 @@ sub getRecords {
     return ( undef, $results_hashref, \@facets_loop );
 }
 
+sub pazGetRecords {
+    my (
+        $koha_query,       $simple_query, $sort_by_ref,    $servers_ref,
+        $results_per_page, $offset,       $expanded_facet, $branches,
+        $query_type,       $scan
+    ) = @_;
+
+    my $paz = C4::Search::PazPar2->new(C4::Context->config('pazpar2url'));
+    $paz->init();
+    $paz->search($simple_query);
+    sleep 1;
+
+    # do results
+    my $results_hashref = {};
+    my $stats = XMLin($paz->stat);
+    my $results = XMLin($paz->show($offset, $results_per_page, 'work-title:1'), forcearray => 1);
+   
+    # for a grouped search result, the number of hits
+    # is the number of groups returned; 'bib_hits' will have
+    # the total number of bibs. 
+    $results_hashref->{'biblioserver'}->{'hits'} = $results->{'merged'}->[0];
+    $results_hashref->{'biblioserver'}->{'bib_hits'} = $stats->{'hits'};
+
+    HIT: foreach my $hit (@{ $results->{'hit'} }) {
+        my $recid = $hit->{recid}->[0];
+
+        my $work_title = $hit->{'md-work-title'}->[0];
+        my $work_author;
+        if (exists $hit->{'md-work-author'}) {
+            $work_author = $hit->{'md-work-author'}->[0];
+        }
+        my $group_label = (defined $work_author) ? "$work_title / $work_author" : $work_title;
+
+        my $result_group = {};
+        $result_group->{'group_label'} = $group_label;
+        $result_group->{'group_merge_key'} = $recid;
+
+        my $count = 1;
+        if (exists $hit->{count}) {
+            $count = $hit->{count}->[0];
+        }
+        $result_group->{'group_count'} = $count;
+
+        for (my $i = 0; $i < $count; $i++) {
+            # FIXME -- may need to worry about diacritics here
+            my $rec = $paz->record($recid, $i);
+            push @{ $result_group->{'RECORDS'} }, $rec;
+        }
+
+        push @{ $results_hashref->{'biblioserver'}->{'GROUPS'} }, $result_group;
+    }
+    
+    # pass through facets
+    my $termlist_xml = $paz->termlist('author,subject');
+    my $terms = XMLin($termlist_xml, forcearray => 1);
+    my @facets_loop = ();
+    #die Dumper($results);
+#    foreach my $list (sort keys %{ $terms->{'list'} }) {
+#        my @facets = ();
+#        foreach my $facet (sort @{ $terms->{'list'}->{$list}->{'term'} } ) {
+#            push @facets, {
+#                facet_label_value => $facet->{'name'}->[0],
+#            };
+#        }
+#        push @facets_loop, ( {
+#            type_label => $list,
+#            facets => \@facets,
+#        } );
+#    }
+
+    return ( undef, $results_hashref, \@facets_loop );
+}
+
 # STOPWORDS
 sub _remove_stopwords {
     my ( $operand, $index ) = @_;
@@ -775,9 +852,7 @@ See verbose embedded documentation.
 sub buildQuery {
     my ( $operators, $operands, $indexes, $limits, $sort_by, $scan ) = @_;
 
-    warn "---------"        if $DEBUG;
-    warn "Enter buildQuery" if $DEBUG;
-    warn "---------"        if $DEBUG;
+    warn "---------\nEnter buildQuery\n---------" if $DEBUG;
 
     # dereference
     my @operators = @$operators if $operators;
@@ -1019,10 +1094,11 @@ sub buildQuery {
     foreach my $this_limit (@limits) {
         if ( $this_limit =~ /available/ ) {
 
-# available is defined as (items.notloan is NULL) and (items.itemlost > 0 or NULL) (last clause handles NULL values for lost in zebra)
-# all records not indexed in the onloan register and allrecords not indexed in the lost register, or where the value of lost is equal to or less than 0
+# 'available' is defined as (items.onloan is NULL) and (items.itemlost = 0)
+# In English:
+# all records not indexed in the onloan register (zebra) and all records with a value of lost equal to 0
             $availability_limit .=
-"( ( allrecords,AlwaysMatches='' not onloan,AlwaysMatches='') and ((lost,st-numeric <= 0) or ( allrecords,AlwaysMatches='' not lost,AlwaysMatches='')) )";
+"( ( allrecords,AlwaysMatches='' not onloan,AlwaysMatches='') and (lost,st-numeric=0) )"; #or ( allrecords,AlwaysMatches='' not lost,AlwaysMatches='')) )";
             $limit_cgi  .= "&limit=available";
             $limit_desc .= "";
         }
@@ -1062,10 +1138,12 @@ sub buildQuery {
         $_ =~ s/^ //g;     # remove any beginning spaces
         $_ =~ s/ $//g;     # remove any ending spaces
         $_ =~ s/==/=/g;    # remove double == from query
-
     }
     $query_cgi =~ s/^&//; # remove unnecessary & from beginning of the query cgi
 
+    for ($query_cgi,$simple_query) {
+        $_ =~ s/"//g;
+    }
     # append the limit to the query
     $query .= " " . $limit;
 
@@ -1077,9 +1155,7 @@ sub buildQuery {
         warn "LIMIT:" . $limit;
         warn "LIMIT CGI:" . $limit_cgi;
         warn "LIMIT DESC:" . $limit_desc;
-        warn "---------";
-        warn "Leave buildQuery";
-        warn "---------";
+        warn "---------\nLeave buildQuery\n---------";
     }
     return (
         undef,              $query, $simple_query, $query_cgi,
@@ -1099,7 +1175,6 @@ Format results in a form suitable for passing to the template
 sub searchResults {
     my ( $searchdesc, $hits, $results_per_page, $offset, @marcresults ) = @_;
     my $dbh = C4::Context->dbh;
-    my $toggle;
     my $even = 1;
     my @newresults;
 
@@ -1229,31 +1304,34 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
             }
             # FIXME: yuk
             $summary =~ s/\[(.*?)]//g;
-            $summary =~ s/\n/<br>/g;
+            $summary =~ s/\n/<br\/>/g;
             $oldbiblio->{summary} = $summary;
         }
 
 # Add search-term highlighting to the whole record where they match using <span>s
-        my $searchhighlightblob;
-        for my $highlight_field ( $marcrecord->fields ) {
-
-  # FIXME: need to skip title, subtitle, author, etc., as they are handled below
-            next if $highlight_field->tag() =~ /(^00)/;    # skip fixed fields
-            my $match;
-            my $field = $highlight_field->as_string();
-            for my $term ( keys %$span_terms_hashref ) {
-                if ( ( $field =~ /$term/i ) && ( length($term) > 3 ) ) {
-                    $field =~ s/$term/<span class=\"term\">$&<\/span>/gi;
-                    $match++;
+        if (C4::Context->preference("OpacHighlightedWords")){
+            my $searchhighlightblob;
+            for my $highlight_field ( $marcrecord->fields ) {
+    
+    # FIXME: need to skip title, subtitle, author, etc., as they are handled below
+                next if $highlight_field->tag() =~ /(^00)/;    # skip fixed fields
+                for my $subfield ($highlight_field->subfields()) {
+                    my $match;
+                    next if $subfield->[0] eq '9';
+                    my $field = $subfield->[1];
+                    for my $term ( keys %$span_terms_hashref ) {
+                        if ( ( $field =~ /$term/i ) && (( length($term) > 3 ) || ($field =~ / $term /i)) ) {
+                            $field =~ s/$term/<span class=\"term\">$&<\/span>/gi;
+                        $match++;
+                        }
+                    }
+                    $searchhighlightblob .= $field . " ... " if $match;
                 }
+    
             }
-
-            # FIXME: we might want to limit the size of these fields if we
-            # want to get fancy
-            $searchhighlightblob .= $field . " ... " if $match;
+            $searchhighlightblob = ' ... '.$searchhighlightblob if $searchhighlightblob;
+            $oldbiblio->{'searchhighlightblob'} = $searchhighlightblob;
         }
-        $oldbiblio->{'searchhighlightblob'} = $searchhighlightblob;
-
 # save an author with no <span> tag, for the <a href=search.pl?q=<!--tmpl_var name="author"-->> link
         $oldbiblio->{'author_nospan'} = $oldbiblio->{'author'};
 
@@ -1281,15 +1359,7 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
             }
         }
 
-        # FIXME:
-        # surely there's a better way to handle this
-        if ( $i % 2 ) {
-            $toggle = "#ffffcc";
-        }
-        else {
-            $toggle = "white";
-        }
-        $oldbiblio->{'toggle'} = $toggle;
+        ($i % 2) and $oldbiblio->{'toggle'} = 1;
 
         # Pull out the items fields
         my @fields = $marcrecord->field($itemtag);
@@ -1329,34 +1399,32 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
             foreach my $code ( keys %subfieldstosearch ) {
                 $item->{$code} = $field->subfield( $subfieldstosearch{$code} );
             }
-
-      # set item's branch name, use homebranch first, fall back to holdingbranch
-            if ( $item->{'homebranch'} ) {
-                $item->{'branchname'} = $branches{ $item->{homebranch} };
+                       my $hbranch     = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'homebranch'    : 'holdingbranch';
+                       my $otherbranch = C4::Context->preference('HomeOrHoldingBranch') eq 'homebranch' ? 'holdingbranch' : 'homebranch';
+            # set item's branch name, use HomeOrHoldingBranch syspref first, fall back to the other one
+            if ($item->{$hbranch}) {
+                $item->{'branchname'} = $branches{$item->{$hbranch}};
             }
-
-            # Last resort
-            elsif ( $item->{'holdingbranch'} ) {
-                $item->{'branchname'} = $branches{ $item->{holdingbranch} };
+            elsif ($item->{$otherbranch}) {    # Last resort
+                $item->{'branchname'} = $branches{$item->{$otherbranch}}; 
             }
 
+                       my $prefix = $item->{$hbranch} . '--' . $item->{location} . $item->{itype} . $item->{itemcallnumber};
 # For each grouping of items (onloan, available, unavailable), we build a key to store relevant info about that item
             if ( $item->{onloan} ) {
                 $onloan_count++;
-                $onloan_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{due_date} = format_date( $item->{onloan} );
-                $onloan_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{count}++ if $item->{'homebranch'};
-                $onloan_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{branchname} = $item->{'branchname'};
-                $onloan_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{location} = $locations{ $item->{location} };
-                $onloan_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{itemcallnumber} = $item->{itemcallnumber};
-               $onloan_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date}  }->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
+                               my $key = $prefix . $item->{due_date};
+                               $onloan_items->{$key}->{due_date} = format_date($item->{onloan});
+                               $onloan_items->{$key}->{count}++ if $item->{homebranch};
+                               $onloan_items->{$key}->{branchname} = $item->{branchname};
+                               $onloan_items->{$key}->{location} = $locations{ $item->{location} };
+                               $onloan_items->{$key}->{itemcallnumber} = $item->{itemcallnumber};
+                               $onloan_items->{$key}->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
                 # if something's checked out and lost, mark it as 'long overdue'
                 if ( $item->{itemlost} ) {
-                    $onloan_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{due_date} }->{longoverdue}++;
+                    $onloan_items->{$prefix}->{longoverdue}++;
                     $longoverdue_count++;
-                }
-
-                # can place holds as long as this item isn't lost
-                else {
+                } else {       # can place holds as long as item isn't lost
                     $can_place_holds = 1;
                 }
             }
@@ -1381,48 +1449,44 @@ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
                     $item->{status} = $item->{wthdrawn} . "-" . $item->{itemlost} . "-" . $item->{damaged} . "-" . $item->{notforloan};
                     $other_count++;
 
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{wthdrawn} = $item->{wthdrawn};
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{itemlost} = $item->{itemlost};
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{damaged} = $item->{damaged};
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{notforloan} = GetAuthorisedValueDesc( '', '', $item->{notforloan}, '', '', $notforloan_authorised_value ) if $notforloan_authorised_value;
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{count}++ if $item->{'homebranch'};
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{branchname} = $item->{'branchname'};
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{location} = $locations{ $item->{location} };
-                    $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{itemcallnumber} = $item->{itemcallnumber};
-                   $other_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} . $item->{status} }->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
+                                       my $key = $prefix . $item->{status};
+                                       foreach (qw(wthdrawn itemlost damaged branchname itemcallnumber)) {
+                       $other_items->{$key}->{$_} = $item->{$_};
+                                       }
+                                       $other_items->{$key}->{notforloan} = GetAuthorisedValueDesc('','',$item->{notforloan},'','',$notforloan_authorised_value) if $notforloan_authorised_value;
+                                       $other_items->{$key}->{count}++ if $item->{homebranch};
+                                       $other_items->{$key}->{location} = $locations{ $item->{location} };
+                                       $other_items->{$key}->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
                 }
-
                 # item is available
                 else {
                     $can_place_holds = 1;
                     $available_count++;
-                    $available_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{count}++ if $item->{'homebranch'};
-                    $available_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{branchname} = $item->{'branchname'};
-                    $available_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{location} = $locations{ $item->{location} };
-                    $available_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{itemcallnumber} = $item->{itemcallnumber};
-                   $available_items->{ $item->{'homebranch'} . '--' . $item->{location} . $item->{'itype'} . $item->{'itemcallnumber'} }->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
+                                       $available_items->{$prefix}->{count}++ if $item->{homebranch};
+                                       foreach (qw(branchname itemcallnumber)) {
+                       $available_items->{$prefix}->{$_} = $item->{$_};
+                                       }
+                                       $available_items->{$prefix}->{location} = $locations{ $item->{location} };
+                                       $available_items->{$prefix}->{imageurl} = getitemtypeimagesrc() . "/" . $itemtypes{ $item->{itype} }->{imageurl};
                 }
             }
         }    # notforloan, item level and biblioitem level
         my ( $availableitemscount, $onloanitemscount, $otheritemscount );
-        my $maxitems =
+        $maxitems =
           ( C4::Context->preference('maxItemsinSearchResults') )
           ? C4::Context->preference('maxItemsinSearchResults') - 1
           : 1;
         for my $key ( sort keys %$onloan_items ) {
-            $onloanitemscount++;
-            push @onloan_items_loop, $onloan_items->{$key}
-              unless $onloanitemscount > $maxitems;
+            (++$onloanitemscount > $maxitems) and last;
+            push @onloan_items_loop, $onloan_items->{$key};
         }
         for my $key ( sort keys %$other_items ) {
-            $otheritemscount++;
-            push @other_items_loop, $other_items->{$key}
-              unless $otheritemscount > $maxitems;
+            (++$otheritemscount > $maxitems) and last;
+            push @other_items_loop, $other_items->{$key};
         }
         for my $key ( sort keys %$available_items ) {
-            $availableitemscount++;
+            (++$availableitemscount > $maxitems) and last;
             push @available_items_loop, $available_items->{$key}
-              unless $availableitemscount > $maxitems;
         }
 
 # last check for norequest : if itemtype is notforloan, it can't be reserved either, whatever the items
@@ -1487,9 +1551,9 @@ sub NZgetRecords {
 
 sub NZanalyse {
     my ( $string, $server ) = @_;
-    warn "---------"       if $DEBUG;
-    warn "Enter NZanalyse" if $DEBUG;
-    warn "---------"       if $DEBUG;
+    warn "---------"       if $DEBUG;
+    warn " NZanalyse" if $DEBUG;
+    warn "---------"       if $DEBUG;
 
  # $server contains biblioserver or authorities, depending on what we search on.
  #warn "querying : $string on $server";
@@ -1526,29 +1590,7 @@ sub NZanalyse {
             # depending of operand, intersect, union or exclude both lists
             # to get a result list
             if ( $operator eq ' and ' ) {
-                my @leftresult = split /;/, $leftresult;
-                warn " @leftresult / $rightresult \n" if $DEBUG;
-
-                #             my @rightresult = split /;/,$leftresult;
-                my $finalresult;
-
-# parse the left results, and if the biblionumber exist in the right result, save it in finalresult
-# the result is stored twice, to have the same weight for AND than OR.
-# example : TWO : 61,61,64,121 (two is twice in the biblio #61) / TOWER : 61,64,130
-# result : 61,61,61,61,64,64 for two AND tower : 61 has more weight than 64
-                foreach (@leftresult) {
-                    my $value = $_;
-                    my $countvalue;
-                    ( $value, $countvalue ) = ( $1, $2 )
-                      if $value =~ m/(.*)-(\d+)$/;
-                    if ( $rightresult =~ /$value-(\d+);/ ) {
-                        $countvalue = ( $1 > $countvalue ? $countvalue : $1 );
-                        $finalresult .=
-                          "$value-$countvalue;$value-$countvalue;";
-                    }
-                }
-                warn " $finalresult \n" if $DEBUG;
-                return $finalresult;
+                return NZoperatorAND($leftresult,$rightresult);      
             }
             elsif ( $operator eq ' or ' ) {
 
@@ -1556,32 +1598,20 @@ sub NZanalyse {
                 return $leftresult . $rightresult;
             }
             elsif ( $operator eq ' not ' ) {
-                my @leftresult = split /;/, $leftresult;
-
-                #             my @rightresult = split /;/,$leftresult;
-                my $finalresult;
-                foreach (@leftresult) {
-                    my $value = $_;
-                    $value = $1 if $value =~ m/(.*)-\d+$/;
-                    unless ( $rightresult =~ "$value-" ) {
-                    }
-                }
-                return $finalresult;
+                return NZoperatorNOT($leftresult,$rightresult);      
             }
-            else {
-
+        }      
+        else {
 # this error is impossible, because of the regexp that isolate the operand, but just in case...
-                return $leftresult;
-                exit;
-            }
-        }
+            return $leftresult;
+        } 
     }
     warn "string :" . $string if $DEBUG;
     $string =~ /(.*?)( and | or | not | AND | OR | NOT )(.*)/;
     my $left     = $1;
     my $right    = $3;
     my $operator = lc($2);    # FIXME: and/or/not are operators, not operands
-    warn "dealing w/parenthesis. left :$left operator:$operator right:$right"
+    warn "no parenthesis. left : $left operator: $operator right: $right"
       if $DEBUG;
 
     # it's not a leaf, we have a and/or/not
@@ -1593,26 +1623,14 @@ sub NZanalyse {
         warn "node : $left / $operator / $right\n" if $DEBUG;
         my $leftresult  = NZanalyse( $left,  $server );
         my $rightresult = NZanalyse( $right, $server );
-
+        warn " leftresult : $leftresult" if $DEBUG;
+        warn " rightresult : $rightresult" if $DEBUG;
         # OK, we have the results for right and left part of the query
         # depending of operand, intersect, union or exclude both lists
         # to get a result list
         if ( $operator eq ' and ' ) {
-            my @leftresult = split /;/, $leftresult;
-
-            #             my @rightresult = split /;/,$leftresult;
-            my $finalresult;
-
-# parse the left results, and if the biblionumber exist in the right result, save it in finalresult
-# the result is stored twice, to have the same weight for AND than OR.
-# example : TWO : 61,61,64,121 (two is twice in the biblio #61) / TOWER : 61,64,130
-# result : 61,61,61,61,64,64 for two AND tower : 61 has more weight than 64
-            foreach (@leftresult) {
-                if ( $rightresult =~ "$_;" ) {
-                    $finalresult .= "$_;$_;";
-                }
-            }
-            return $finalresult;
+            warn "NZAND";
+            return NZoperatorAND($leftresult,$rightresult);
         }
         elsif ( $operator eq ' or ' ) {
 
@@ -1620,16 +1638,7 @@ sub NZanalyse {
             return $leftresult . $rightresult;
         }
         elsif ( $operator eq ' not ' ) {
-            my @leftresult = split /;/, $leftresult;
-
-            #             my @rightresult = split /;/,$leftresult;
-            my $finalresult;
-            foreach (@leftresult) {
-                unless ( $rightresult =~ "$_;" ) {
-                    $finalresult .= "$_;";
-                }
-            }
-            return $finalresult;
+            return NZoperatorNOT($leftresult,$rightresult);
         }
         else {
 
@@ -1642,6 +1651,8 @@ sub NZanalyse {
     else {
         $string =~ s/__X__/"$commacontent"/ if $commacontent;
         $string =~ s/-|\.|\?|,|;|!|'|\(|\)|\[|\]|{|}|"|&|\+|\*|\// /g;
+        #remove trailing blank at the beginning
+        $string =~ s/^ //g;
         warn "leaf:$string" if $DEBUG;
 
         # parse the string in in operator/operand/value again
@@ -1649,8 +1660,8 @@ sub NZanalyse {
         my $left     = $1;
         my $operator = $2;
         my $right    = $3;
-        warn "handling leaf... left:$left operator:$operator right:$right"
-          if $DEBUG;
+        warn "handling leaf... left:$left operator:$operator right:$right"
+          if $DEBUG;
         unless ($operator) {
             $string =~ /(.*)(>|<|=)(.*)/;
             $left     = $1;
@@ -1663,7 +1674,7 @@ sub NZanalyse {
         my $results;
 
 # strip adv, zebra keywords, currently not handled in nozebra: wrdl, ext, phr...
-        $left =~ s/[ ,].*$//;
+        $left =~ s/ .*$//;
 
         # automatic replace for short operators
         $left = 'title'            if $left =~ '^ti$';
@@ -1672,6 +1683,7 @@ sub NZanalyse {
         $left = 'subject'          if $left =~ '^su$';
         $left = 'koha-Auth-Number' if $left =~ '^an$';
         $left = 'keyword'          if $left =~ '^kw$';
+        warn "handling leaf... left:$left operator:$operator right:$right" if $DEBUG;
         if ( $operator && $left ne 'keyword' ) {
 
             #do a specific search
@@ -1681,7 +1693,7 @@ sub NZanalyse {
               $dbh->prepare(
 "SELECT biblionumbers,value FROM nozebra WHERE server=? AND indexname=? AND value $operator ?"
               );
-            warn "$left / $operator / $right\n";
+            warn "$left / $operator / $right\n" if $DEBUG;
 
             # split each word, query the DB and build the biblionumbers result
             #sanitizing leftpart
@@ -1690,7 +1702,7 @@ sub NZanalyse {
                 my $biblionumbers;
                 $_ =~ s/^\s+|\s+$//;
                 next unless $_;
-                warn "EXECUTE : $server, $left, $_";
+                warn "EXECUTE : $server, $left, $_" if $DEBUG;
                 $sth->execute( $server, $left, $_ )
                   or warn "execute failed: $!";
                 while ( my ( $line, $value ) = $sth->fetchrow ) {
@@ -1701,27 +1713,13 @@ sub NZanalyse {
                       unless ( $right =~ /^\d+$/ && $value =~ /\D/ );
                     warn "result : $value "
                       . ( $right  =~ /\d/ ) . "=="
-                      . ( !$value =~ /\d/ );         #= $line";
+                      . ( $value =~ /\D/?$line:"" ) if $DEBUG;         #= $line";
                 }
 
 # do a AND with existing list if there is one, otherwise, use the biblionumbers list as 1st result list
                 if ($results) {
-                    my @leftresult = split /;/, $biblionumbers;
-                    my $temp;
-                    foreach my $entry (@leftresult)
-                    {    # $_ contains biblionumber,title-weight
-                            # remove weight at the end
-                        my $cleaned = $entry;
-                        $cleaned =~ s/-\d*$//;
-
-                   # if the entry already in the hash, take it & increase weight
-                        warn "===== $cleaned =====" if $DEBUG;
-                        if ( $results =~ "$cleaned" ) {
-                            $temp .= "$entry;$entry;";
-                            warn "INCLUDING $entry" if $DEBUG;
-                        }
-                    }
-                    $results = $temp;
+                    warn "NZAND" if $DEBUG;
+                    $results = NZoperatorAND($biblionumbers,$results);
                 }
                 else {
                     $results = $biblionumbers;
@@ -1750,24 +1748,7 @@ sub NZanalyse {
 
 # do a AND with existing list if there is one, otherwise, use the biblionumbers list as 1st result list
                 if ($results) {
-                    warn "RES for $_ = $biblionumbers" if $DEBUG;
-                    my @leftresult = split /;/, $biblionumbers;
-                    my $temp;
-                    foreach my $entry (@leftresult)
-                    {    # $_ contains biblionumber,title-weight
-                            # remove weight at the end
-                        my $cleaned = $entry;
-                        $cleaned =~ s/-\d*$//;
-
-               # if the entry already in the hash, take it & increase weight
-               #                          warn "===== $cleaned =====" if $DEBUG;
-                        if ( $results =~ "$cleaned" ) {
-                            $temp .= "$entry;$entry;";
-
-               #                              warn "INCLUDING $entry" if $DEBUG;
-                        }
-                    }
-                    $results = $temp;
+                    $results = NZoperatorAND($biblionumbers,$results);
                 }
                 else {
                     warn "NEW RES for $_ = $biblionumbers" if $DEBUG;
@@ -1778,9 +1759,56 @@ sub NZanalyse {
         warn "return : $results for LEAF : $string" if $DEBUG;
         return $results;
     }
-    warn "---------"       if $DEBUG;
-    warn "Leave NZanalyse" if $DEBUG;
-    warn "---------"       if $DEBUG;
+    warn "---------\nLeave NZanalyse\n---------" if $DEBUG;
+}
+
+sub NZoperatorAND{
+    my ($rightresult, $leftresult)=@_;
+    
+    my @leftresult = split /;/, $leftresult;
+    warn " @leftresult / $rightresult \n" if $DEBUG;
+    
+    #             my @rightresult = split /;/,$leftresult;
+    my $finalresult;
+
+# parse the left results, and if the biblionumber exist in the right result, save it in finalresult
+# the result is stored twice, to have the same weight for AND than OR.
+# example : TWO : 61,61,64,121 (two is twice in the biblio #61) / TOWER : 61,64,130
+# result : 61,61,61,61,64,64 for two AND tower : 61 has more weight than 64
+    foreach (@leftresult) {
+        my $value = $_;
+        my $countvalue;
+        ( $value, $countvalue ) = ( $1, $2 ) if ($value=~/(.*)-(\d+)$/);
+        if ( $rightresult =~ /\Q$value\E-(\d+);/ ) {
+            $countvalue = ( $1 > $countvalue ? $countvalue : $1 );
+            $finalresult .=
+                "$value-$countvalue;$value-$countvalue;";
+        }
+    }
+    warn "NZAND DONE : $finalresult \n" if $DEBUG;
+    return $finalresult;
+}
+      
+sub NZoperatorOR{
+    my ($rightresult, $leftresult)=@_;
+    return $rightresult.$leftresult;
+}
+
+sub NZoperatorNOT{
+    my ($rightresult, $leftresult)=@_;
+    
+    my @leftresult = split /;/, $leftresult;
+
+    #             my @rightresult = split /;/,$leftresult;
+    my $finalresult;
+    foreach (@leftresult) {
+        my $value=$_;
+        $value=$1 if $value=~m/(.*)-\d+$/;
+        unless ($rightresult =~ "$value-") {
+            $finalresult .= "$_;";
+        }
+    }
+    return $finalresult;
 }
 
 =head2 NZorder
@@ -2122,6 +2150,12 @@ sub ModBiblios {
     }
     my ( $bntag,   $bnsubf )   = GetMarcFromKohaField('biblio.biblionumber');
     my ( $itemtag, $itemsubf ) = GetMarcFromKohaField('items.itemnumber');
+    if ($tag eq $itemtag) {
+        # do not allow the embedded item tag to be 
+        # edited from here
+        warn "Attempting to edit item tag via C4::Search::ModBiblios -- not allowed";
+        return (0, []);
+    }
     foreach my $usmarc (@$listbiblios) {
         my $record;
         $record = eval { MARC::Record->new_from_usmarc($usmarc) };
@@ -2129,15 +2163,14 @@ sub ModBiblios {
         if ($@) {
 
             # usmarc is not a valid usmarc May be a biblionumber
-            if ( $tag eq $itemtag ) {
-                my $bib = GetBiblioFromItemNumber($usmarc);
-                $record = GetMarcItem( $bib->{'biblionumber'}, $usmarc );
-                $biblionumber = $bib->{'biblionumber'};
-            }
-            else {
-                $record       = GetMarcBiblio($usmarc);
-                $biblionumber = $usmarc;
-            }
+            # FIXME - sorry, please let's figure out whether
+            #         this function is to be passed a list of
+            #         record numbers or a list of MARC::Record
+            #         objects.  The former is probably better
+            #         because the MARC records supplied by Zebra
+            #         may be not current.
+            $record       = GetMarcBiblio($usmarc);
+            $biblionumber = $usmarc;
         }
         else {
             if ( $bntag >= 010 ) {