[Bps-public-commit] dbix-searchbuilder branch, distinct-argument-in-joins, created. 1.63_01-42-g2cf2194

Ruslan Zakirov ruz at bestpractical.com
Wed May 8 05:53:04 EDT 2013


The branch, distinct-argument-in-joins has been created
        at  2cf2194aecbc8e49bf89563322e73c7b28cccd28 (commit)

- Log -----------------------------------------------------------------
commit f7f2750d633afee4b4622c88f55580a2a88a156c
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue May 7 18:05:47 2013 +0400

    DISTINCT argument in Join method
    
    if all joins are marked as distinct then we can
    avoid distincting whole query.

diff --git a/lib/DBIx/SearchBuilder.pm b/lib/DBIx/SearchBuilder.pm
index ef722a4..a836bf1 100755
--- a/lib/DBIx/SearchBuilder.pm
+++ b/lib/DBIx/SearchBuilder.pm
@@ -136,7 +136,7 @@ sub CleanSlate {
     $self->{'first_row'}        = 0;
     $self->{'must_redo_search'} = 1;
     $self->{'show_rows'}        = 0;
-    $self->{'joins_are_distinct'} = 0;
+    $self->{'joins_are_distinct'} = undef;
     @{ $self->{'aliases'} } = ();
 
     delete $self->{$_} for qw(
diff --git a/lib/DBIx/SearchBuilder/Handle.pm b/lib/DBIx/SearchBuilder/Handle.pm
index fc4ff5e..aa9b287 100755
--- a/lib/DBIx/SearchBuilder/Handle.pm
+++ b/lib/DBIx/SearchBuilder/Handle.pm
@@ -976,6 +976,7 @@ sub Join {
         FIELD2        => undef,
         ALIAS2        => undef,
         EXPRESSION    => undef,
+        DISTINCT      => 0,
         @_
     );
 
@@ -1079,6 +1080,12 @@ sub Join {
     $meta->{'criteria'}{'base_criterion'} =
         [ { field => "$alias.$args{'FIELD2'}", op => '=', value => $criterion } ];
 
+    if ( $args{'DISTINCT'} && !defined $args{'SearchBuilder'}{'joins_are_distinct'} ) {
+        $args{SearchBuilder}{joins_are_distinct} = 1;
+    } elsif ( !$args{'DISTINCT'} ) {
+        $args{SearchBuilder}{joins_are_distinct} = 0;
+    }
+
     return ($alias);
 }
 

commit 2cf2194aecbc8e49bf89563322e73c7b28cccd28
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed May 8 13:44:27 2013 +0400

    DISTINCT argument in Join and NewAlias
    
    We have hidden joins_are_distinct to indicate that all joins
    are distinct, but it's only useful when all joins and limits
    are within one function and SB object is not passed around.
    
    By using DISTINCT argument a new join can be marked as distinct.
    If all joins are marked as distinct then whole query is distinct
    and we can avoid DB side distinct operations.

diff --git a/lib/DBIx/SearchBuilder.pm b/lib/DBIx/SearchBuilder.pm
index a836bf1..c906c8e 100755
--- a/lib/DBIx/SearchBuilder.pm
+++ b/lib/DBIx/SearchBuilder.pm
@@ -1208,7 +1208,17 @@ sub _GroupClause {
 
 =head2 NewAlias
 
-Takes the name of a table.
+Takes the name of a table and paramhash with TYPE and DISTINCT.
+
+Use TYPE equal to C<LEFT> to indicate that it's LEFT JOIN. Old
+style way to call (see below) is also supported, but should be
+B<avoided>:
+
+    $records->NewAlias('aTable', 'left');
+
+True DISTINCT value indicates that this join keeps result set
+distinct and DB side distinct is not required. See also L</Join>.
+
 Returns the string of a new Alias for that table, which can be used to Join tables
 or to Limit what gets found by a search.
 
@@ -1217,7 +1227,9 @@ or to Limit what gets found by a search.
 sub NewAlias {
     my $self  = shift;
     my $table = shift || die "Missing parameter";
-    my $type = shift;
+    my %args = @_%2? (TYPE => @_) : (@_);
+
+    my $type = $args{'TYPE'};
 
     my $alias = $self->_GetAlias($table);
 
@@ -1232,6 +1244,12 @@ sub NewAlias {
         die "Unsupported alias(join) type";
     }
 
+    if ( $args{'DISTINCT'} && !defined $self->{'joins_are_distinct'} ) {
+        $self->{'joins_are_distinct'} = 1;
+    } elsif ( !$args{'DISTINCT'} ) {
+        $self->{'joins_are_distinct'} = 0;
+    }
+
     return $alias;
 }
 
@@ -1278,6 +1296,11 @@ It is also possible to join to a pre-existing, already-limited
 L<DBIx::SearchBuilder> object, by passing it as COLLECTION2, instead
 of providing an ALIAS2 or TABLE2.
 
+By passing true value as DISTINCT argument join can be marked distinct. If
+all joins are distinct then whole query is distinct and SearchBuilder can
+avoid L</_DistinctQuery> call that can hurt performance of the query. See
+also L</NewAlias>.
+
 =cut
 
 sub Join {
diff --git a/lib/DBIx/SearchBuilder/Handle.pm b/lib/DBIx/SearchBuilder/Handle.pm
index aa9b287..2609902 100755
--- a/lib/DBIx/SearchBuilder/Handle.pm
+++ b/lib/DBIx/SearchBuilder/Handle.pm
@@ -976,7 +976,6 @@ sub Join {
         FIELD2        => undef,
         ALIAS2        => undef,
         EXPRESSION    => undef,
-        DISTINCT      => 0,
         @_
     );
 
@@ -1025,6 +1024,9 @@ sub Join {
                 }
             }
 
+        } else {
+            # we found alias, so NewAlias should take care of distinctness
+            $args{'DISTINCT'} = 1 unless exists $args{'DISTINCT'};
         }
 
         unless ( $alias ) {
diff --git a/t/02searches_joins.t b/t/02searches_joins.t
index 7e9dbd1..1661f2b 100644
--- a/t/02searches_joins.t
+++ b/t/02searches_joins.t
@@ -6,7 +6,7 @@ use Test::More;
 BEGIN { require "t/utils.pl" }
 our (@AvailableDrivers);
 
-use constant TESTS_PER_DRIVER => 47;
+use constant TESTS_PER_DRIVER => 59;
 
 my $total = scalar(@AvailableDrivers) * TESTS_PER_DRIVER;
 plan tests => $total;
@@ -208,6 +208,91 @@ diag "LEFT JOIN optimization and OR clause" if $ENV{'TEST_VERBOSE'};
     is( $users_obj->Count, 4, "all users" );
 }
 
+diag "DISTINCT in Join" if $ENV{'TEST_VERBOSE'};
+{
+    $users_obj->CleanSlate;
+    is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+    ok( !$users_obj->_isJoined, "new object isn't joined");
+    my $alias = $users_obj->Join(
+        FIELD1 => 'id',
+        TABLE2 => 'UsersToGroups',
+        FIELD2 => 'UserId',
+        DISTINCT => 1,
+    );
+    $users_obj->Limit(
+        ALIAS => $alias,
+        FIELD => 'GroupId',
+        VALUE => 1,
+    );
+    ok( $users_obj->BuildSelectQuery !~ /DISTINCT|GROUP\s+BY/i, 'no distinct in SQL');
+    is_deeply(
+        [ sort map $_->Login, @{$users_obj->ItemsArrayRef} ],
+        [ 'aurelia', 'ivan', 'john' ],
+        "members of dev group"
+    );
+}
+
+diag "DISTINCT in NewAlias" if $ENV{'TEST_VERBOSE'};
+{
+    $users_obj->CleanSlate;
+    is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+    ok( !$users_obj->_isJoined, "new object isn't joined");
+    my $alias = $users_obj->NewAlias('UsersToGroups', DISTINCT => 1);
+    $users_obj->Join(
+        FIELD1 => 'id',
+        ALIAS2 => $alias,
+        FIELD2 => 'UserId',
+    );
+    $users_obj->Limit(
+        ALIAS => $alias,
+        FIELD => 'GroupId',
+        VALUE => 1,
+    );
+    ok( $users_obj->BuildSelectQuery !~ /DISTINCT|GROUP\s+BY/i, 'no distinct in SQL');
+    is_deeply(
+        [ sort map $_->Login, @{$users_obj->ItemsArrayRef} ],
+        [ 'aurelia', 'ivan', 'john' ],
+        "members of dev group"
+    );
+}
+
+diag "mixing DISTINCT" if $ENV{'TEST_VERBOSE'};
+{
+    $users_obj->CleanSlate;
+    is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+    ok( !$users_obj->_isJoined, "new object isn't joined");
+    my $u2g_alias = $users_obj->Join(
+        FIELD1 => 'id',
+        TABLE2 => 'UsersToGroups',
+        FIELD2 => 'UserId',
+        DISTINCT => 0,
+    );
+    my $g_alias = $users_obj->Join(
+        ALIAS1 => $u2g_alias,
+        FIELD1 => 'GroupId',
+        TABLE2 => 'Groups',
+        FIELD2 => 'id',
+        DISTINCT => 1,
+    );
+
+    $users_obj->Limit(
+        ALIAS => $g_alias,
+        FIELD => 'Name',
+        VALUE => 'Developers',
+    );
+    $users_obj->Limit(
+        ALIAS => $g_alias,
+        FIELD => 'Name',
+        VALUE => 'Sales',
+    );
+    ok( $users_obj->BuildSelectQuery =~ /DISTINCT|GROUP\s+BY/i, 'distinct in SQL');
+    is_deeply(
+        [ sort map $_->Login, @{$users_obj->ItemsArrayRef} ],
+        [ 'aurelia', 'ivan', 'john' ],
+        "members of dev group"
+    );
+}
+
     cleanup_schema( 'TestApp', $handle );
 
 }} # SKIP, foreach blocks

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list