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 newsとなるのは、meta_key = custom_author_name。しかしnews、その特定のmeta_keyを含むpost_meta行を持たない数百の投稿があります。meta_queryは含まれていないことに注意してください。meta_valueでフィルターするのではなく、meta_keyで投稿をソートしようとしているだけなので、meta_valueは割り当てられていません。

すべての投稿をorderbyで選択するべきではありませんか?注文するだけですか?

もしそうなら、なぜ結果はフィルタリングされますか?meta_keyが見つからない場合は、空の文字列を使用するか、すべてに一致しないのはなぜですか?

そうでない場合、なぜでしょうか?

すべてのニュース投稿にmeta_keyを入力すると(空の文字列であっても)、期待どおりの結果が得られます。しかし、それはそこにある必要のないテーブル行の多くのように思えます。

回答:


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;

これが行うことは、そのメタキーが宣言されている、または宣言されていない投稿を検索する高度なメタクエリを使用することです。with EXISTSが最初なので、で並べ替えるとmeta_value、最初のクエリが使用されます。


3
これは私にとっては順序をまったく変更しませんでした。私がを使用した場合'orderby' => 'meta_value'、順序は変更されましたが、実際のメタフィールドとは何の関係もありませんでした。
ジェイク、

3

@Manny Fleurmondの回答を適用してみましたが、@ Jakeのように、本来ある'orderby' => 'meta_key'べきタイプミスを修正した後でも機能しません'orderby' => 'meta_value'でした。(そして完全にするために、それはそうであるべきではあり'posts_per_page'ません'post_per_page'が、それは見られている問題に影響しません。)

@Manny Fleurmondの回答(タイプミスを修正した)によって実際に生成されたSQLクエリを見ると、次のようになります。

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'、2番目の'compare' => 'NOT EXISTS'句をORで結合すると(必要に応じて)、順序が混乱します。結果は、LEFT JOINを使用して最初の句/テーブルと2番目の句/テーブルの両方を結合することです。WPがすべてをまとめる方法は、使用して作成されたテーブルに、'compare' => 'EXISTS'実際には任意のカスタムフィールドのmeta_values 'custom_author_name'興味のあるフィールドです。そのため、その句/テーブルによる順序付けは、「news」の特定のpost_typeに1つのカスタムフィールドしかない場合にのみ、望ましい結果が得られると思います。

私の状況で機能した解決策は、他の句/テーブルで注文することでした-NOT EXISTS 1。一見わかりにくいようですが、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);

メタキーcustom_author_nameが設定されてから設定が解除された場合、それmeta_keyに応答しEXISTS、を含む投稿と一緒にそれらがバブルアップされるという効果があることを指摘する価値がありcustom_author_nameます。私の場合、チェックボックスがあるのでの"value" => "1"代わりに使用してEXISTSいますが、文字列には別のアプローチが必要になります。
djb

1

これが実際に機能する方法です。

テーブルの行を追加せずにそれを行うには、2つのクエリを実行する必要があります。1つは限られた結果を持つmeta_keyを持ち、もう1つはリスト全体を取得します。次に、PHPを使用して2つのクエリ結果を比較します(おそらく、他のクエリからmeta_keyの結果を削除して重複を削除するか、設定で意味のあるものを削除します)。


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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.