メタ値で並べ替えるが、メタ値を持たない投稿を含める


37

pre_get_postsフィルターを使用して組み込みのWP検索を変更し、ユーザーがさまざまなフィールドで投稿(カスタム投稿タイプの束を含む)をソートできるようにしました。

私が抱えている問題は、メタ値でソートするようにWPに指示すると、そのメタ値が設定されていないすべての投稿が除外されることです。これにより、「投稿」には「価格」が設定されていないが「アイテム」には設定されているため、ソートを「価格」から「日付」に変更すると、結果の数が変更されます。

これは私が望むものではないので、すべての投稿を含める方法があるかどうかを知りたいです-私がソートしているメタ値を欠いているものも含めて-そして値のないものを最後に置きます。

複数のフィールドでソートする方法は知っていますが、それは役に立ちません。

ありがとう

私がこの質問を持っているのは私だけではないようです:wp_queryの引数に特定のmeta_keyのある投稿とない投稿の両方を含める方法?しかし、解決策はありません。

更新

私は答えを試してみましたが、私が正しく理解したかどうかはわかりません。ここに私が今持っているものがあります:

<?php
function my_stuff ($qry) {
    $qry->set('meta_query', array(array(
        'key' => 'item_price', 
        'value' => '', 
        'compare' => 'NOT EXISTS'
    )));

    $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

メタ値は数値です(名前が示すように価格を保存するために使用されます)

更新2

私は注文品をコメントアウトしました、そして、私が今持っているすべてはこれです:

<?php
$qry->set('meta_query', array(array(
    'key' => 'item_price', 
    'value' => '', 
    'compare' => 'NOT EXISTS'
)));

このコードでは、クエリはitem_priceキーを持たないすべての投稿を返し、キーを含む投稿はどれも返さないようです。IEの問題は今では逆転しています。

注文コードも追加すると、結果は0になります。

編集:... 3年後...:PIは再びこの問題を抱えていました。私は与えられたすべての答えを試しましたが、どれもうまくいきませんでした。なぜ一部の人々は自分が機能していると思うように見えるのかわかりませんが、少なくとも私には機能しません。

解決策はsave_postフィルターを使用することです。すべての投稿に、並べ替えたいカスタムフィールドがあることを確認します。私はそれをやらなければならないのは少し面倒ですが、早い段階でやれば問題はないでしょう。

この場合、私は投稿に「ビューカウンター」を構築しており、ユーザーが最も読んだ投稿でソートできるようにしたかったのです。繰り返しになりますが、ビューカウントで並べ替えると、一度も表示されたことのない投稿(かなり可能性は低いとは思いますが、それでも)は消えました。すべての投稿の表示回数を確認するために、次のコードを追加しました。

add_action('save_post', function ($postId) {
    add_post_meta($postId, '_sleek_view_count', 0, true);
});

コードを教えてください。回答しやすくします。
カイザー

まず:meta_querytax_queryされ、常にarray( array() )彼らは複数のアレイを組み合わせて。第二-私の答えで述べたように-あなたmeta_value_numは数字に使用する必要があります。実際に定義する必要があるかもしれませんmeta_value_numWP_Query-Codexページエントリを参照)。最後には、それはしても意味がありませんorderASC して DESC方向。それは可能ではありません。スペース区切り文字は機能するだけでorderby、最初ASCと2番目をソートするように指示することはできませんDESC。それがposts_clausesフィルターの目的です。
カイザー

また、meta_value_numエントリが実数であることを確認してください。誰かがそれが数字であると述べるが、実際にはそれを文字列としてデータベースに保存することを頻繁に見ました。
カイザー

あなたの助けをありがとう、私はこれを試して、あなたに戻ります。理由は、ASC DESCそれが上でソートするほどでmeta_valueASCdateDESC、私はそれが動作伝えることができる限り、。
パワーブイ

1
@Howdy_McGeeそれは正しいです。私のカスタム投稿タイプには、この値が設定されているものがあります。しない人もいます。また、組み込みの投稿タイプ(POSTやPAGEなど)はサポートしていません。そのため、そのカスタムフィールドでソートしようとすると、必ずカスタムフィールドのWITH投稿のみが表示されます。
powerbuoy 14

回答:


4

これには2つの解決策があります。

1.すべての投稿にメタがあります

ここで見つけた最良の解決策は、残りの投稿/製品にアイテム価格0を与えることです。これを手動で行うか、すべての投稿をループして価格が空の場合は更新します。

これを将来管理しやすくするために、save_post最初に追加されたときにフックして値を与えることができます(空の場合のみ)。

2.複数のクエリ

実行中に最初のクエリを実行し、返された投稿のIDを保存できます。次に、最初のクエリから返されたID を除き、すべての投稿と注文日に対して別のクエリを実行できます。

その後、2つの結果を別々に印刷して注文すると、目的の結果が得られます。


1
3年後、同じ問題が再びありました:P save_postメソッドを使用しなければなりませんでした(使用したコードで質問を更新しました)。
-powerbuoy

10

Easy Peasy、2018年にテスト済み、現在実稼働で使用中。

$query->set( 'meta_query', array(
    'relation' => 'OR',
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'EXISTS'
    ),
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'NOT EXISTS'
    )
) );
$query->set( 'orderby', 'meta_value title' ); 

これにより、値が指定されていないメタキーの有無にかかわらず、すべてのアイテムがチェックされます。メタクエリは、orderbyのキーを確実に提供します。テスト済みです。ただし、メタクエリが複数のキーを使用する場合にどのように機能するかはわかりません。

実用例

/**
 * Modifies query before retrieving posts. Sets the 
 * `meta_query` and `orderby` param when no `orderby` 
 * param is set, (default ordering).
 * 
 * @param   WP_Query  $query  The full `WP_Query` object.
 * @return  void
 */
function example_post_ordering( $query ) {

    // if not in wp-admin, 
    // and the query is the main query, 
    // and the query is not a singular query, 
    // and the query does not have an orderby param set...
    // Note: check for post types, etc. here as desired.
    if ( ! is_admin() 
    && $query->is_main_query() 
    && ! $query->is_singular() 
    && empty( $query->get( 'orderby' ) ) ) {

        // Setting just `meta_key` is not sufficient, as this 
        // will ignore posts that do not yet, or never will have 
        // a value for the specified key. This meta query will 
        // register the `meta_key` for ordering, but will not 
        // ignore those posts without a value for this key.
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'EXISTS'
            ),
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'NOT EXISTS'
            )
        ) );

        // Order by the meta value, then by the title if multiple 
        // posts share the same value for the provided meta key.
        // Use `meta_value_num` if the meta values are numeric.
        $query->set( 'orderby', 'meta_value title' );
    }

}

add_action( 'pre_get_posts', 'example_post_ordering', 10 );

これはcustom_meta_keyデフォルトで投稿を順序付けし、そのキーの値のない投稿を無視しません。


コードを読んだだけで、やっているように見えるのは、 custom_meta_keyし、whickポストを取得していない持っているがcustom_meta_key。並べ替えの実際の作業例を自由に含めてください。
powerbuoy

1
あなたは正しいです、それがすべてです、しかし、下の行はmeta_value(クエリされているメタキーの)による順序付けに責任があります。$query->set( 'orderby', 'meta_value title' );(複数の投稿がメタキーに同じ値を持っている場合は、メタ値で並べ替え、次にタイトルで並べ替えます)。これはpre_get_posts、渡された$query変数を使用して、フックで実行する必要があります。質問された質問は、そのメタキーの値を持たない投稿を無視せずに、メタ値で並べ替える方法でした。
ノアマソン

@powerbuoy更新された実用例を参照してください
noahmason

次回はこの問題に直面したときに、試してみます。
パワーブイ

1
私のためにカスタムget_posts()コールで働き、_featuredメタ付きの投稿をトップに押し出し、その後日付順に並べました。ありがとう!
natebeaty

8

このメソッドは、requestedのあるものとないものを含むすべての投稿を返しますが、meta_key注文すると奇妙なことをします。

add_action('pre_get_posts', 'my_stuff');
function my_stuff ($qry) {
    $qry->set(
        'meta_query',
        array(
            'relation' => 'OR', # Matches to this meta_query should be added to those matching the 'meta_key' query
            array(
                'key' => 'item_price', 
                'value' => 'bug #23268', 
                'compare' => 'NOT EXISTS'
            )
        )
    );

    $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

この質問に対するさまざまな答えをいじって分析することでこれを見つけました 、試行錯誤を通して生成されたSQLを。メタデータが欠落している投稿を含めるにはarray('meta_query' => array('relation' => 'OR'))、出力を設定LEFT JOINするのINNER JOINではなく、適切に設定する必要があるようです。を指定NOT EXISTSすると、WHERE句がメタフィールドのない投稿を除外できなくなります。この特定のWP_Query場合、生成されるSQLは次のとおりです(インデント/改行が追加されます)。

SELECT SQL_CALC_FOUND_ROWS
    wp_posts.ID
    FROM wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
    LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'item_price')
    WHERE 1=1
    AND ( wp_term_relationships.term_taxonomy_id IN (2) )
    AND wp_posts.post_type = 'post'
    AND (wp_posts.post_status = 'publish'
        OR wp_posts.post_status = 'private')
    AND (wp_postmeta.meta_key = 'item_price'
        -- Oh look, here we give SQL permission to choose a random
        -- row from wp_postmeta when this particular post is missing
        -- 'item_price':
        OR  mt1.post_id IS NULL )
    GROUP BY wp_posts.ID
    ORDER BY wp_postmeta.meta_value,wp_posts.post_date DESC
    LIMIT 0, 10

結果はのmeta_valueを持つすべての記事の一覧ですitem_priceし、不足しているものをitem_price。のすべての投稿はitem_price相互に正しく並べられますが、ポストが不足しているitem_priceいくつかのランダムに使用するのメタ値(たとえば、_edit_lastのようです1そのために私のデータベースまたは完全に任意であるいくつかの他の内部ワードプレスのメタデータにかなり頻繁に)wp_postmeta.meta_valueORDER BY句。そのため、このメソッドは近く、特定のデータで機能するように見える場合がありますが、壊れています。したがって、私が言えることは、あなたのitem_price値が、MySQLが欠落している投稿に対して選択するランダムメタフィールドと競合しない場合item_price、これはあなたのためにうまくいくかもしれません。必要なのは、あなたの投稿がitem_price他の投稿の順序に関係なく、互いに対して正しく順序付けられている場合は、問題ない可能性があります。しかし、これはワードプレスの欠点に過ぎないと思います。私を修正してください、私が間違っていることを望み、これに対処する方法があります;-)。

INNER JOIN wp_postmeta場合、MySQLは、特定の投稿にが欠落しているpostmetaときに、その投稿に関連付けられた複数の行からランダムな行を選択しているようですmeta_key。SQLの観点から、wordpressにoutputを指示する方法を理解する必要がありますORDER BY mt1.meta_valueNULLリクエストmeta_keyとは異なり、この列は適切wp_postmeta.meta_valueです。それが可能な場合、SQLはこれらのNULL(不足しているエントリ)を他の値の前にソートし、明確に定義された順序を与えます:最初に特定のpostmetaフィールドがないすべての投稿、次にフィールドを持つ投稿が来ます。しかし、それが問題全体です。'orderby' => 'meta_value'参照できるのは'meta_key' => 'item_price' 、エイリアスさいないものwp_postmetaは常にの意味ではINNER JOINなく、LEFT JOINwp_postmeta.meta_valuewp_postmeta.meta_key決してなりませんNULL

だから、これはwordpressに組み込まれWP_Queryている(wordpress-3.9.1で)ようになったため、これは不可能だと言わざるを得ないでしょう。わざわざ。したがって、これが正しく機能するために実際に必要な場合、おそらく他の場所でwordpressにフックし、生成されたSQLを直接変更する必要があります


とても有望に見えます!次回この問題が発生したときにこれを試してみます。すぐにお答えしたいと思いますが、最初にそれが機能することを確認したいと思います。
powerbuoy 14

これにより、すべてが表示されなくなりました。これを実装しても何も表示されません。
ジェイク

@Jake Yeaここも同じ。今日もこの問題を抱えていて、これを試しました。0件の結果を返します。
-powerbuoy

ワードプレスのどのバージョンを使用していますか?この投稿では、wordpressでサポートされていない、ドキュメント化されていない内部APIの使用方法について説明しているため、おそらくwordpress-3.9.1を使用している場合、またはそれ以上のバージョンがない場合にのみ機能すると考えています。
ビンキ

2

私には解決策があると思います。

2つmeta_keyのs を使用できます。1 つはすべての投稿(like "_thumbnail_id")にあり、meta_keyあなたは、フィルタとして使用することを望みます。

あなたの引数:

$qry->set(
    'meta_query',
    array(
        'relation' => 'OR',
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        ),
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        )
    )
);

$qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');

1
ここでの問題は、空の文字列の比較です。それを削除すると、'value' => '', 2番目の比較も機能するはずですNOT EXISTS。最後のセット命令は不要です
-nodws

2

ここの全員が抱えている問題は、メタクエリの順序に関係しています。正しく並べ替えるには、「EXISTS」クエリの前に「NOT EXISTS」クエリを配置する必要があります。

これは、WordPressが「ORDER BY」句の最後の「LEFT JOIN」ステートメントのmeta_valueを使用するためです。

例えば:

$pageQuery = new WP_Query([
    'meta_query' => [
        'relation' => 'OR',
        ['key' => 'item_price', 'compare' => 'NOT EXISTS'], // this comes first!
        ['key' => 'item_price', 'compare' => 'EXISTS'],
    ],
    'order' => 'DESC',
    'orderby' => 'meta_value_num',
    'post_status' => 'publish',
    'post_type' => 'page',
    'posts_per_page' => 10,
]);

1

メタ値が存在しない場合、適切であれば、投稿が保存または更新されるたびにデフォルトのメタ値を追加できます。

function addDefaultMetaValue($post_id) {
    add_post_meta($post_id, 'item_price', 0, true);
}
add_action('save_post', 'addDefaultMetaValue');

カスタム投稿タイプを使用している場合はadd_action('save_post', 'addDefaultMetaValue');add_action('save_post_{post_type}', 'addDefaultMetaValue');たとえばadd_action('save_post_product', 'addDefaultMetaValue');


1

私は数値メタ値について自分で問題を抱えており、クエリの順序も重要であることを指摘しました。私にとってNOT EXISTSクエリは最初のものでなければなり。

例:

$query->set( 'orderby', 'meta_value_num' );
$query->set( 'meta_query', [
    'relation' => 'OR',
    [ 'key' => 'your_meta_name', 'compare' => 'NOT EXISTS' ],
    [
        'key' => 'your_meta_name',
        'compare' => 'EXISTS',
    ],
] );

また、数値の正しい方向を取得するために重要なのは、一般的’orderby’にに設定されること’meta_value_num’です。そうしないと、数値に対して奇妙な結果が得られます。例:

1、2、20、21、3、4、5…

の代わりに:

1、2、3、4、5…20、21


1

また、同様の問題が発生したため、次の解決策が役立ちました。

$args = array(
'post_type' => 'kosh_products',
'posts_per_page' => -1,
'meta_query' => array(
    'relation' => 'OR',
    'category_sort_order' => array(
        'key' => '_sort_order',
        'compare' => 'EXISTS'
    ),
    'category_sort_order_not_exists' => array(
        'key' => '_sort_order',
        'compare' => 'NOT EXISTS'
    ), 
),
'orderby' => array( 
    'category_sort_order' => 'ASC',
    'date' => 'ASC'
));
$query = new WP_Query( $args );

複数の「meta_key」を含む「orderby」というタイトルのWordPress Codexの説明を見つけました:https : //codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters ここに画像の説明を入力してください


0

そのための可能なorderby値がありmeta_valueます。

$query = new WP_Query( array ( 
    'meta_key'   => 'your_keys_name',
    'orderby'    => 'meta_value',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'your_meta_key',
         'value'   => '',
         'compare' => 'NOT EXISTS',
         // 'type'    => 'CHAR',
    ) )
) );

数値を取得した場合は、meta_value_num代わりに使用します。

免責事項:これはテストされていませんが、動作するはずです。ポイントはmeta_keykey値と値を指定する必要があるということです。そうでない場合は、存在しない値と比較することはできません。これにより、両方の種類の投稿を照会できるようになります。それはある種のハックっぽいですが、それが機能する限り...


ご回答いただきありがとうございます。更新された質問をご確認ください。正しく理解できたかどうかわかりません。
powerbuoy

私はまだこの仕事をしていませんので、解決策があれば、私が間違っていることを知りたいと思います。あなたがそれを主張したい場合にも、私はSOの懸賞金を設定します。stackoverflow.com/questions/17016770/...
powerbuoy

1
二つのこと。'your_keys_name'そして、'your_meta_key'両方とも別個ではなく同じ文字列でなければなりません。そうでなければ、質問を誤解したように見えます。次に、ローカル設定でこれをテストし、キーが存在する投稿を(を介してmeta_query)除外し、キーが欠落している投稿を(を介して)除外して投稿meta_keyを表示しないようにします。しかし、この答えは、少なくとも;-) という言葉に向けた一歩です。
ビンキ14

1
おもしろいことに、この答えはに追加'relation' => 'OR'するだけで機能しmeta_queryます。風変わりなものo_o。
ビンキ14

@binki 質問を編集して、変更する必要があると思われる部分を変更するだけです。これはコミュニティ主導のサイトです:)
kaiser 14

0

私は@kaiserがやろうとしていたものがダミーの一種適用することによって、そのメタキーを持つすべての投稿を返すために、クエリを伝えることだったと思う条件がするではない、これらのポストのいずれかをフィルタリングします。したがって、カスタムフィールドが取ることができるすべての値がx、y、zであることがわかっいる場合、「WHERE meta_key IN(x、y、z)」と言うことができますが、!=( ' ')

$query = new WP_Query( array ( 
    'orderby'    => 'meta_value_num',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'item_price',
         'value'   => '',
         'compare' => '!=',
    ) )
) );

また、テストされていませんが、試してみる価値があると感じています:-)。


1
現時点ではこれを実際にテストすることはできませんが、item_priceが設定されている投稿のみを返し、それが ''ではないことを確認してください。
パワーブイ

0

私はこれを少しハック(IMHO)で回避しましたが、私の場合はそれでうまくいきました。

フィルタposts_join_pagedおよびposts_orderbyにフックして、結合文字列と順序文字列を更新できます。これにより、WP_Queryではなく、その特定の投稿にフィールドが存在する必要があると想定するのではなく、最初に参加する限り、好きな順に並べ替えることができます。その後、削除することができmeta_keyorderbyあなたのWP_Queryの引数から、そして`順序を。

以下に例を示します。各関数の先頭で、特定のケースではエスケープする必要がありました。WP_Queryを使用するすべてにこれを追加するからです。特定のニーズに合わせて変更する必要がある場合があります。

残念ながら、これら2つのフィルターに関するドキュメントは不足しているので...幸運を祈ります!:)

add_filter('posts_join_paged', 'edit_join', 999, 2);
add_filter('posts_orderby', 'edit_orderby', 999, 2);

/**
 * Edit join
 *
 * @param string $join_paged_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_join($join_paged_statement, $wp_query)
{
    global $wpdb;
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $join_paged_statement;
    }

    $join_to_add = "
        LEFT JOIN {$wpdb->prefix}postmeta AS my_custom_meta_key
            ON ({$wpdb->prefix}posts.ID = my_custom_meta_key.post_id
                AND my_custom_meta_key.meta_key = 'my_custom_meta_key')
    ";

    // Only add if it's not already in there
    if (strpos($join_paged_statement, $join_to_add) === false) {
        $join_paged_statement = $join_paged_statement . $join_to_add;
    }

    return $join_paged_statement;
}

/** 
 * Edit orderby
 *
 * @param string $orderby_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_orderby($orderby_statement, $wp_query)
{
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $orderby_statement;
    }

    $orderby_statement = "my_custom_meta_key.meta_value DESC";

    return $orderby_statement;
}

コードは動作します。ただし、me​​ta_valueは文字列として処理されます。それで、6は50と高く評価されます。それらを数値として扱うために可能な変更はありますか?
-Drivingralle

@Drivingralleはcast(my_custom_meta_key.meta_value as unsigned) DESC...トリックを行う必要があります
tfrommen

1
ありがとう@tfrommen。$orderby_statement = "cast(my_custom_meta_key.meta_value as unsigned) DESC";よく働く。
Drivingralle

0

このソリューションは私のために働いた:

add_action( 'pre_get_posts', 'orden_portfolio' );
function orden_portfolio( $query ) {

    if( ! is_admin() ) {

        $query->set( 'orderby', 'meta_value_num' );
        $query->set( 'order', 'ASC' );
        $query->set( 'meta_query', [
            'relation' => 'OR',
            [ 
                'key' => 'ce_orden', 
                'compare' => 'NOT EXISTS' ],
            [
                'key' => 'ce_orden',
                'compare' => 'EXISTS',
            ],
        ] );

        return $query;

    }

}

ただし、このソリューションでは、最初にメタ値がnullのレコードが表示されます。この他のソリューションは、ASCの順序と最後のヌルを示しています。

function custom_join($join) {
    global $wpdb;

    if( ! is_admin() ) {
        $join .= $wpdb->prepare(
        ' LEFT JOIN ' . $wpdb->postmeta . ' cpm ON cpm.post_id = ' . $wpdb->posts . '.ID AND cpm.meta_key = %s'
        , 'ce_orden' );
    }

    return $join;
}

add_filter('posts_join','custom_join');

function custom_orderby($orderby_statement){
    global $wpdb;

    if ( ! is_admin() ) {
        $orderby_statement = "CAST( COALESCE(cpm.meta_value,99999) as SIGNED INTEGER) ASC";
    }

    return $orderby_statement;
}

add_filter('posts_orderby','custom_orderby', 10, 2 ); 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.