Add support for facet and filter negation via "-" prefix
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Wed, 20 Apr 2011 18:50:44 +0000 (18:50 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Wed, 20 Apr 2011 18:50:44 +0000 (18:50 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@20255 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm

index cef9cca..1915424 100644 (file)
@@ -1,3 +1,6 @@
+use strict;
+use warnings;
+
 package OpenILS::Application::Storage::Driver::Pg::QueryParser;
 use OpenILS::Application::Storage::QueryParser;
 use base 'QueryParser';
@@ -511,8 +514,9 @@ sub toSQL {
         if ($filter) {
             my @fargs = @{$filter->args};
 
-            if (@fargs > 1) {
-                $dyn_filters{$f} = "( " .
+            if (@fargs > 1 || $filter->negate) {
+                my $NOT = $filter->negate ? 'NOT' : '';
+                $dyn_filters{$f} = "$NOT( " .
                     join(
                         " OR ",
                         map { "mrd.attrs \@> hstore('$col', " . $self->QueryParser->quote_value($_) . ")" } @fargs
@@ -706,11 +710,12 @@ sub flatten {
                     @field_ids = @{ $self->QueryParser->facet_field_ids_by_class( $node->classname ) };
                 }
 
-                $from .= "\n\tJOIN /* facet */ metabib.facet_entry $talias ON (\n\t\tm.source = ${talias}.source\n\t\t".
+                my $join_type = $node->negate ? 'LEFT' : 'INNER';
+                $from .= "\n\t$join_type JOIN /* facet */ metabib.facet_entry $talias ON (\n\t\tm.source = ${talias}.source\n\t\t".
                          "AND SUBSTRING(${talias}.value,1,1024) IN (" . join(",", map { $self->QueryParser->quote_value($_) } @{$node->values}) . ")\n\t\t".
                          "AND ${talias}.field IN (". join(',', @field_ids) . ")\n\t)";
 
-                $where .= 'TRUE';
+                $where .= $node->negate ? "${talias}.id IS NULL" : 'TRUE';
 
             } else {
                 my $subnode = $node->flatten;
index 0b146a0..613d5f7 100644 (file)
@@ -3011,7 +3011,7 @@ sub query_parser_fts {
        my $metarecord = ($self->api_name =~ /metabib/ or $query->parse_tree->find_modifier('metabib') or $query->parse_tree->find_modifier('metarecord')) ? "'t'" : "'f'";
 
        my $sth = metabib::metarecord_source_map->db_Main->prepare(<<"    SQL");
-        SELECT  * /* bib search */
+        SELECT  * -- bib search: $args{query}
           FROM  search.query_parser_fts(
                     $param_search_ou\:\:INT,
                     $param_depth\:\:INT,
index 1bee562..52a203b 100644 (file)
@@ -1,3 +1,6 @@
+use strict;
+use warnings;
+
 package QueryParser;
 use OpenSRF::Utils::JSON;
 our %parser_config = (
@@ -523,11 +526,11 @@ sub decompose {
 
 
     # Build the filter and modifier uber-regexps
-    my $facet_re = '^\s*((?:' . join( '|', @{$pkg->facet_classes}) . ')(?:\|\w+)*)\[(.+?)\]';
+    my $facet_re = '^\s*(-?)((?:' . join( '|', @{$pkg->facet_classes}) . ')(?:\|\w+)*)\[(.+?)\]';
     warn " Facet RE: $facet_re\n" if $self->debug;
 
-    my $filter_re = '^\s*(' . join( '|', @{$pkg->filters}) . ')\(([^()]+)\)';
-    my $filter_as_class_re = '^\s*(' . join( '|', @{$pkg->filters}) . '):\s*(\S+)';
+    my $filter_re = '^\s*(-?)(' . join( '|', @{$pkg->filters}) . ')\(([^()]+)\)';
+    my $filter_as_class_re = '^\s*(-?)(' . join( '|', @{$pkg->filters}) . '):\s*(\S+)';
 
     my $modifier_re = '^\s*'.$modifier_tag_re.'(' . join( '|', @{$pkg->modifiers}) . ')\b';
     my $modifier_as_class_re = '^\s*(' . join( '|', @{$pkg->modifiers}) . '):\s*(\S+)';
@@ -547,17 +550,19 @@ sub decompose {
 
             $last_type = '';
         } elsif ($self->filter_count && /$filter_re/) { # found a filter
-            warn "Encountered search filter: $1 set to $2\n" if $self->debug;
+            warn "Encountered search filter: $1$2 set to $3\n" if $self->debug;
 
+            my $negate = ($1 eq '-') ? 1 : 0;
             $_ = $';
-            $struct->new_filter( $1 => [ split '[, ]+', $2 ] );
+            $struct->new_filter( $2 => [ split '[,]+', $3 ], $negate );
 
             $last_type = '';
         } elsif ($self->filter_count && /$filter_as_class_re/) { # found a filter
-            warn "Encountered search filter: $1 set to $2\n" if $self->debug;
+            warn "Encountered search filter: $1$2 set to $3\n" if $self->debug;
 
+            my $negate = ($1 eq '-') ? 1 : 0;
             $_ = $';
-            $struct->new_filter( $1 => [ split '[, ]+', $2 ] );
+            $struct->new_filter( $2 => [ split '[,]+', $3 ], $negate );
 
             $last_type = '';
         } elsif ($self->modifier_count && /$modifier_re/) { # found a modifier
@@ -611,11 +616,12 @@ sub decompose {
 
             $last_type = 'OR';
         } elsif ($self->facet_class_count && /$facet_re/) { # changing current class
-            warn "Encountered facet: $1 => $2\n" if $self->debug;
+            warn "Encountered facet: $1$2 => $3\n" if $self->debug;
 
-            my $facet = $1;
-            my $facet_value = [ split '\s*#\s*', $2 ];
-            $struct->new_facet( $facet => $facet_value );
+            my $negate = ($1 eq '-') ? 1 : 0;
+            my $facet = $2;
+            my $facet_value = [ split '\s*#\s*', $3 ];
+            $struct->new_facet( $facet => $facet_value, $negate );
             $_ = $';
 
             $last_type = '';
@@ -751,8 +757,9 @@ sub new_facet {
     my $pkg = ref($self) || $self;
     my $name = shift;
     my $args = shift;
+    my $negate = shift;
 
-    my $node = do{$pkg.'::facet'}->new( plan => $self, name => $name, 'values' => $args );
+    my $node = do{$pkg.'::facet'}->new( plan => $self, name => $name, 'values' => $args, negate => $negate );
     $self->add_node( $node );
 
     return $node;
@@ -763,8 +770,9 @@ sub new_filter {
     my $pkg = ref($self) || $self;
     my $name = shift;
     my $args = shift;
+    my $negate = shift;
 
-    my $node = do{$pkg.'::filter'}->new( plan => $self, name => $name, args => $args );
+    my $node = do{$pkg.'::filter'}->new( plan => $self, name => $name, args => $args, negate => $negate );
     $self->add_filter( $node );
 
     return $node;
@@ -1074,6 +1082,11 @@ sub name {
     return $self->{name};
 }
 
+sub negate {
+    my $self = shift;
+    return $self->{negate};
+}
+
 sub args {
     my $self = shift;
     return $self->{args};
@@ -1100,6 +1113,11 @@ sub name {
     return $self->{name};
 }
 
+sub negate {
+    my $self = shift;
+    return $self->{negate};
+}
+
 sub values {
     my $self = shift;
     return $self->{'values'};