From 919bb3c5a0dd04de1d4c07d3e0f0f442ab02b429 Mon Sep 17 00:00:00 2001 From: miker Date: Wed, 20 Apr 2011 18:50:44 +0000 Subject: [PATCH] Add support for facet and filter negation via "-" prefix git-svn-id: svn://svn.open-ils.org/ILS/trunk@20255 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- .../Storage/Driver/Pg/QueryParser.pm | 13 ++++-- .../Application/Storage/Publisher/metabib.pm | 2 +- .../Application/Storage/QueryParser.pm | 44 +++++++++++++------ 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm index cef9cca945..191542434c 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Driver/Pg/QueryParser.pm @@ -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; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm index 0b146a0b4c..613d5f7049 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm @@ -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, diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm index 1bee5623d3..52a203b0d2 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm @@ -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'}; -- 2.43.2