query_posts - использование meta_compare для сравнения значений мета-полей (больше, меньше или равно)
Я использую query_posts( $args )
для фильтрации цикла Loop.
Мне нужно отфильтровать записи на основе их meta_value
"vote", иногда меньше чем, иногда равно и так далее...
Я определенно хочу использовать функцию query_posts()
и передавать мой фильтр через $args
!
Я не хочу использовать add_filter('posts_where', 'filter_where');
и затем добавлять оператор AND
к запросу.
Я хочу использовать встроенную функциональность WordPress для фильтрации записей с помощью meta_key
, meta_value
и meta_compare
таким образом:
$args = array( 'meta_key'=>'vote', 'meta_compare'=>'>=', 'meta_value'=>5, 'posts_per_page'=>100 ) )
query_posts( $args );
Результат этого запроса:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND wp_postmeta.meta_key = 'vote' AND wp_postmeta.meta_value >= '5' GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 100
Проблема в следующем:
wp_postmeta.meta_value >= '5'
Должно быть:
wp_postmeta.meta_value >= 5
Тогда это будет работать нормально.
Я не понимаю, почему WordPress добавляет кавычки.
Я использую предопределенные параметры WordPress (<
, >
, <=
, >=
) и очевидно, что это будет работать только с числами, а не со строками, которые должны быть в кавычках.
В документации говорится:
Возвращает записи с ключом произвольного поля 'miles' со значением произвольного поля, которое МЕНЬШЕ ИЛИ РАВНО 22
query_posts('meta_key=miles&meta_compare=<=&meta_value=22');

Начиная с WP 3.1, вы можете привести значение метаполя к любому типу, используя аргумент 'type' в 'meta_query':
$args = array(
'meta_query'=> array(
array(
'key' => 'vote', // Ключ метаполя
'compare' => '>=', // Оператор сравнения
'value' => 5, // Значение для сравнения
'type' => 'numeric', // Тип значения (числовой)
)
)
'posts_per_page' => 100 // Количество записей на странице
) );
query_posts( $args ); // Выполнение запроса

При беглом просмотре документации, кажется, что meta_value
предназначен для строк, а для числовых значений есть meta_value_num
.
Смотрите Параметры Orderby
Обновление
Провёл небольшое исследование.
meta_value_num
действительно игнорируется при фильтрации. Думаю, они просто забыли добавить эту часть. :)
Проблема в том, что WP_Query
корректно получает число как int
(передача в виде массива не имеет значения), но он передаёт сгенерированное условие meta_compare
через $wpdb->prepare()
и явно помечает значение как строку %s
. В этом случае prepare
принудительно обрамляет его в одинарные кавычки.
Похоже, вам всё же придётся фильтровать posts_where
. Можно попробовать просто убрать кавычки у этой конкретной строки вместо того, чтобы генерировать условие вручную.

Я рекомендую сначала преобразовать ваш массив $args
в строку перед передачей в query_posts
. Когда вы создаете массив $args
, система автоматически преобразует число 5 в строку "5" при преобразовании массива обратно в строку.
Поэтому лучше использовать такую запись:
query_posts('meta_key=vote&meta_compare=>=&meta_value=5&posts_per_page=100');
Таким образом, в query_posts
передается та же информация, но число 5 будет обрабатываться как число, а не как строка "5".
Обновление
Как выяснилось, meta_value
хранит значения в виде строк, а не чисел, что делает сравнение "больше/меньше" неэффективным. Однако после дополнительного исследования я обнаружил флаг запроса meta_value_num
.
Если выполнить следующий вызов query_posts
:
query_posts('meta_key=vote&meta_compare=>=&meta_value=5&posts_per_page=100&orderby=meta_value_num');
То вы получите желаемое поведение. meta_value_num
указывает WordPress интерпретировать значения meta_value
как числа, а не строки.

Я пробовал, это дает точно такой же результат. На самом деле, я даже проследил путь от функции к функции в WordPress-Core, и строка разбивается на & и преобразуется в массив перед обработкой....

Согласно Codex: Обратите внимание, что значение 99 будет считаться больше 100, так как данные хранятся в виде строк, а не чисел.
Таким образом, meta_value
действительно хранится как строка... поэтому WordPress добавляет кавычки. Следующий вопрос: можете ли вы описать, в чем именно "не работает"? Возвращает ли он вообще какие-либо данные?

Да, вы правы! Как я уже говорил выше, что бы я ни делал, значение оборачивается в '5'. MySQL не может сравнивать что-то на числовом уровне, если оно обернуто в '', поэтому мне интересно, почему WordPress реализовал эту функцию, если она не работает. Нет, он возвращает результат, но не тот, который ожидается.

Смотрите мое обновление выше. Добавление orderby=meta_value_num
должно решить проблему, если у вас WP 2.8.4 или новее.

Спасибо за все усилия. Но это тоже не сработало. Я действительно глубоко изучил эту тему, и это просто не поддерживается в WP. Я создам тикет по этой проблеме, потому что ее действительно легко исправить, и это откроет столько возможностей!

@Joakim Да, это поддерживается WP. Прочтите тикет, на который я ссылался выше, где была добавлена эта функция. Возможно, вы можете предоставить нам более крупный пример кода того, что вы пытаетесь сделать... возможно, проблема в другом.

WordPress обрабатывает все 'value' как строки и добавляет к ним одинарные кавычки в итоговом запросе, поэтому SQL также вынужден обрабатывать их как строки, а не числа. Вы можете убрать эти кавычки с помощью фильтра:
add_filter('get_meta_sql', function($data) {
$regex = "/'(-?\d+)'/";
$data['where'] = preg_replace($regex, "$1", $data['where']);
return $data;
});
И убедитесь, что у вас указан 'type' => 'NUMERIC'
или аналогичный тип
