From f558ee79da6f32aeedd5a42503fd92e191668630 Mon Sep 17 00:00:00 2001 From: Lebbeous Fogle-Weekley Date: Wed, 28 Dec 2011 15:52:08 -0500 Subject: [PATCH] cleanup, add support for facets, move canonicalizer into open-ils.storage Signed-off-by: Lebbeous Fogle-Weekley --- Open-ILS/src/extras/fts-replacement.pl | 83 ----------- .../lib/OpenILS/Application/Search/Biblio.pm | 17 ++- .../Application/Storage/Publisher/metabib.pm | 151 ++++++++++++++++++++- .../lib/OpenILS/Application/Storage/QueryParser.pm | 9 ++ 4 files changed, 170 insertions(+), 90 deletions(-) diff --git a/Open-ILS/src/extras/fts-replacement.pl b/Open-ILS/src/extras/fts-replacement.pl index 9d63072261..2ce707a02b 100755 --- a/Open-ILS/src/extras/fts-replacement.pl +++ b/Open-ILS/src/extras/fts-replacement.pl @@ -10,88 +10,6 @@ use Data::Dumper; $Data::Dumper::Indent = 1; use Time::HiRes qw/time/; -my $qpconfig; - -sub _abstract_query2str_filter { - my $f = shift; - - return sprintf( - "%s%s(%s)", - $f->{negate} ? $qpconfig->{operators}{disallowed} : "", - $f->{name}, - join(",", @{$f->{args}}) - ); -} - -sub _abstract_query2str_modifier { - my $f = shift; - - return $qpconfig->{operators}{modifier} . $f; -} - -# This should produce an equivalent query to the original, given an -# abstract_query with a qp config. -sub abstract_query2str { - my $abstract_query = shift; - my $depth = shift || 0; - - $qpconfig ||= $abstract_query->{config}; - - my $gs = $qpconfig->{operators}{group_start}; - my $ge = $qpconfig->{operators}{group_end}; - my $and = $qpconfig->{operators}{and}; - my $or = $qpconfig->{operators}{or}; - - my $q = ""; - $q .= $gs if $abstract_query->{type} and $abstract_query->{type} eq "query_plan" and $depth; - - if (exists $abstract_query->{type}) { - if ($abstract_query->{type} eq 'query_plan') { - $q .= join(" ", map { _abstract_query2str_filter($_) } @{$abstract_query->{filters}}) if - exists $abstract_query->{filters}; - $q .= " "; - - $q .= join(" ", map { _abstract_query2str_modifier($_) } @{$abstract_query->{modifiers}}) if - exists $abstract_query->{modifiers}; - } elsif ($abstract_query->{type} eq 'node') { - if ($abstract_query->{alias}) { - $q .= " " . $abstract_query->{alias}; - $q .= "|$_" foreach @{$abstract_query->{alias_fields}}; - } else { - $q .= " " . $abstract_query->{class}; - $q .= "|$_" foreach @{$abstract_query->{fields}}; - } - $q .= ":"; - } elsif ($abstract_query->{type} eq 'atom') { - my $prefix = $abstract_query->{prefix} || ''; - $prefix = $qpconfig->{operators}{disallowed} if $prefix eq '!'; - $q .= $prefix . - ($abstract_query->{content} || '') . - ($abstract_query->{suffix} || ''); - } - } - - if (exists $abstract_query->{children}) { - my $op = (keys(%{$abstract_query->{children}}))[0]; - $q .= join( - " " . ($op eq '&' ? $and : $or) . " ", - map { abstract_query2str($_, $depth + 1) } @{$abstract_query->{children}{$op}} - ); - } elsif ($abstract_query->{'&'} or $abstract_query->{'|'}) { - my $op = (keys(%{$abstract_query}))[0]; - $q .= join( - " " . ($op eq '&' ? $and : $or) . " ", - map { abstract_query2str($_, $depth + 1) } @{$abstract_query->{$op}} - ); - } - $q .= " "; - - - $q .= $ge if $abstract_query->{type} and $abstract_query->{type} eq "query_plan" and $depth; - - return $q; -} - OpenILS::Application::Storage::Driver::Pg::QueryParser->TEST_SETUP; my $query = '#available title: foo bar* || (-baz || (subject:"1900'. @@ -135,7 +53,6 @@ print "SQL:\n$sql\n\n" if (!$quiet); my $abstract_query = $plan->parse_tree->to_abstract_query(with_config => 1); print "abstract_query: " . Dumper($abstract_query) . "\n"; -print "abstract_query back to string: " . abstract_query2str($abstract_query) . "\n"; print "Simple plan: " . ($plan->simple_plan ? 'yes' : 'no') . "\n"; print "Total parse time, $runs runs: " . ($end - $start) . "s\n"; print "Average parse time, $runs runs: " . sprintf('%0.3f',(($end - $start) / $runs) * 1000) . "ms\n"; diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm index 8c7cc2a9c8..0d8e84f124 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm @@ -772,7 +772,8 @@ __PACKAGE__->register_method( params => [ {name => 'arghash', desc => 'Arg hash (see open-ils.search.biblio.multiclass)', type => 'object'}, {name => 'query', desc => 'Raw human-readable query (see perldoc '. __PACKAGE__ .')', type => 'string'}, - {name => 'docache', desc => 'Flag for caching (see open-ils.search.biblio.multiclass)', type => 'object'}, + {name => 'docache', desc => 'Flag for caching (see open-ils.search.biblio.multiclass)', type => 'bool'}, + {name => 'return_abstract', desc => "A flag to enable/disable returning an abstract represenation of the query itself, in addition to the result set (default OFF)", type => 'bool'} ], return => { desc => 'Search results from query, like: { "count" : $count, "ids" : [ [ $id, $relevancy, $total ], ...] }', @@ -783,7 +784,7 @@ __PACKAGE__->register_method( } sub multiclass_query { - my($self, $conn, $arghash, $query, $docache) = @_; + my($self, $conn, $arghash, $query, $docache, $return_abstract) = @_; $logger->debug("initial search query => $query"); my $orig_query = $query; @@ -899,7 +900,7 @@ sub multiclass_query { # unless $arghash->{preferred_language}; $method = $self->method_lookup($method); - my ($data) = $method->run($arghash, $docache); + my ($data) = $method->run($arghash, $docache, $return_abstract); $arghash->{searches} = $search if (!$data->{complex_query}); @@ -1165,7 +1166,11 @@ __PACKAGE__->register_method( }, { desc => "A flag to enable/disable searching and saving results in cache, including facets (default OFF)", - type => 'string', + type => 'bool' + }, + { + desc => "A flag to enable/disable returning an abstract represenation of the query itself, in addition to the result set (default OFF)", + type => 'bool' } ], return => { @@ -1192,7 +1197,7 @@ __PACKAGE__->register_method( ); sub staged_search { - my($self, $conn, $search_hash, $docache) = @_; + my($self, $conn, $search_hash, $docache, $return_abstract) = @_; my $IAmMetabib = ($self->api_name =~ /metabib/) ? 1 : 0; @@ -1366,7 +1371,7 @@ sub staged_search { superpage_summary => $current_page_summary, facet_key => $facet_key, ids => \@results, - abstract_query => $current_abstract + ($return_abstract ? (abstract_query => $current_abstract) : ()) } ); 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 b7fe27a46b..c525d14fd0 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 @@ -8,9 +8,9 @@ use OpenSRF::Utils::Logger qw/:level/; use OpenSRF::Utils::Cache; use OpenSRF::Utils::JSON; use Data::Dumper; -$Data::Dumper::Indent = 0; use Digest::MD5 qw/md5_hex/; +#use OpenILS::Application::Storage::Driver::Pg::QueryParser; my $log = 'OpenSRF::Utils::Logger'; @@ -2776,6 +2776,155 @@ __PACKAGE__->register_method( api_level => 1, ); + +sub _abstract_query2str_filter { + my $f = shift; + + return sprintf( + "%s%s(%s)", + $f->{negate} ? $qpconfig->{operators}{disallowed} : "", + $f->{name}, + join(",", @{$f->{args}}) + ); +} + +sub _abstract_query2str_modifier { + my $f = shift; + + return $qpconfig->{operators}{modifier} . $f; +} + +# This should produce an equivalent query to the original, given an +# abstract_query with a qp config. +sub abstract_query2str_impl { + my ($abstract_query, $depth, $qpconfig) = @_; + + my $gs = $qpconfig->{operators}{group_start}; + my $ge = $qpconfig->{operators}{group_end}; + my $and = $qpconfig->{operators}{and}; + my $or = $qpconfig->{operators}{or}; + + my $q = ""; + $q .= $gs if $abstract_query->{type} and $abstract_query->{type} eq "query_plan" and $depth; + + if (exists $abstract_query->{type}) { + if ($abstract_query->{type} eq 'query_plan') { + $q .= join(" ", map { _abstract_query2str_filter($_) } @{$abstract_query->{filters}}) if + exists $abstract_query->{filters}; + $q .= " "; + + $q .= join(" ", map { _abstract_query2str_modifier($_) } @{$abstract_query->{modifiers}}) if + exists $abstract_query->{modifiers}; + } elsif ($abstract_query->{type} eq 'node') { + if ($abstract_query->{alias}) { + $q .= " " . $abstract_query->{alias}; + $q .= "|$_" foreach @{$abstract_query->{alias_fields}}; + } else { + $q .= " " . $abstract_query->{class}; + $q .= "|$_" foreach @{$abstract_query->{fields}}; + } + $q .= ":"; + } elsif ($abstract_query->{type} eq 'atom') { + my $prefix = $abstract_query->{prefix} || ''; + $prefix = $qpconfig->{operators}{disallowed} if $prefix eq '!'; + $q .= $prefix . + ($abstract_query->{content} || '') . + ($abstract_query->{suffix} || ''); + } elsif ($abstract_query->{type} eq 'facet') { + # facet syntax [ # ] is hardcoded I guess? + my $prefix = $abstract_query->{negate} ? $qpconfig->{operators}{disallowed} : ''; + $q .= $prefix . $abstract_query->{name} . "[" . + join(" # ", @{$abstract_query->{values}}) . "]"; + } + } + + if (exists $abstract_query->{children}) { + my $op = (keys(%{$abstract_query->{children}}))[0]; + $q .= join( + " " . ($op eq '&' ? $and : $or) . " ", + map { + abstract_query2str_impl($_, $depth + 1, $qpconfig) + } @{$abstract_query->{children}{$op}} + ); + } elsif ($abstract_query->{'&'} or $abstract_query->{'|'}) { + my $op = (keys(%{$abstract_query}))[0]; + $q .= join( + " " . ($op eq '&' ? $and : $or) . " ", + map { + abstract_query2str_impl($_, $depth + 1, $qpconfig) + } @{$abstract_query->{$op}} + ); + } + $q .= " "; + + $q .= $ge if $abstract_query->{type} and $abstract_query->{type} eq "query_plan" and $depth; + + return $q; +} + +# Takes an abstract query object and recursively turns it back into a string +# for QueryParser. +sub abstract_query2str { + my ($self, $conn, $query) = @_; + + return abstract_query2str_impl($query, 0, $query->{config}); +} + +__PACKAGE__->register_method( + api_name => "open-ils.storage.query_parser.abstract_query.canonicalize", + method => "abstract_query2str", + api_level => 1, + signature => { + params => [ + {desc => q/ +Abstract query parser object, with complete config data. For example input, +see the 'abstract_query' part of the output of an API call like +open-ils.search.biblio.multiclass.query, when called with the return_abstract +flag set to true./, + type => "object"} + ], + return => { type => "string", desc => "String representation of abstract query object" } + } +); + +## XXX The following str2abstract_query stuff doesn't work, but do we need it +## to? Is this the right place for it? for now, you *can* get an abstract query +## back as part of the result if you call +## open-ils.search.biblio.multiclass.query with appropriate args. +## I suspect all we need to do (other than uncommenting the use statement +## near the top) is OpenILS::...::QueryParser->TEST_SETUP;, but I'm not sure +## that actually makes sense within the storage app. +# +#sub str2abstract_query { +# my ($self, $conn, $query, $opts) = @_; +# +# my %use_opts = ( # reasonable defaults? should these even be hardcoded here? +# superpage => 1, +# superpage_size => 1000, +# core_limit => 25000, +# query => $query, +# (ref $opts eq 'HASH' ? %$opts : ()) +# ); +# +# my $plan = new +# OpenILS::Application::Storage::Driver::Pg::QueryParser(%use_opts); +# +# return $plan->parse_tree->to_abstract_query; +#} +# +#__PACKAGE__->register_method( +# api_name => "open-ils.storage.query_parser.abstract_query.from_string", +# method => "str2abstract_query", +# api_level => 1, +# signature => { +# params => [ +# {desc => "Query", type => "string"}, +# {desc => "arguments to QueryParser constructor (optional)", type => "object"} +# ], +# return => { type => "object", desc => "abstract representation of query parser query" } +# } +#); + sub query_parser_fts { my $self = shift; my $client = shift; 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 1703925833..68a7fdebd9 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm @@ -1486,6 +1486,15 @@ sub values { return $self->{'values'}; } +sub to_abstract_query { + my ($self) = @_; + + return { + (map { $_ => $self->$_ } qw/name negate values/), + "type" => "facet" + }; +} + #------------------------------- package QueryParser::query_plan::modifier; -- 2.11.0