$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'.
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";
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 ], ...] }',
}
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;
# 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});
},
{
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 => {
);
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;
superpage_summary => $current_page_summary,
facet_key => $facet_key,
ids => \@results,
- abstract_query => $current_abstract
+ ($return_abstract ? (abstract_query => $current_abstract) : ())
}
);
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';
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;
return $self->{'values'};
}
+sub to_abstract_query {
+ my ($self) = @_;
+
+ return {
+ (map { $_ => $self->$_ } qw/name negate values/),
+ "type" => "facet"
+ };
+}
+
#-------------------------------
package QueryParser::query_plan::modifier;