Orderby meta_value возвращает только сообщения с существующим meta_key

10

У меня есть следующий wp_query:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_key',
    'order' => 'ASC',
    'meta_key'=>'custom_author_name',
    'post_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

echo = 10 результатов, потому что есть только 10 newsсообщений с meta_key = custom_author_name. Но есть сотни newsсообщений, у которых нет строки post_meta с этим конкретным meta_key. Обратите внимание, что мета-запрос не задействован. Значение meta_value не назначено, потому что я пытаюсь отсортировать сообщения только по meta_key, а не фильтровать по meta_value.

Не следует ли заказать, чтобы выбрать все сообщения? и просто заказать их?

Если так, то почему результат фильтруется? Если meta_key не найден, почему бы просто не использовать пустую строку или сопоставить все?

Если нет, то почему нет?

Если я введу meta_key для каждого новостного сообщения (даже если это пустая строка), я получу ожидаемый результат. Но это похоже на множество строк таблицы, которые не должны быть там.

gdaniel
источник

Ответы:

10

Как указано в ответе @ ambroseya, он должен работать так. После того, как вы объявите мета-запрос, даже если вы не ищете конкретное значение, он будет запрашивать только сообщения с объявленным мета-ключом. Если вы хотите включить все сообщения, сортируйте их по мета-ключу, используйте следующий код:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key'=>'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        array( 
            'key'=>'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'posts_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

Для этого используется расширенный мета-запрос, который ищет сообщения, для которых этот метаключ не объявлен. Так как тот, который EXISTSимеет первый, при сортировке meta_valueбудет использовать первый запрос.

Мэнни Флермонд
источник
3
Это не изменило порядок для меня вообще, когда я использовал 'orderby' => 'meta_value', это изменило порядок, но это не имело ничего общего с фактическим метаполем.
Джейк
3

Я попытался применить ответ @Manny Fleurmond и, как @Jake, я не мог заставить его работать даже после исправления опечатки, которая 'orderby' => 'meta_key'должна быть 'orderby' => 'meta_value'. (И для полноты это должно быть 'posts_per_page'не так, 'post_per_page'но это не влияет на рассматриваемый вопрос.)

Если вы посмотрите на SQL-запрос, фактически сгенерированный ответом @Manny Fleurmond (исправив опечатки), то вот что вы получите:

SELECT   wp_{prefix}_posts.* FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
GROUP BY wp_{prefix}_posts.ID ORDER BY wp_{prefix}_postmeta.meta_value ASC

Это иллюстрирует, как WP выполняет синтаксический анализ запросов: он создает таблицу для каждого предложения meta_query, а затем выясняет, как их объединить и как упорядочить. Порядок работал бы хорошо, если бы вы использовали только одно предложение с 'compare' => 'EXISTS', но при объединении второго 'compare' => 'NOT EXISTS'предложения с помощью OR (как мы должны) портит порядок. В результате LEFT JOIN используется для соединения как первого предложения / таблицы, так и второго предложения / таблицы - и то, как WP собирает все вместе, означает, что таблица, созданная с использованием, 'compare' => 'EXISTS'на самом деле заполняется meta_values ​​из ЛЮБОГО настраиваемого поля, а не только 'custom_author_name'интересующее нас поле. Поэтому я думаю, что упорядочение по этому предложению / таблице даст желаемые результаты только в том случае, если конкретный post_type для «news» имеет только одно настраиваемое поле.

Решение, которое сработало для моей ситуации, состояло в том, чтобы заказать другой пункт / таблицу - НЕ СУЩЕСТВУЕТ. Кажется, мне кажется это нелогичным, но из-за того, как WP анализирует переменные запросов, именно эта таблица meta_valueзаполняется только настраиваемым полем, к которому мы стремимся.

(Единственный способ, которым я понял это, - выполнить эквивалент этого запроса для моего случая:

SELECT   wp_{prefix}_posts.ID, wp_{prefix}_postmeta.meta_value, mt1.meta_value FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
ORDER BY wp_{prefix}_postmeta.meta_value ASC

Все, что я сделал, это изменил отображаемые столбцы и удалил предложение GROUP BY. Затем это показало мне, что происходит - что столбец postmeta.meta_value извлекал значения из всех meta_keys, в то время как столбец mt1.meta_value извлекал только meta_values ​​из настраиваемого поля новостей.)

Решение

Как говорит @Manny Fleurmond, это первое предложение, которое используется для orderby, поэтому ответ заключается в том, чтобы просто поменять местами предложения, получив следующее:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        ),
        array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);

В качестве альтернативы вы можете сделать предложения ассоциативными массивами и упорядочить по соответствующему ключу, например так:

$args = array(
    'post_type' => 'news',
    'orderby' => 'not_exists_clause',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        'exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        'not_exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);
jlad26
источник
Стоит отметить, что если мета-ключ custom_author_nameустановлен, а затем не установлен, он meta_keyбудет реагировать, EXISTSи в результате они будут всплывать рядом с сообщениями, которые имеют custom_author_name. В моем случае у меня есть флажок, поэтому я использую "value" => "1"вместо EXISTS, но для строк потребуется другой подход.
DJB
1

Вот на самом деле, как это работает.

Если вы хотите сделать это без добавления строк таблицы, вам придется выполнить два запроса. Один с meta_key, который имеет ограниченные результаты, и другой, который получает весь список; затем используйте PHP для сравнения результатов двух запросов (возможно, удалив результаты meta_key из другого запроса, чтобы удалить дубликаты, или что-либо еще, что имеет смысл в ваших настройках).

ambroseya
источник
0

К сожалению, это не так WP_Query. Как только вы добавите этот «мета» компонент, вы создали своего рода фильтр. Дамп, $query->requestи вы поймете, о чем я.

Во-вторых, WP_Queryне поддерживает упорядочение по мета- ключу вообще. Вы можете заказать мета- значение для определенного ключа, но не по самому ключу. Опять дамп запроса, чтобы понять, что я имею в виду. Вы заметите, что компоненты "порядка" выпадают, если вы попытаетесь.

На мой взгляд, самый простой способ заставить это работать - пара коротких фильтров:

function join_meta_wpse_188287($join) {
  remove_filter('posts_join','join_meta_wpse_188287');
  global $wpdb;
  return ' INNER JOIN '.$wpdb->postmeta.' ON ('.$wpdb->posts.'.ID = '.$wpdb->postmeta.'.post_id)';
}
add_filter('posts_join','join_meta_wpse_188287');

function orderby_meta_wpse_188287($orderby) {
  remove_filter('posts_orderby','orderby_meta_wpse_188287');
  global $wpdb;
  return $wpdb->postmeta.'.meta_key ASC';
}
add_filter('posts_orderby','orderby_meta_wpse_188287');

$args = array(
    'post_type' => 'news',
    'post_per_page'=>-1
);
$q = new WP_Query($args);
var_dump($q->request); // debug
var_dump(wp_list_pluck($q->posts,'post_title')); // debug
s_ha_dum
источник