Чому слід уникати 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/

Михайло Петров
Михайло Петров

Мене звати Михайло. Я є WordPress-розробником. Створюю сайти з "нуля", розробляю плагіни, оновлюю наявний функціонал та підтримую готові проєкти.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *