[open-ils-commits] r15836 - trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq (senator)
svn at svn.open-ils.org
svn at svn.open-ils.org
Fri Mar 12 15:37:09 EST 2010
Author: senator
Date: 2010-03-12 15:37:04 -0500 (Fri, 12 Mar 2010)
New Revision: 15836
Modified:
trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm
Log:
Acq: unified search: big improvement
You can now search any field linked to au by string matching name, username,
alias, or barcode, not just user ID (which was fairly useless).
Modified: trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm
===================================================================
--- trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm 2010-03-12 20:12:52 UTC (rev 15835)
+++ trunk/Open-ILS/src/perlmods/OpenILS/Application/Acq/Search.pm 2010-03-12 20:37:04 UTC (rev 15836)
@@ -85,34 +85,108 @@
my $result = {"+acqlia" => {"-or" => $point}};
foreach my $unit (@$acqlia) {
- while (my ($k, $v) = each %$unit) {
- if ($k !~ /^__/) {
- if ($unit->{"__fuzzy"} and not ref $v) {
- push @$point, {
- "-and" => {
- "definition" => $k,
- "attr_value" => {"ilike" => "%" . $v . "%"}
- }
- };
- } elsif ($unit->{"__between"} and could_be_range($v)) {
- push @$point, {
- "-and" => {
- "definition" => $k,
- "attr_value" => {"between" => $v}
- }
- };
- } elsif (check_1d_max($v)) {
- push @$point, {
- "-and" => {"definition" => $k, "attr_value" => $v}
- };
+ my ($k, $v, $fuzzy, $between) = breakdown_term($unit);
+ if ($fuzzy and not ref $v) {
+ push @$point, {
+ "-and" => {
+ "definition" => $k,
+ "attr_value" => {"ilike" => "%" . $v . "%"}
+ }
+ };
+ } elsif ($between and could_be_range($v)) {
+ push @$point, {
+ "-and" => {
+ "definition" => $k, "attr_value" => {"between" => $v}
+ }
+ };
+ } elsif (check_1d_max($v)) {
+ push @$point, {
+ "-and" => {"definition" => $k, "attr_value" => $v}
+ };
+ }
+ }
+ $result;
+}
+
+sub breakdown_term {
+ my ($term) = @_;
+
+ my $key = (grep { !/^__/ } keys %$term)[0];
+ (
+ $key, $term->{$key},
+ $term->{"__fuzzy"} ? 1 : 0,
+ $term->{"__between"} ? 1 : 0
+ );
+}
+
+sub get_fm_links_by_hint {
+ my ($hint) = @_;
+ foreach my $field (values %{$Fieldmapper::fieldmap}) {
+ return $field->{"links"} if $field->{"hint"} eq $hint;
+ }
+ undef;
+}
+
+sub gen_au_term {
+ my ($value, $n) = @_;
+ +{
+ "-or" => {
+ "+au$n" => {
+ "-or" => {
+ "usrname" => $value,
+ "alias" => $value,
+ "first_given_name" => $value,
+ "second_given_name" => $value,
+ "family_name" => $value
+ }
+ },
+ "+ac$n" => {"barcode" => $value}
+ }
+ };
+}
+
+# go through the terms hash, find keys that correspond to fields links
+# to actor.usr, and rewrite the search as one that searches not by
+# actor.usr.id but by any of these user properties: card barcode, username,
+# alias, given names and family name.
+sub prepare_au_terms {
+ my ($terms, $join_num) = @_;
+ my @joins = ();
+ $join_num ||= 0;
+
+ foreach my $conj (qw/-and -or/) {
+ next unless exists $terms->{$conj};
+
+ my @new_outer_terms = ();
+ foreach my $hint_unit (@{$terms->{$conj}}) {
+ my $hint = (keys %$hint_unit)[0];
+ (my $plain_hint = $hint) =~ y/+//d;
+
+ if (my $links = get_fm_links_by_hint($plain_hint) and
+ $plain_hint ne "acqlia") {
+ my @new_terms = ();
+ foreach my $pair (@{$hint_unit->{$hint}}) {
+ my ($attr, $value) = breakdown_term($pair);
+ if ($links->{$attr} and
+ $links->{$attr}->{"class"} eq "au") {
+ push @joins, [$plain_hint, $attr, $join_num];
+ push @new_outer_terms, gen_au_term($value, $join_num);
+ $join_num++;
+ } else {
+ push @new_terms, $pair;
+ }
+ }
+ if (@new_terms) {
+ $hint_unit->{$hint} = [ @new_terms ];
} else {
- next;
+ delete $hint_unit->{$hint};
}
- last;
}
+ push @new_outer_terms, $hint_unit if scalar keys %$hint_unit;
}
+ $terms->{$conj} = [ @new_outer_terms ];
}
- $result;
+ @joins;
}
sub prepare_terms {
@@ -127,16 +201,13 @@
my $clause = [];
$outer_clause->{$conj} = [] unless $outer_clause->{$conj};
foreach my $unit (@{$terms->{$class}}) {
- while (my ($k, $v) = each %$unit) {
- if ($k !~ /^__/) {
- if ($unit->{"__fuzzy"} and not ref $v) {
- push @$clause, {$k => {"ilike" => "%" . $v . "%"}};
- } elsif ($unit->{"__between"} and could_be_range($v)) {
- push @$clause, {$k => {"between" => $v}};
- } elsif (check_1d_max($v)) {
- push @$clause, {$k => $v};
- }
- }
+ my ($k, $v, $fuzzy, $between) = breakdown_term($unit);
+ if ($fuzzy and not ref $v) {
+ push @$clause, {$k => {"ilike" => "%" . $v . "%"}};
+ } elsif ($between and could_be_range($v)) {
+ push @$clause, {$k => {"between" => $v}};
+ } elsif (check_1d_max($v)) {
+ push @$clause, {$k => $v};
}
}
push @{$outer_clause->{$conj}}, {"+" . $class => $clause};
@@ -152,9 +223,42 @@
$outer_clause;
}
+sub add_au_joins {
+ my ($from) = shift;
+
+ my $n = 0;
+ foreach my $join (@_) {
+ my ($hint, $attr, $num) = @$join;
+ my $start = $hint eq "jub" ? $from->{$hint} : $from->{"jub"}->{$hint};
+ my $clause = {
+ "class" => "au",
+ "type" => "left",
+ "field" => "id",
+ "fkey" => $attr,
+ "join" => {
+ "ac$num" => {
+ "class" => "ac",
+ "type" => "left",
+ "field" => "id",
+ "fkey" => "card"
+ }
+ }
+ };
+ if ($hint eq "jub") {
+ $start->{"au$num"} = $clause;
+ } else {
+ $start->{"join"} ||= {};
+ $start->{"join"}->{"au$num"} = $clause;
+ }
+ $n++;
+ }
+ $n;
+}
+
__PACKAGE__->register_method(
- method => "grand_search",
- api_name => "open-ils.acq.lineitem.grand_search",
+ method => "unified_search",
+ api_name => "open-ils.acq.lineitem.unified_search",
+ stream => 1,
signature => {
desc => q/Returns lineitems based on flexible search terms./,
params => [
@@ -172,38 +276,43 @@
);
__PACKAGE__->register_method(
- method => "grand_search",
- api_name => "open-ils.acq.purchase_order.grand_search",
+ method => "unified_search",
+ api_name => "open-ils.acq.purchase_order.unified_search",
+ stream => 1,
signature => {
desc => q/Returns purchase orders based on flexible search terms.
- See open-ils.acq.lineitem.grand_search/,
+ See open-ils.acq.lineitem.unified_search/,
return => {desc => "A stream of POs on success, Event on failure"}
}
);
__PACKAGE__->register_method(
- method => "grand_search",
- api_name => "open-ils.acq.picklist.grand_search",
+ method => "unified_search",
+ api_name => "open-ils.acq.picklist.unified_search",
+ stream => 1,
signature => {
desc => q/Returns pick lists based on flexible search terms.
- See open-ils.acq.lineitem.grand_search/,
+ See open-ils.acq.lineitem.unified_search/,
return => {desc => "A stream of PLs on success, Event on failure"}
}
);
-sub grand_search {
+sub unified_search {
my ($self, $conn, $auth, $and_terms, $or_terms, $conj, $options) = @_;
+ $options ||= {};
+
my $e = new_editor("authtoken" => $auth);
return $e->die_event unless $e->checkauth;
# What kind of object are we returning? Important: (\w+) had better be
# a legit acq classname particle, so don't register any crazy api_names.
- my $ret_type = ($self->api_name =~ /cq.(\w+).gr/)[0];
+ my $ret_type = ($self->api_name =~ /cq.(\w+).un/)[0];
my $retriever = $RETRIEVERS{$ret_type};
+ my $hint = F("acq::$ret_type")->{"hint"};
my $query = {
"select" => {
- F("acq::$ret_type")->{"hint"} =>
+ $hint =>
[{"column" => "id", "transform" => "distinct"}]
},
"from" => {
@@ -219,9 +328,13 @@
"fkey" => "picklist"
}
}
- }
+ },
+ "order_by" => { $hint => {"id" => {}}},
+ "offset" => ($options->{"offset"} || 0)
};
+ $query->{"limit"} = $options->{"limit"} if $options->{"limit"};
+
$and_terms = prepare_terms($and_terms, 1);
$or_terms = prepare_terms($or_terms, 0) and do {
$query->{"from"}->{"jub"}->{"acqlia"} = {
@@ -229,6 +342,9 @@
};
};
+ my $offset = add_au_joins($query->{"from"}, prepare_au_terms($and_terms));
+ add_au_joins($query->{"from"}, prepare_au_terms($or_terms, $offset));
+
if ($and_terms and $or_terms) {
$query->{"where"} = {
"-" . (lc $conj eq "or" ? "or" : "and") => [$and_terms, $or_terms]
More information about the open-ils-commits
mailing list