fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2011-03-08 06:34 am

[dw-free] Search for several interests at once

[commit: http://hg.dwscoalition.org/dw-free/rev/21d02a514f23]

http://bugs.dwscoalition.org/show_bug.cgi?id=2821

Refactoring for backend to facilitate multiple interests search. (No
functional change)

Patch by [personal profile] kareila.

Files modified:
  • cgi-bin/DW/Controller/Search/Interests.pm
  • cgi-bin/LJ/Keywords.pm
--------------------------------------------------------------------------------
diff -r d7de4e60ea79 -r 21d02a514f23 cgi-bin/DW/Controller/Search/Interests.pm
--- a/cgi-bin/DW/Controller/Search/Interests.pm	Tue Mar 08 13:42:11 2011 +0800
+++ b/cgi-bin/DW/Controller/Search/Interests.pm	Tue Mar 08 14:33:42 2011 +0800
@@ -291,41 +291,45 @@ sub interest_handler {
         $rv->{intid} = $intid;
         $rv->{intcount} = $intcount;
 
-        my $dbr = LJ::get_db_reader();
-        my $int_query = sub {
-            my $i = shift;  # comminterests or userinterests
-            my $LIMIT = 500;
-            my $q = "SELECT $i.userid FROM $i, userusage
-                     WHERE $i.intid = ? AND $i.userid = userusage.userid
-                     ORDER BY userusage.timeupdate DESC LIMIT $LIMIT";
-            my $uref = $dbr->selectall_arrayref( $q, undef, $intid );
-            return LJ::load_userids( map { $_->[0] } @$uref );
-            # can't trust LJ::load_userids to maintain sort order
-        };
-
         # filtering by account type
         $rv->{type_list} = [ 'none', 'P', 'C', 'I' ];
 
         my $type = $args->{type};
         $type = 'none' unless $type && $type =~ /^[PCI]$/;
-        my $typefilter = sub { return $type eq 'none' ? 1 :
-                               $_[0]->journaltype eq $type };
 
-        # get top 500 user and/or community results
-        my $us = {};
+        # constructor for filter links
+        $rv->{type_link} = sub {
+            return '' if $type eq $_[0];  # no link for active selection
+            my $typearg = $_[0] eq 'none' ? '' : $_[0];
+            return LJ::page_change_getargs( type => $typearg );
+        };
 
-        unless ( $type eq 'C' ) {  # unless no users needed
-            $us = $int_query->( "userinterests" );
-            $rv->{comm_count} = 1;  # reset later if comms loaded
+        # determine which account types we need to search for
+        my $type_opts = {};
+        my %opt_map = ( C => 'nousers', P => 'nocomms', I => 'nocomms' );
+        $type_opts = { $opt_map{$type} => 1 } if defined $opt_map{$type};
+
+        my @uids = LJ::users_with_all_ints( [$intid], $type_opts );
+
+        # limit results to 500 most recently updated journals
+        if ( scalar @uids > 500 ) {
+            my $dbr = LJ::get_db_reader();
+            my $qs = join ',', map { '?' } @uids;
+            my $uref = $dbr->selectall_arrayref(
+                "SELECT userid FROM userusage WHERE userid IN ($qs)
+                 ORDER BY timeupdate DESC LIMIT 500", undef, @uids );
+            die $dbr->errstr if $dbr->err;
+            @uids = map { $_->[0] } @$uref;
         }
 
-        unless ( $type =~ /^[PI]$/ ) {  # unless no comms needed
-            my $cus = $int_query->( "comminterests" );
-            $rv->{comm_count} = scalar values %$cus;
+        my $us = LJ::load_userids( @uids );
 
-            # combine the two sets of results
-            @{ $us }{ keys %$cus } = values %$cus;
-        }
+        # prepare to filter and sort the results into @ul
+
+        my $typefilter = sub {
+            $rv->{comm_count}++ if $_[0]->is_community;
+            return $type eq 'none' ? 1 : $_[0]->journaltype eq $type;
+        };
 
         my $should_show = sub {
             return $_[0]->should_show_in_search_results( for => $remote );
@@ -339,13 +343,7 @@ sub interest_handler {
                  grep { $_ && $typefilter->( $_ ) && $should_show->( $_ ) }
                  values %$us;
         $rv->{type_count} = scalar @ul if $intcount != scalar @ul;
-
-        # construct filter links
-        $rv->{type_link} = sub {
-            return '' if $type eq $_[0];  # no link for active selection
-            my $typearg = $_[0] eq 'none' ? '' : $_[0];
-            return LJ::page_change_getargs( type => $typearg );
-        };
+        $rv->{comm_count} = 1 if $type_opts->{nocomms};  # doesn't count
 
         if ( @ul ) {
             # pagination
diff -r d7de4e60ea79 -r 21d02a514f23 cgi-bin/LJ/Keywords.pm
--- a/cgi-bin/LJ/Keywords.pm	Tue Mar 08 13:42:11 2011 +0800
+++ b/cgi-bin/LJ/Keywords.pm	Tue Mar 08 14:33:42 2011 +0800
@@ -102,6 +102,47 @@ sub interest_string_to_list {
 
     # final list is ,-sep
     return grep { length } split (/\s*,\s*/, $intstr);
+}
+
+
+# This function takes a list of intids and returns the list of uids
+# for accounts interested in ALL of the given interests.
+#
+# Args: arrayref of intids, hashref of opts
+# Returns: array of uids
+#
+# Valid opts: nousers => 1, nocomms => 1
+#
+sub users_with_all_ints {
+    my ( $ints, $opts ) = @_;
+    $opts ||= {};
+    return unless defined $ints && ref $ints eq 'ARRAY';
+
+    my @intids = grep /^\d+$/, @$ints;  # numeric only
+    return unless @intids;
+
+    my @tables;
+    push @tables, 'userinterests' unless $opts->{nousers};
+    push @tables, 'comminterests' unless $opts->{nocomms};
+    return unless @tables;
+
+    my $dbr = LJ::get_db_reader();
+    my $qs = join ',', map { '?' } @intids;
+    my @uids;
+
+    foreach ( @tables ) {
+        my $q = "SELECT userid FROM $_ WHERE intid IN ($qs)";
+        my $uref = $dbr->selectall_arrayref( $q, undef, @intids );
+        die $dbr->errstr if $dbr->err;
+
+        # Count the number of times the uid appears.
+        # If it's the same as the number of interests, it has all of them.
+        my %ucount;
+        $ucount{$_}++ foreach map { $_->[0] } @$uref;
+        push @uids, grep { $ucount{$_} == scalar @intids } keys %ucount;
+    }
+
+    return @uids;
 }
 
 
--------------------------------------------------------------------------------