2

I'm trying to display a list of 5 authors randomly. Here are some solutions I had found:

https://stackoverflow.com/questions/5502609/get-authors-randomly https://wordpress.stackexchange.com/a/91326/1044

I'm currently using the former solution except that I use WP_User_Query like so:

$args  = array(
    'role' => 'subscriber'
);
$wp_user_query = new WP_User_Query($args);
$authors = $wp_user_query->get_results();

// Shuffle list to get random results
shuffle($authors);

However, to my amateurish understanding, this will fetch the whole list of users. So I'm just wondering if I had around 2000-5000 users (or more), will this affect page load drastically? how can I make this more efficient?

UPDATE:

Also would array_rand() be a more efficient method compare to shuffle()?

3

If you are concerned about efficiency, you might want to use the Transients API to store the query. Storing something that you want to randomize might seem counter-intuitive, but if you store the entire query, you can always randomize and manipulate the resulting array to get the results you want.

Here's how to get all the subscribers and store them in a transient, with a but of straight PHP at the end to shuffle/randomize the result and then pick off the first 5 results using array_slice()

if ( false === ( $users = get_transient( 'get_all_subscribers' ) ) ) {
     // this code runs when there is no valid transient set

    $args  = array(
        'role'   => 'subscriber'
    );

    $wp_user_query = new WP_User_Query( $args );

    $users = $wp_user_query->get_results();

    set_transient( 'get_all_subscribers', $users );
}

// proceed with data normally
// randomize the stored result
shuffle( $users );
$rand_users = array_slice( $users, 0, 5 );

var_dump( $rand_users );

and then to make sure the transient is up to date, we'll delete it when a user is updated or added:

// Create a simple function to delete our transient
function delete_all_subscribers_transient() {
     delete_transient( 'get_all_subscribers' );
}
// Add the function to the profile_update and user_registration hooks
add_action( 'profile_update', 'delete_all_subscribers_transient' );
add_action( 'user_register', 'delete_all_subscribers_transient' );
  • Does this randomize the list with every page refresh? – Giraldi Oct 21 '13 at 9:46
  • Did you try it? shuffle() is outside the transient, so it should. – helgatheviking Oct 21 '13 at 9:50
  • Just wondering, will 'shuffling' the entire query every time the page is viewed affect page load? (Sorry for the amateurish question) – Giraldi Oct 22 '13 at 2:47
  • 1
    It's not amateurish, but you'd have to actual run some benchmark tests to compare, and I don't know how to do that either. I'd wager that shuffling an array is a lot more efficient than running a custom query on every page load. – helgatheviking Oct 22 '13 at 9:05
  • You could shuffle $users when the transient is created and then use a random starting point for the array_slice. Then you are only shuffling once. – Charles Clarkson Oct 22 '13 at 14:17
3

You don't want to fetch all users if you only need 5.

If you want to use WP_User_Query() to fetch 5 users by random, you can try to use the pre_user_query hook to overwrite the orderby part:

$args  = array(
    'role'   => 'subscriber',
    'number' => 5,
);
add_action( 'pre_user_query', 'my_pre_user_query' );
$wp_user_query = new WP_User_Query( $args );

where

function my_pre_user_query( $q ){
    $q->query_orderby = ' ORDER BY RAND() ';

    // remove the hook
    remove_action( current_filter(), __FUNCTION__ );
}

Update:

This article contains a trick to cut down the number of rows, using a special WHERE condition, before the ORDER BY RAND() kicks in ... as far as I understand it.

The WHERE condition in your case might be

WHERE RAND()<(SELECT ((5/COUNT(*))*10) FROM wp_users)

So if you have a very large number of users, then you could try out this modified action callback:

function my_pre_user_query( $q ){
    $q->query_where = str_replace( 'WHERE 1=1', 'WHERE RAND()<(SELECT ((5/COUNT(*))*10) FROM wp_users) ', $q->query_where );
    $q->query_orderby = ' ORDER BY RAND() ';

    // remove hook
    remove_action( current_filter() , __FUNCTION__ );
}

if the trick from the above article is working!

Here is a more general version of the modified callback:

function my_pre_user_query( $q ){

    $limit = preg_replace( '/[^\d]/', '', $q->query_limit );

    $from   = 'WHERE 1=1';
    $to     = sprintf( 'WHERE RAND()<(SELECT ((%d/COUNT(*))*10) FROM %susers)', 
                        $limit, 
                        $GLOBALS['wpdb']->prefix 
             );

    $q->query_where   = str_replace( $from, $to, $q->query_where );
    $q->query_orderby = ' ORDER BY RAND() ';

    // remove the hook    
    remove_action( current_filter() , __FUNCTION__ );
}
  • You can give the $args a query_id parameter that you can then look for conditionally in the my_user_query instead of having to remove_action. – helgatheviking Oct 21 '13 at 8:41
  • @helgatheviking thanks for pointing out that method – birgire Oct 21 '13 at 8:49
  • I don't think I knew you could remove the action from within itself. Neat. Many ways to do the same thing. – helgatheviking Oct 21 '13 at 9:08
  • This works well but I heard that RAND() is inefficient for large tables. Found this tips here but not really sure yet how to implement it. What do you think? – Giraldi Oct 21 '13 at 16:33
  • Yes, but always do some benchmarks on your own system to test ;-) This article gives a nice trick on how to speed things up using an alternative ORDER BY RAND() method. – birgire Oct 21 '13 at 17:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.