Чому слід уникати post__not_in у користувацьких запитах WP_Query
Привіт:) У цій замітці розповім чому слід уникати аргументу post__not_in
при побудові користувацького WP_Query-запиту WordPress.
Аргумент WP_Query post__not_in
здається корисною опцією, але він може призвести до низької продуктивності на завантаженому та/або великому сайті через вплив на частоту потрапляння у кеш.
Зазвичай він використовується для виключення певних ідентифікаторів (ID) постів (записів) з результатів запиту. Наприклад, є віджет, який показує останні 5 публікацій на кожній сторінці, але у ньому потрібно виключити із виведення на екран поточну публікацію.
Вивести 5 останніх постів можна за допомогою коду
// Display the most recent news posts
function my_recent_news_widget() {
$args = array(
'category_name' => 'news',
'posts_per_page' => 5,
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
);
$recent_posts = new WP_Query( $args );
echo '<div class="most-recent-news"><h1>News</h1>';
while ( $recent_posts->have_posts() ) {
$recent_posts->the_post();
the_title( '<h2><a href="' . get_permalink() . '">', '</a></h2>');
}
echo '</div>';
wp_reset_postdata();
}
Типовий підхід до вирішення задачі виключення із виводу на екран поточної публікації — модифікувати цю функцію, додавши необов'язковий аргумент функції та параметр запиту post__not_in
:
// Display the most recent news posts (but not the current one)
function my_recent_news_widget( $exclude = array() ) {
$args = array(
'category_name' => 'news',
'posts_per_page' => 5,
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'post__not_in' => $exclude,
);
$recent_posts = new WP_Query( $args );
echo '<div class="most-recent-news"><h1>News</h1>';
while ( $recent_posts->have_posts() ) {
$recent_posts->the_post();
the_title( '<h2><a href="' . get_permalink() . '">', '</a></h2>');
}
echo '</div>';
wp_reset_postdata();
}
Цю функцію можна викликати десь у файлі-шаблоні WordPress-теми:
<?php my_recent_news_widget( [ get_the_ID() ] ); ?>
В результаті будуть виведені останні 5 публікацій, за винятком поточної (тієї, що переглядається відвідувачем сайту у цей момент). Так, цей спосіб повністю вирішує задачу, але є не зовсім коректним у відношенні продуктивності. І ось чому.
до змісту ↑В чому проблема post__not_in
Запит, який раніше використовував вбудований кеш запитів, тепер є унікальним для кожного допису або сторінки завдяки доданому AND ID not in ( '12345' )
. Це пов'язано з тим, що ключ кешу (хеш аргументів) тепер включає список принаймні одного ідентифікатора, який є різним для всіх дописів. Таким чином, замість того, щоб наступні сторінки отримували список із 5 дописів з кешу об'єкта, він буде пропущений кешем, і база даних виконає ту ж роботу на декількох сторінках.
В результаті кожен з цих запитів тепер кешується окремо, невиправдано збільшуючи використання Memcached. Для сайту з сотнями тисяч публікацій це потенційно може вплинути на розмір об'єктного кешу і призвести до передчасного видалення (очищення) кешу.
до змісту ↑Який вихід
Майже у всіх випадках досягти значного покращення швидкості можливо, обираючи більше записів і пропускаючи виключені із запиту.
Ви можете підвищити продуктивність, переконавшись, що запит публікацій є узгодженим для всіх URL-адрес, і що він отримує останні 6 дописів з об'єктного кешу. Якщо ви очікуєте, що список $exclude
буде більшим, ніж один пост, встановіть більший ліміт, наприклад, 10. Зробіть це число фіксованим, а не змінним, щоб зменшити кількість варіантів кешу.
Оновлена функція більше не виключає публікації в SQL через post__not_in
, а використовує умови в циклі:
// Display the most recent news posts (but not the current one)
function my_recent_news_widget( $exclude = array() ) {
$args = array(
'category_name' => 'news',
'posts_per_page' => 10,
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
);
$recent_posts = new WP_Query( $args );
echo '<div class="most-recent-news"><h1>News</h1>';
$posts = 0; // count the posts displayed, up to 5
while ( $recent_posts->have_posts() && $posts < 5 ) {
$recent_posts->the_post();
$current = get_the_ID();
if ( ! in_array( $current, $exclude ) ) {
$posts++;
the_title( '<h2><a href="' . get_permalink() . '">', '</a></h2>');
}
}
echo '</div>';
wp_reset_postdata();
}
Хоча цей підхід вимагає трохи логіки в PHP, він краще використовує кеш запитів і дозволяє уникнути створення багатьох варіантів кешу, які можуть вплинути на масштабованість і стабільність сайту.
Джерело: https://docs.wpvip.com/code-quality/using-post__not_in/