Параметр meta_query в WordPress фильтрует записи по произвольным полям. Например, выбирает товары дешевле 5000, предстоящие события или статьи с указанным источником.
Разберём условия WP_Query, сравнение чисел и дат, связки AND и OR, а также сортировку результатов. В конце — как применить фильтр к архиву и проверить скорость запроса.
Как добавить поля в редактор и сохранить их, описано в статье о метабоксах и произвольных полях. В примерах ниже ключи начинаются с подчёркивания, как там.
Простой пример WP_Query с meta_query
Записи, у которых заполнен источник:
$query = new WP_Query( array(
'post_type' => 'post',
'posts_per_page' => 10,
'meta_query' => array(
array(
'key' => '_mytheme_source_url',
'compare' => 'EXISTS',
),
),
) );
Даже одно условие передаётся внутри массива meta_query. Параметр key задаёт ключ поля, value — искомое значение, compare — оператор, type — тип сравнения. Для проверки наличия поля значение не требуется.
По умолчанию используется оператор =. Простое равенство можно записать короче, на верхнем уровне запроса: 'meta_key' => '_color', 'meta_value' => 'red'.
Операторы compare: сравнение, диапазон и наличие поля
=,!=,>,>=,<,<=— обычные сравнения.INиNOT IN— значение входит в список.valueпередаётся массивом:array( 'red', 'blue' ).BETWEENиNOT BETWEEN— диапазон,value— массив из двух значений. Границы входят в диапазон.LIKEиNOT LIKE— поиск подстроки. Знаки%по краям WordPress добавит сам.EXISTSиNOT EXISTS— поле есть или его нет.valueне нужен.REGEXPиNOT REGEXP— регулярное выражение MySQL.
Как сравнивать числа и даты через type
Значения полей хранятся в базе как текст. Без указания типа MySQL сравнивает строки посимвольно: '900' > '5000', потому что 9 больше 5. Для чисел нужен type:
$query = new WP_Query( array(
'post_type' => 'product',
'meta_query' => array(
array(
'key' => '_price',
'value' => array( 1000, 5000 ),
'compare' => 'BETWEEN',
'type' => 'NUMERIC',
),
),
) );
NUMERICв WP_Meta_Query использует целочисленный типSIGNED. Для дробных значений, например цен с копейками, выбирайDECIMAL.- Для цен с копейками указывай точность:
'type' => 'DECIMAL(10,2)'. DATE,DATETIMEиTIME— для дат и времени.
Дату храни в формате 2026-10-13 (Y-m-d). Тогда она правильно сравнивается и с типом DATE, и даже как строка. Формат 13.10.2026 так не работает: MySQL не распознает его как дату, и сравнение вернёт неверные записи. Предстоящие события:
'meta_query' => array(
array(
'key' => '_event_date',
'value' => current_time( 'Y-m-d' ),
'compare' => '>=',
'type' => 'DATE',
),
),
current_time() возвращает дату в часовом поясе из настроек сайта, а не сервера. Иначе события могут пропасть из списка за несколько часов до полуночи.
AND и OR: объединение условий meta_query
Условия объединяются через AND, если не указано иначе. Для «или» нужен 'relation' => 'OR'. Группы можно вкладывать друг в друга. Товары в наличии, которые дешевле 3000 или участвуют в распродаже:
'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_in_stock',
'value' => '1',
),
array(
'relation' => 'OR',
array(
'key' => '_price',
'value' => 3000,
'compare' => '<',
'type' => 'NUMERIC',
),
array(
'key' => '_sale',
'compare' => 'EXISTS',
),
),
),
Сортировка записей по значению поля
Условию можно дать имя — ключ массива вместо номера — и сортировать по нему. Предстоящие события по дате, при одинаковой дате — по названию:
$events = new WP_Query( array(
'post_type' => 'event',
'posts_per_page' => 20,
'meta_query' => array(
'event_date' => array(
'key' => '_event_date',
'value' => current_time( 'Y-m-d' ),
'compare' => '>=',
'type' => 'DATE',
),
),
'orderby' => array(
'event_date' => 'ASC',
'title' => 'ASC',
),
) );
Здесь event — свой тип записей. Для обычных записей укажи post. Сортировка учитывает type условия, поэтому даты и числа упорядочиваются правильно.
Старый способ — 'meta_key' => '_price', 'orderby' => 'meta_value_num' — тоже работает. Но у него и у именованного условия есть общая особенность: записи без этого поля в результат не попадут. Если их нужно оставить и показать в начале списка, добавь второе условие с NOT EXISTS:
'meta_query' => array(
'relation' => 'OR',
'price' => array(
'key' => '_price',
'compare' => 'EXISTS',
'type' => 'NUMERIC',
),
'no_price' => array(
'key' => '_price',
'compare' => 'NOT EXISTS',
),
),
'orderby' => array( 'price' => 'ASC' ),
В архивах — через pre_get_posts
Для архива рубрики или страницы блога добавляй фильтр в основной запрос через pre_get_posts. Подмена цикла отдельным WP_Query может рассогласовать число страниц и привести к 404:
function mytheme_events_archive( $query ) {
if ( is_admin() || ! $query->is_main_query() || ! $query->is_category( 'events' ) ) {
return;
}
$query->set( 'meta_query', array(
'event_date' => array(
'key' => '_event_date',
'value' => current_time( 'Y-m-d' ),
'compare' => '>=',
'type' => 'DATE',
),
) );
$query->set( 'orderby', array( 'event_date' => 'ASC' ) );
}
add_action( 'pre_get_posts', 'mytheme_events_archive' );
Почему так и что делать, если вторая страница отдаёт 404, — в статье о постраничной навигации.
Как ускорить запросы по произвольным полям
В стандартной таблице wp_postmeta есть индекс по ключу поля, но нет универсального индекса по его значению. Сложные условия могут потребовать нескольких соединений таблицы и преобразования типов.
Скорость зависит от объёма данных, условий и индексов. Не оценивай её только по числу записей: посмотри фактический SQL и время выполнения. Начни с таких изменений:
- Ограничивай выборку:
posts_per_pageвместо-1. - Если пагинация не нужна, добавь
'no_found_rows' => true: WordPress не будет считать общее число записей. - Если нужны только номера записей, укажи
'fields' => 'ids'. - Проверяй скорость
LIKEиREGEXPна больших таблицах: поиск по значению поля может требовать просмотра большого числа строк. - Признаки «да/нет» и наборы вариантов вроде «цвет» или «город» часто удобнее хранить в таксономии: по ней запросы быстрее, и у каждого варианта сразу есть свой архив.
- Результат тяжёлого запроса можно сохранить в transient — пример в статье о похожих записях.
Готовый SQL-запрос лежит в $query->request. Его удобно вывести при отладке и проверить, какие соединения и условия построил WordPress.