Ignore:
Timestamp:
06/24/16 07:01:18 (8 years ago)
Author:
nanardon
Message:

Merge branch 'bettersearch'

Location:
trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Sql
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Sql/Group.pm

    r1786 r1788  
    8787                multiple => 1,  
    8888                delayed => 1, 
     89                iname => 'memberUID', 
    8990                can_values => sub { $base->list_objects('user') }, 
    9091                ro => sub { 
  • trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Sql/objects.pm

    r1783 r1788  
    736736    my ($class, $base, @filter) = @_; 
    737737 
    738     my %attrsql; 
    739     my %attrbind; 
     738    # Results groups by attr (OR filter) 
     739    # foo=1 foo=1 => foo = 1 or foo = 2 
     740    # foo=1 bar=1 => foo =1 and bar = 2 
     741    my $results = {}; 
    740742 
    741743    while (my $item = shift(@filter)) { 
    742744        # attr=foo => no extra white space ! 
    743745        # \W is false, it is possible to have two char 
    744         my ($attr, $mode, $val) = $item =~ /^(\w+)(?:([^\w*]+)(.+))?$/ or next; 
    745         if (!$mode) { 
    746             $mode = '~'; 
     746        my ($attr, $attrref, $operator, $val) = $item =~ /^(\w+)(?:\.([\.\w]+))?(?:([^\w*]+)(.+))?$/ or next; 
     747        if (!$operator) { 
     748            $operator = '~'; 
    747749            $val = shift(@filter); 
    748750        } 
     
    753755        defined($val) or $val =  ''; 
    754756 
     757        $base->log(LA_DEBUG, "Search for %s %s (ref %s) %s %s", $class->type, $attr, $attrref || '(none)', $operator || '', $val); 
     758 
    755759        # Invalid filter due to impossible value: 
    756         if ($mode ne '~' && !($mode eq '=' && $val eq 'NULL')) { 
     760        if ($operator ne '~' && !($operator eq '=' && $val eq 'NULL')) { 
    757761            if (!$attribute->checkinputformat($val)) { 
    758762                $base->log(LA_ERR, "Invalid format value $val for attribute $attr"); 
     
    761765        } 
    762766 
    763         $val = $attribute->input($val) unless($mode eq '=' && $val eq 'NULL'); 
     767        if ($attrref) { 
     768            my $otype = $attribute->reference or do { 
     769                $base->log(LA_ERR, "Attribute $attr do not refer to another object"); 
     770                return; 
     771            }; 
     772 
     773            my @results = $base->search_objects($otype, "$attrref$operator$val"); 
     774            $base->log(LA_DEBUG, "Sub search %s res: %s", $otype, join(', ', @results)); 
     775 
     776            if (!@results) { 
     777                $results->{$attr} ||= {}; 
     778                next; 
     779            } 
     780 
     781            ($operator, $val) = ('=', join('||', @results)); 
     782        } 
     783 
     784        my @results = $class->_search_uniq_filter($base, $attr, $operator, $val); 
     785 
     786        $results->{$attr}{$_} = 1 foreach (@results)  
     787    } 
     788 
     789    # Merging filter result 
     790    my ($attrRef) = keys %{ $results } or return; 
     791 
     792    my %mresults = %{ $results->{$attrRef} }; 
     793 
     794    foreach my $attr (keys %{ $results }) { 
     795        my @values = keys %mresults; 
     796        foreach (@values) { 
     797            $results->{$attr}{$_} or delete($mresults{$_}); 
     798        } 
     799    } 
     800 
     801    return(sort keys %mresults); 
     802} 
     803 
     804 
     805sub _search_uniq_filter { 
     806    my ($class, $base, $attr, $operator, $value) = @_; 
     807 
     808    my @attrsql; 
     809    my @attrbind; 
     810 
     811    my $attribute = $base->attribute($class->type, $attr) or do { 
     812        $base->log(LA_ERR, "Unknown attribute $attr"); 
     813        return; 
     814    }; 
     815 
     816    my @values = split(/([\|\&]+)/, $value); 
     817     
     818    # We detect if we can do a very quick search: 
     819    my $forRef = $operator eq '=' && ! grep { $_ eq '*' or $_ eq  '&&' or $_ eq 'NULL' } @values; 
     820    if ($forRef) { 
     821        # Improv perf 
     822        if ($attribute->{inline}) { 
     823            my $sql = sprintf( 
     824                q{select ikey from %s where %s = ANY (?)}, 
     825                $base->db->quote_identifier($class->_object_table), 
     826                $base->db->quote_identifier($attribute->iname), 
     827            ); 
     828            push(@attrsql, $sql); 
     829        } else { 
     830            my $sql = sprintf( 
     831                q{select okey from %s where attr = ? and "value" = ANY (?) }, 
     832                    $base->db->quote_identifier($class->_object_table . '_attributes'), 
     833            ); 
     834            push(@attrbind, $attribute->iname); 
     835            push(@attrsql, $sql); 
     836        } 
     837        push(@attrbind, [ grep { $_ ne '||' } @values ]); 
     838    } else { 
     839 
     840    # No optimisation possible: 
     841    while (defined(my $val = shift(@values))) { 
     842 
     843        if ($val eq '&&') { 
     844            push(@attrsql, 'intersect'); 
     845            next; 
     846        } 
     847        if ($val eq '||') { 
     848            push(@attrsql, 'union'); 
     849            next; 
     850        } 
     851 
     852        $val = $attribute->input($val) unless($operator eq '=' && $val eq 'NULL'); 
    764853 
    765854        my $sql; 
     
    777866                $base->db->quote_identifier($class->_object_table), 
    778867                $base->db->quote_identifier($attribute->iname), 
    779                 ($mode eq '~' ? '::text' : ''), 
    780                 $mode eq '=' && $val eq '*' 
     868                ($operator eq '~' ? '::text' : ''), 
     869                $operator eq '=' && $val eq '*' 
    781870                    ? 'is not NULL' 
    782                     : $mode eq '=' && $val eq 'NULL' 
     871                    : $operator eq '=' && $val eq 'NULL' 
    783872                    ? 'is NULL' 
    784                     : $mode eq '~' 
     873                    : $operator eq '~' 
    785874                        ? 'ILIKE ?' 
    786                         : "$mode ?" 
     875                        : "$operator ?" 
    787876            ); 
    788             push(@{$attrbind{$attr}}, $mode eq '~' ? '%' . $val . '%' : $val) unless($mode eq '=' && ($val eq '*' || $val eq 'NULL')); 
     877            push(@attrbind, $operator eq '~' ? '%' . $val . '%' : $val) unless($operator eq '=' && ($val eq '*' || $val eq 'NULL')); 
    789878        } else { 
    790             if ($mode eq '=' && $val eq 'NULL') { 
     879            if ($operator eq '=' && $val eq 'NULL') { 
    791880                $sql = sprintf(q{ select ikey from %s where ikey 
    792881                          not in (select okey from %s where attr = ? and ("value" is NOT NULL and value != '')) }, 
     
    796885                          ), 
    797886                ); 
    798                 push(@{$attrbind{$attr}}, $attribute->iname); 
     887                push(@attrbind, $attribute->iname); 
    799888            } else { 
    800889                $sql = sprintf( 
     
    805894                    $val eq '*' 
    806895                        ? '' 
    807                         : $mode eq '~' 
     896                        : $operator eq '~' 
    808897                            ? q{and value::text ILIKE ?} 
    809                             : qq{and value $mode ?} 
     898                            : qq{and value $operator ?} 
    810899 
    811900                ); 
    812                 push(@{$attrbind{$attr}}, $attribute->iname); 
    813                 push(@{$attrbind{$attr}}, $mode eq '~' ? '%' . $val . '%' : $val) unless($val eq '*'); 
     901                push(@attrbind, $attribute->iname); 
     902                push(@attrbind, $operator eq '~' ? '%' . $val . '%' : $val) unless($val eq '*'); 
    814903            } 
    815904        } 
    816905 
    817         push(@{ $attrsql{$attr} }, $sql); 
    818     } 
     906        push(@attrsql, $sql); 
     907    } 
     908 
     909    } # Perf 
    819910 
    820911    # building the query 
    821     my @sqlintersec; 
    822912    if (!$base->{wexported}) { 
    823         push(@sqlintersec, sprintf( 
     913        push(@attrsql, 'intersect', sprintf( 
    824914                q{select ikey from %s where exported = true}, 
    825915                $base->db->quote_identifier($class->_object_table) 
    826916            ) 
    827917        ); 
    828     } 
    829     my @bind; 
    830     foreach (keys %attrsql) { 
    831         push(@sqlintersec, '(' . join(" union ", @{$attrsql{$_}}) . ")\n"); 
    832         push(@bind, @{$attrbind{$_} || []}); 
    833918    } 
    834919    my $sth = $base->db->prepare( 
     
    839924            }, 
    840925            $base->db->quote_identifier($class->_object_table), 
    841             @sqlintersec  
    842             ? "where ikey in (\n" . join("\n intersect\n", @sqlintersec) . ")\n" 
     926            @attrsql 
     927            ? "where ikey in (\n" . join(" ", @attrsql) . ")\n" 
    843928            : '', 
    844929        ) 
    845930    ); 
    846     $sth->execute(@bind); 
     931    $sth->execute(@attrbind); 
    847932    my @results; 
    848933    while (my $res = $sth->fetchrow_hashref) { 
Note: See TracChangeset for help on using the changeset viewer.