按元值排序,但包括没有元值的帖子

时间:2013-06-09 作者:powerbuoy

我一直在使用pre_get_posts 过滤器,允许用户按不同字段对帖子(包括一组自定义帖子类型)进行排序。

但我遇到的问题是,当我告诉WP按元值排序时,它将排除所有未设置该元值的帖子。如果您将排序从“价格”更改为“日期”,这会导致结果数发生变化,因为“帖子”没有设置“价格”,但“项目”有设置。

这不是我想要的,所以我想知道是否有一种方法可以包含所有的帖子——即使是那些没有我正在排序的元值的帖子——并将没有值的帖子放在最后。

我知道如何在多个字段上排序,但这没有帮助。

谢谢

似乎我不是唯一一个有这个问题的人:Way to include posts both with & without certain meta_key in args for wp_query? 但没有解决办法。

Update

我试过这个答案,但不确定我是否理解正确,下面是我现在的答案:

<?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\');
}
元值是一个数字(顾名思义,它用于存储价格)

Update 2

我已经把订单内容注释掉了,现在我只有以下内容:

<?php
$qry->set(\'meta_query\', array(array(
    \'key\' => \'item_price\', 
    \'value\' => \'\', 
    \'compare\' => \'NOT EXISTS\'
)));
使用此代码,查询似乎会返回所有没有item_price 键,并且没有包含它的帖子。一、 E.问题现在颠倒过来了。

如果我也添加订单代码,则得到0个结果。

编辑:。。。三年后:P我又遇到了这个问题。我尝试了所有给出的答案,但没有任何效果。不知道为什么有些人似乎认为他们有效,但至少对我无效。

我最终得到的解决方案是使用save_post 筛选-确保所有帖子都有我想要排序的自定义字段。我不得不这么做,这有点烦人,但只要你早一点这样做,你可能就没有问题了。

在本例中,我在帖子上构建了一个“查看计数器”,希望用户能够对阅读量最大的帖子进行排序。同样,在按视图计数排序时,从未被查看过的帖子(我想这不太可能,但仍然)消失了。我添加了这段代码,以确保所有帖子都有一个视图计数:

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

14 个回复
最合适的回答,由SO网友:Steven Jones 整理而成

有两种可能的解决方案:

1。所有帖子都有meta

我在这里找到的最佳解决方案是为其余帖子/产品提供0的商品价格。您可以手动执行此操作,或者遍历所有帖子,如果价格为空,则更新它。

要使这在将来易于管理,您可以save_post 并在第一次添加时给它们一个值(只有当它为空时)。

2。多个查询可以在执行时运行第一个查询,并存储返回帖子的ID。然后,您可以对所有帖子和订单日期运行另一个查询,excluding ID从第一个查询返回。

然后,您可以按顺序分别打印这两个结果,您将得到所需的结果。

SO网友:noahmason

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 默认情况下,不会忽略没有该键值的帖子。

SO网友:binki

此方法将返回所有帖子,包括包含和不包含请求的帖子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\');
}
我通过摆弄这个问题的所有不同答案并分析the generated 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\\u值为的帖子item_price 还有那些失踪的人item_price. 所有具有item_price 将相对彼此正确排序,但posts missing item_price will use some random other meta value (比如,_edit_last 似乎是1 通常在我的数据库或其他一些完全任意的内部wordpress元数据中wp_postmeta.meta_valueORDER BY 条款因此,虽然此方法很接近,并且可能对某些数据有效,但它是无效的。所以,我只能说,如果你item_price 值恰好与MySQL为缺少的帖子选择的随机元字段不冲突item_price, 这可能对你很有用。如果你所需要的只是保证你的帖子item_price 没有考虑到其他帖子的顺序,可能就可以了。但我认为这只是wordpress的一个缺点。请纠正我,我希望我错了,有办法解决这个问题;-)。

看来INNER JOIN wp_postmeta, MySQL正在从多个postmetameta_key 给定帖子中缺少。从SQL的角度来看,我们需要弄清楚如何告诉wordpress输出ORDER BY mt1.meta_value. 此列正确NULL 当我们的要求meta_key 缺少,不像wp_postmeta.meta_value. 如果我们能做到这一点,SQL将对这些NULL (缺少条目)在任何其他值之前,为我们提供了一个定义良好的顺序:首先是缺少特定Posteta字段的所有帖子,其次是具有该字段的帖子。但这就是问题的症结所在:\'orderby\' => \'meta_value\' 只能参考\'meta_key\' => \'item_price\' 无负债者wp_postmeta 始终是INNER JOIN 而不是LEFT JOIN, 意思wp_postmeta.meta_valuewp_postmeta.meta_key 永远不会NULL.

所以我想我不得不说,这是不可能的wordpress的内置WP_Query 正如现在所记录的(在wordpress-3.9.1中)。打扰所以如果你真的需要它来正常工作,你可能need to hook into wordpress elsewhere and modify the generated SQL directly.

SO网友:Paul

这里的每个人都面临的问题与元查询的顺序有关。为了正确排序,您需要输入“NOT EXISTS”查询before “存在”查询。

原因是WordPress使用了“ORDER BY”子句中最后一条“LEFT JOIN”语句的meta\\u值。

例如:

$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,
]);

SO网友:Ilya Kogan

我还遇到了类似的问题,以下解决方案对我有所帮助:

$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 );
我在WordPress Codex上找到了一个标题为“的描述”\'orderby\' with multiple \'meta_key\'s“:https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parametersenter image description here

SO网友:Rafael M31

我想我有一个解决办法。

您可以使用两个meta_keys、 所有帖子都有一个(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\');

SO网友:rjpedrosa

如果合适,如果元数据不存在,则可以在每次保存或更新帖子时添加默认的元数据。

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\'); e、 g。add_action(\'save_post_product\', \'addDefaultMetaValue\');

SO网友:KittMedia

我自己也遇到了数字元值的问题,并指出查询的顺序也很重要。对我来说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

SO网友:mikemike

这是一个非常古老的问题,但没有一个答案足够可靠,我可以使用并对结果感到满意。下面的解决方案仍然使用WP_Query, 但是操纵join和order-by语句。

它使用了一个名为ORDER BY IF, 允许您在ORDER BY语句期间有效地运行逻辑。这一点很重要,因为这里的其他解决方案只是将结果集中在列表的前端或末尾,没有任何值,当您尝试按2键排序时,这是无用的。

在下面的例子中,我有一个“企业”的CPT。我希望他们都是按标题排序的,但任何有is_premium 应该放在第一位。我已经对代码进行了注释,以便您可以看到在哪里发生了什么。

<?php
$paged = ( get_query_var( \'paged\' ) ) ? get_query_var( \'paged\' ) : 1;
$args = [
    \'post_type\' => \'business\',
    \'posts_per_page\' => 12,
    \'meta_query\' => [
        \'relation\' => \'OR\',
        [
            \'key\' => \'is_premium\', 
            \'compare\' => \'NOT EXISTS\' // NOT EXISTS first is important
        ],
        [
            \'key\' => \'is_premium\', 
            \'compare\' => \'EXISTS\'
        ],
    ]
    ,
    \'orderby\' => [
        \'meta_value\' => \'DESC\', // This is the is_premium field
        \'title\' => \'ASC\',
    ],
    \'paged\' => $paged
];

// Add our own join to the query
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\'
        , \'is_premium\' );
    }

    return $join;
}
add_filter(\'posts_join\',\'custom_join\');

// Add our own order by to the query
function custom_orderby($orderby_statement){
    global $wpdb;

    if ( ! is_admin() ) {
        // Here we order by the meta_value ONLY is it\'s 1, otherwise ignore it for that order statement, which 
        // means it\'ll be ordered by the next statement (title), the same as the other results
        $orderby_statement = "IF(cpm.meta_value = 1, 1, 0) DESC, wp_posts.post_title ASC ";
    }

    return $orderby_statement;
}
add_filter(\'posts_orderby\',\'custom_orderby\', 10, 2 ); 

// Query
$business = new WP_Query( $args );

// Remove the filters
remove_filter(\'posts_orderby\',\'custom_orderby\');
remove_filter(\'posts_join\',\'custom_join\');

SO网友:kaiser

有一种可能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 价值观否则,您无法与不存在的值进行比较,这应该可以查询这两种帖子。这是一种黑客行为,但只要它起作用。。。

SO网友:Jon

我认为@kaiser试图做的是告诉查询返回所有具有该元键的帖子,方法是对not 过滤所有这些帖子。所以如果你知道all 自定义字段可以采用的值是x、y、z。您可以说“WHERE meta\\u key IN(x、y、z)”,但您可以通过说“WHERE meta\\u 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\' => \'!=\',
    ) )
) );
也未经测试,但感觉值得一试:-)。

SO网友:Scruffy Paws

最后,我用了一点技巧(IMHO)解决了这个问题,但在我的情况下,它帮了我的忙。

你可以挂上过滤器posts_join_pagedposts_orderby 更新联接和顺序字符串。这将允许您按任何您想要的顺序排序,只要您首先加入它,而不是假设该字段必须存在于该特定帖子中的WP\\u查询。然后可以删除meta_key, orderby, 和“来自WP\\U查询参数的订单”。

下面是一个示例。在每个函数的顶部,我必须在某些情况下退出,因为它会将此添加到使用WP\\u查询的所有内容中。您可能需要修改它以满足您的特殊需要。

遗憾的是,缺少关于这两个过滤器的文档,所以。。。祝你好运!:)

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;
}

SO网友:jpussacq

此解决方案对我有效:

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 meta\\u值的记录。另一种解决方案在末尾显示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 ); 

SO网友:Jacob Raccuia

我可以通过使用一个查询和适当的参数来解决这个问题。WP 4.1+

$args = array( 
    \'posts_per_page\' => -1, 
    \'post_status\' => \'publish\',
    \'meta_query\' => array(
        \'relation\' => \'OR\',
        array(
            \'key\' => \'custom_sort\',
            \'compare\' => \'EXISTS\'
        ),
        array(
            \'key\' => \'custom_sort\',
            \'compare\' => \'NOT EXISTS\'
        )
    ),
    \'orderby\' => \'meta_value_num title\',
    \'order\' => \'ASC\',
));

$query = new WP_Query($args);
请参见我的完整答案:https://wordpress.stackexchange.com/a/370841/15209

结束

相关推荐

使用Pre Get帖子添加多个Orderby

我已经在使用一个小代码来设置默认的排序方式,一个名为“Year”的自定义字段。我需要添加另一个排序方式,以便在相同的排序中也按“Post Title”排序。Sort = 年份自定义字段(DESC)和职位名称(ASC)下面是我当前用于按年份DESC排序的代码。add_filter(\'pre_get_posts\', \'sort_arc\'); function sort_arc($q) { if ($q->is_category) { $q->set(\'or