meta_query в WordPress: выборка и сортировка по полям
← Блог
WordPress · 05.10.2026 · 5 мин чтения

meta_query в WordPress: выборка и сортировка по полям

Параметр 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.

Документация

Есть вопрос или дополнение?

Обсудить в Telegram

Добавить комментарий