For a relatively small number of posts, you could try the following:
/**
* Return a single random search result
*/
add_action( 'pre_get_posts', function( \WP_Query $q )
{
if( ! is_admin() && $q->is_main_query() && $q->is_search() )
{
$q->set( 'posts_per_page', 1 );
$q->set( 'orderby', 'rand' );
}
} );
where we modify the main front-end search query.
Note that ordering by random doesn’t scale well.
Another approach would be to fetch e.g. max 15 posts from the search query, order these posts by random and then display a single one:
/**
* Return max 15 posts from the search results, ignore paging
* and then only display a single random post
*/
add_action( 'pre_get_posts', function( \WP_Query $q )
{
if( ! is_admin() && $q->is_main_query() && $q->is_search() )
{
$q->set( 'posts_per_page', 15 );
$q->set( 'no_found_rows', true ); // ignore paging
// Display only a single random post from these 15
add_filter( 'the_posts', 'wpse_random_post' );
}
} );
/**
* Return a single random post from the posts results array.
*/
function wpse_random_post( Array $posts )
{
remove_filter( current_filter(), __FUNCTION__ );
if( count( $posts ) > 0 )
$posts = [ $posts[array_rand( $posts )] ];
return $posts;
}