Wordpress - WP_Query: get 3 random posts from 10 latest

There's one way with:

$args = [
    'post_type'             => 'post',
    'posts_per_page'        => 10,
    'orderby'               => 'date',
    'order'                 => 'DESC',
    'no_found_rows'         => 'true',
    '_shuffle_and_pick'     => 3 // <-- our custom argument
];

$query = new \WP_Query( $args );

where the custom _shuffle_and_pick attribute is supported by this demo plugin:

<?php
/**
 * Plugin Name: Support for the _shuffle_and_pick WP_Query argument.
 */
add_filter( 'the_posts', function( $posts, \WP_Query $query )
{
    if( $pick = $query->get( '_shuffle_and_pick' ) )
    {
        shuffle( $posts );
        $posts = array_slice( $posts, 0, (int) $pick );
    }
    return $posts;
}, 10, 2 );

You can obviously take all the posts and randomize the result with PHP like shown in this answer. Or, you can do the randomization with SQL as well.

Handling the randomization in Database:

There is no built in WordPress function (or argument) to achieve that, however, you may use the posts_request filter to alter the original SQL query set by WP_Query to achieve the randomization from the database alone.

You may use the following CODE in the active theme's functions.php file or as a new custom plugin:

<?php
/**
 *  Plugin Name: Randomize Posts
 *  Plugin URI: https://wordpress.stackexchange.com/a/260877/110572
 *  Description: Randomize posts basd on '_randomize_posts_count' query argument
 *  Author: Fayaz
 *  Version: 1.0
 *  Author URI: http://fmy.me/
 */

function wpse260713_randomize_posts( $sql_query, $query ) {
    $rand = (int) $query->get( '_randomize_posts_count' );
    if( $rand ) {
        $found_rows = '';
        if( stripos( $sql_query, 'SQL_CALC_FOUND_ROWS' ) !== FALSE ) {
            $found_rows = 'SQL_CALC_FOUND_ROWS';
            $sql_query = str_replace( 'SQL_CALC_FOUND_ROWS ', '', $sql_query );
        }
        $sql_query = sprintf( 'SELECT %s wp_posts.* from ( %s ) wp_posts ORDER BY rand() LIMIT %d', $found_rows, $sql_query, $rand );
    }
    return $sql_query;
}
add_filter( 'posts_request', 'wpse260713_randomize_posts', 10, 2 );

Then you may use the query as follows:

$args = array(
    'post_type' => 'post',
    'posts_per_page' => 10,
    'orderby' => 'date',
    'order' => 'DESC',
    'meta_key' => '_thumbnail_id',
    'no_found_rows' => 'true',
    '_randomize_posts_count' => 3
);
$query = new WP_Query( $args );

Comparative analysis:

  • This method will bring only the maximum number of posts defined by _randomize_posts_count from the database, as opposed to bringing all the results and randomizing on the PHP end. So it's better optimized for data communication with the database. This is better if your database server is separate from your web server.

  • If query cache is not enabled, then this solution will be much faster when the difference between the number of random posts shown vs. total posts selection is big. For example: if you are showing 3 random posts from most recent 200 posts, then this method will be a lot faster.

  • If query cache is enabled, then Birgire's method will be faster as it'll avoid later SQL requests. However, for bigger sample size it may still be slower since you'll have to store a lot of information in the query cache.

  • Best if you consider the sample size carefully and select the solution that suits your use case better.

Note: Random methods are very slow (and often non-scalable) compared to general CODE, so no matter what method you choose, be extra cautious when your sample size of randomisation is considerably large (like thousands).


Great work by Fayaz and birgire - much more expert than I might have come up with - but I think there is an easier way, unless I don't understand the question (quite possible!): 1) use get_posts() or, easiest, wp_get_recent_posts(), both of which return arrays by default, accept WP Query $args, and also use no_found_rows=true by default, 2) shuffle the array, 3) then slice off three.

That's how I solved a problem similar to this one for myself, at a point at which I understood close to nothing, as compared to my current state of understanding how close to nothing I understood then. However, birgire and Fayaz's code is cool, so please feel free to go with one or the other!

Tags:

Wp Query