[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