Задать вопрос
@alex0176

Как отобразить при случайном порядке уникальность страниц?

Привет!
Помогите решить следующую задачу: использую WP_Query() сортировка постов ORDER BY RAND(), а также пагинацию. Запросы выполняются асинхронно. Так вот, когда пользователь загружает больше элементов, записи (посты) могут измениться, поскольку каждое выполнение запроса меняет порядок результатов. В результате некоторые записи могут появляться несколько раз, а другие могут быть полностью пропущены, что приводит к непоследовательному опыту разбиения на страницы.
Проблема в том, что ORDER BY RAND() перетасовывает результаты при каждом запросе, что приводит к дублированию и отсутствию записей в пагинации.
Как можно решить эту проблему, чтобы при переходе на другие страницы посты не повторялись, а также не были пропущены. Нужен какой то равномерный алгоритм работы.

$args = array(
       'post_type' => array( 'post')
	'posts_per_page' => 10,
	'orderby' => 'rand',
);
$q = new WP_Query( $args );


Спасибо за любой совет
  • Вопрос задан
  • 124 просмотра
Подписаться 1 Простой 2 комментария
Пригласить эксперта
Ответы на вопрос 5
@Asokr
как вариант решения - запоминать ID те что уже показаны, и добавить в запрос фильтр "NOT_IN" куда передавать массив уже показаных сущностей.
Ответ написан
Комментировать
@Everything_is_bad
При рандоме не нужна пагинации, пользователи просто обновляют текущую страницу и получают новый случайный набор постов. Либо тебе придется для каждого юзера или сессии постоянно хранить, однажды полученный случайный порядок всех страниц, как в этот порядок встраивать новые посты, хз.
Ответ написан
@weart
2 запроса - первым получаем ids рандомных постов, вторым основной запрос

$random_args = [
            'post_type' => 'post',
            'post_status' => 'publish',
            'orderby' => 'rand',
            'posts_per_page' => -1
            'fields' => 'ids',
        ];

$random_query = new WP_Query($random_args);
$random_ids = $random_query->posts;

// основной запрос
$args = [
         'posts_per_page' => 10,
         'post__in'  => $random_ids,
         'orderby' => 'post__in' ,
        ];

$query = new WP_Query($args);
Ответ написан
@maksam07
у sql RAND() есть аргумент seed, который дает возможность "1" раз перемешать посты и всегда возвращать необходимую последовательность.

Один из примеров:
<?php
// functions.php

add_action('wp_ajax_load_more_posts', 'load_more_posts_callback');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts_callback');

function load_more_posts_callback() {
    if (!isset($_SESSION['random_seed'])) {
        $_SESSION['random_seed'] = rand(1, 999999);
    }
    $seed = $_SESSION['random_seed'];

    add_filter('posts_orderby', function($orderby) use ($seed) {
        return "RAND($seed)";
    });

    $args = array(
        'post_type' => 'post',
        'posts_per_page' => 10,
        'paged' => isset($_POST['page']) ? intval($_POST['page']) : 1,
    );

    $q = new WP_Query($args);

    $output = '';
    if ($q->have_posts()) {
        while ($q->have_posts()) {
            $q->the_post();
            $output .= '<h2>' . get_the_title() . '</h2>';
        }
    }

    wp_send_json(array(
        'posts' => $output,
        'seed' => $seed,
    ));

    wp_die();
}
?>
Ответ написан
Комментировать
@rPman
Если скорость исполнения не критична (что маловероятно но вдруг) можно обойтись без random, а сортировать строки по хешу от crc32(row_id*MAX_USER_ID+user_id) или представив числа как строки например:
select * from table order by md5(concat(id,'|',$user_id))

советую использовать числовые хеши типа crc32, по уму они быстрее.

MAX_USER_ID это максимальное значение user_id, что бы значения не пересекались, так как если просто сложить id+user_id то 1+3 выдаст тот же результат что и 3+1. Можно чуть сократить сдвиг (особенно если хочешь поместить результат в 32-битный int), если убрать из интервала неиспользуемые минимальные user_id - id*(MAX_USER_ID-MIN_USER_ID)+(user_id-MIN_USER_ID). И конечно можно битовыми сдвигами пользоваться.

Достоинство - это полностью вычисляемый алгоритм, ничего хранить не придется.

Недостаток подхода - каждый раз при запросе будет сканироваться (и вычисляться хеш) вся таблица, в т.ч. и при пагинации, но нагрузка останется на базе данных. Но так как любое эффективное решение потребует где то хранить порядок для пользователя, можно и тут просто сохранять хеш в специальной таблице и считать его однократно.

Второй недостаток - новые записи могут попасть в начальную часть списка, которую пользователь уже прочитал, т.е. все статьи в результате сдвинутся вперед. Это можно частично решить (статью, появившуюся в начале, пользователь так и не прочитает), если пагинацию делать не постраничную, а записи по ее id, т.е. в интерфейсе есть next/prev но нет номера страницы (в ссылке id записи или ее вычисляемый хеш).

p.s. как пользователь скажу, что этот подход лучше не применять, мало того, как пользователь я не вижу пользы в случайных статьях.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы