cleanup, add support for facets, move canonicalizer into open-ils.storage
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Wed, 28 Dec 2011 20:52:08 +0000 (15:52 -0500)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Thu, 29 Dec 2011 17:04:04 +0000 (12:04 -0500)
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/src/extras/fts-replacement.pl
Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/metabib.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/QueryParser.pm

index 9d63072..2ce707a 100755 (executable)
@@ -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";
index 8c7cc2a..0d8e84f 100644 (file)
@@ -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) : ())
         }
     );
 
index b7fe27a..c525d14 100644 (file)
@@ -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;
index 1703925..68a7fde 100644 (file)
@@ -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;