我一直在使用pre_get_posts过滤器修改内置的WP搜索,使用户可以按不同字段对帖子(包括一堆自定义帖子类型)进行排序。

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

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

我知道如何对多个字段进行排序,但这无济于事。是否在wp_query的args中带有&没有某些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

I' ve注释掉了订单材料,现在我只剩下这个:

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


使用此代码,查询似乎返回所有没有item_price键的帖子而且没有任何帖子。即问题现在已经解决。

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

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

我最终解决的方法是使用save_post过滤器-确保所有帖子都具有自定义字段希望继续。我必须这样做有点烦人,但只要您尽早这样做,您就可能没有问题。

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

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


评论

请向我们显示您的代码。使其更易于回答。

首先:meta_query和tax_query始终是一个array(array()),因为它们组合了多个数组。第二-正如我的答案中提到的-您需要使用meta_value_num作为数字。可能还需要实际定义meta_value_num(请参阅WP_Query-Codex页面条目)。最后,按ASC和DESC方向排序没有任何意义。那不可能空格分隔符仅适用于orderby,您不能告诉它对第一个ASC和第二个DESC进行排序。这就是posts_clauses过滤器的作用。

并确保您的meta_value_num条目是实数。经常看到有人指出这是一个数字,但实际上将其另存为字符串在数据库中。

感谢您的帮助,我会尽力帮助您。 ASC DESC的原因是,据我所知,它可以对ASC中的meta_value和DESC中的日期进行排序。

@Howdy_McGee是正确的。我的某些自定义帖子类型已设置了此值。有些没有。而内置的帖子类型(例如POST和PAGE)则没有。因此,每当我尝试对自定义字段进行排序时,只会显示带有自定义字段的帖子。

#1 楼

有两种可能的解决方案:

1。所有帖子都有meta

我在这里找到的最佳解决方案是给其余帖子/产品商品价格为0。您可以手动执行此操作,也可以循环浏览所有帖子,如果价格为空,然后对其进行更新。

要使其在将来易于管理,您可以加入save_post,并在首次添加时为它们提供一个值(仅当其为空白时)。

2。多个查询

您可以在执行操作时运行第一个查询,并存储返回的帖子的ID。然后,您可以针对所有帖子和订购日期运行另一个查询,但不包括从第一个查询返回的ID。

然后可以分别打印出两个结果,您将获得所需的结果。

评论


三年后,我又遇到了同样的问题:P必须使用save_post方法(我已经用所使用的代码更新了我的问题)。

–动力浮标
17年7月7日在9:11

无需使用钩子或运行多个查询,这是可能的。看我的答案。

–雅各布·拉库亚(Jacob Raccuia)
20年7月11日在4:51

#2 楼

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' ); 


此命令将检查带有和不带有meta键且未指定值的所有项。元查询可靠地为订单提供了密钥。已经过测试。但是,我不确定元查询使用多个键时它将如何工作。

实际示例

/**
 * 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的帖子,以及获取没有custom_meta_key的帖子。随意包含排序的实际工作示例。

–动力浮标
18年4月18日在17:33

没错,这就是它的全部工作,但是以下行负责按meta_value(要查询的meta键的)排序。 $ query-> set('orderby','meta_value title'); (当多个帖子的met​​a键值相同时,按元值排序,然后按标题排序)。应该使用传入的$ query变量在pre_get_posts挂钩中完成此操作。请记住,询问的问题是如何按元值排序,而不忽略没有该元键值的帖子。

– noahmason
18年4月18日在18:18



@powerbuoy参见更新的实际示例

– noahmason
18年4月18日在18:55

好吧,下次遇到这个问题时,我会尝试一下。

–动力浮标
18年4月27日在18:13

在一个自定义get_posts()调用中为我工作,将带有_featured meta的帖子推到顶部,然后按日期排序。谢谢!

–natebeaty
18年9月9日在18:03

#3 楼

此方法将返回所有帖子,包括带有和不带有meta_key的帖子,但在订购时会做一些奇怪的事情。该问题的所有不同答案,并通过反复试验来分析生成的SQL。似乎设置array('meta_query' => array('relation' => 'OR'))会输出一个适当的LEFT JOIN而不是INNER JOIN来包含缺少元数据的帖子。指定NOT EXISTS可以防止WHERE子句过滤掉缺少meta字段的帖子。对于这个特定的WP_Query,生成的SQL是(添加了缩进/换行符):

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


结果是列出所有meta_value为item_price的帖子以及那些缺少item_price的帖子。所有带有item_price的帖子将相对于彼此正确排序,但是缺少item_price的帖子将使用一些随机的其他元值(例如,_edit_last在我的数据库中或其他内部完全免费的内部wordpress元数据中看起来经常是1 )为其在wp_postmeta.meta_value子句中的ORDER BY。因此,尽管此方法很接近并且可能适用于某些数据,但它还是被破坏了。所以,我只能说,如果您的item_price值恰好与MySQL为缺少item_price的帖子选择的随机元字段冲突,那么这可能对您来说很好。如果您只需要保证item_price的帖子相对于彼此正确地排序,而不考虑其他帖子的排序,那就可以了。但是我认为这只是wordpress的一个缺点。请纠正我,我希望我错了,并且有一种方法可以解决这个问题;-)。

似乎对于INNER JOIN wp_postmeta,当给定帖子中缺少postmeta时,MySQL正在从与该帖子关联的多个meta_key行中选择一个随机行。从SQL的角度来看,我们需要弄清楚如何告诉wordpress输出ORDER BY mt1.meta_value。与NULL不同,当缺少我们要求的meta_key时,此列正确为wp_postmeta.meta_value。如果可以的话,SQL会在其他任何值之前对这些NULL(缺少条目)进行排序,从而为我们提供了明确定义的顺序:首先是所有缺少特定postmeta字段的帖子,其次是具有该字段的帖子。但这就是整个问题:'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中)。烦。因此,如果您实际上需要此功能才能正常工作,则可能需要挂接到其他地方的wordpress中并直接修改生成的SQL。

评论


看起来很有前途!下次遇到此问题时,我将尝试此操作。我想立即给您答案,但希望先确认它对我有用。

–动力浮标
14年8月5日在16:09

这使所有内容都无法显示给我。执行此操作后未显示任何内容。

–杰克
16-10-26在11:32

@杰克耶在这里一样。今天又遇到了这个问题,并尝试了这个问题。返回0个结果。

–动力浮标
2015年6月7日9:00

你们正在使用哪个版本的wordpress?我认为这篇文章介绍了如何使用Wordpress不支持的内部未公开API,因此仅当您使用的是wordpress-3.9.1或没有太多版本时才可能有效。

– Binki
17年7月7日在14:40

#4 楼

我想我有一个解决方案。

您可以使用两个meta_key,其中一个所有帖子都有(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');


评论


这里的问题是空字符串比较,将其删除,它可以执行'value'=>'',第二次比较应该不存在,并且不需要最后一条设置指令

–点头
18年4月24日在15:19

#5 楼

这里每个人都必须解决的问题与元查询的顺序有关。为了正确排序,您需要将“ NOT EXISTS”查询放在“ EXISTS”查询之前。

原因是因为WordPress使用了最后一个“ LEFT JOIN”语句的meta_value在“ ORDER BY”子句中。

例如:

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


#6 楼

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

$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'且带有多个'meta_key's'的描述:https ://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters


#7 楼

如果合适,您可以在每次保存或更新帖子时添加默认元值(如果该元值不存在)。

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');

#8 楼

我自己遇到了数字元值的问题,并指出查询的顺序也很重要。对我来说,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’。否则,对于数字值,您会有奇怪的结果。 g .:

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

代替:

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

#9 楼



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


如果有数值,则改用orderby

免责声明:这是未经测试,但应该可以。关键是您需要指定meta_valuemeta_value_num值。否则,您将无法与不存在的值进行比较,这将使查询两种类型的帖子成为可能。这有点像黑客,但只要能奏效...

评论


感谢您的回答,请检查我更新的问题,我不确定我是否正确理解您。

–动力浮标
13年6月10日,0:05

我仍然没有完成这项工作,因此,如果您有解决方案,我很想知道我在做什么错。另外,如果您要领取奖金,我也提供悬赏:stackoverflow.com/questions/17016770/…

–动力浮标
13年6月20日在2:45

两件事情。 “ your_keys_name”和“ your_meta_key”都应该是相同的字符串,而不是不同的字符串,否则您似乎误解了这个问题。其次,我在本地设置上对此进行了测试,它排除了存在键的任何帖子(通过meta_query),排除了缺少键的任何帖子(通过meta_key),导致没有帖子显示。但是,此答案是迈向至少要用;-)表示的步骤。

– Binki
2014年7月31日下午4:16

哦,有趣的是,如果您仅向meta_query添加'relation'=>'OR',那么此答案确实有效。古怪的东西o_o。

– Binki
2014年7月31日下午4:18

@binki只需对我的问题进行编辑,然后更改您认为应该更改的位即可。这是一个社区驱动的网站:)

– kaiser
2014年7月31日在9:58

#10 楼

我认为@kaiser想要​​做的是通过应用某种虚拟机where条件不过滤任何这些帖子的方式来告诉查询返回具有该meta键的所有帖子。因此,如果您知道自定义字段可以采用的所有值都是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' => '!=',
    ) )
) );


也没有经过测试,但是觉得值得尝试:-)。

评论


现在尚无法真正测试,但可以肯定的是,它只会返回设置了item_price而不是“”的帖子。

–动力浮标
13年8月23日在11:13

#11 楼

我最终遇到了一点麻烦(IMHO),但是对于我来说,它确实对我有用。

您可以连接到过滤器posts_join_paged和posts_orderby来更新联接和顺序字符串。只要您首先加入该字段,就可以根据自己的需要进行排序,而不是假设该特定帖子必须存在该字段,而不是WP_Query。然后,您可以从WP_Query参数中删除meta_keyorderby和`order。

下面是一个示例。在某些情况下,我必须在每个函数的顶部都进行转义,因为它将把它添加到使用WP_Query的所有内容中。您可能需要修改它以满足您的特定需求。可悲的是,缺少这两个过滤器的文档,所以...祝您好运! :)

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


评论


该代码有效。但是meta_value是作为字符串处理的。因此6的等级更高,为50。可以进行任何修改以将其视为数字吗?

–开车兜风
17年1月24日在17:59

@Drivingralle cast(my_custom_meta_key.meta_value为unsigned)DESC应该可以解决问题...

–tfrommen
17年2月2日在21:34

谢谢@tfrommen。 $ orderby_statement =“ cast(my_custom_meta_key.meta_value为未签名)DESC”;效果很好。

–开车兜风
17年2月6日在13:22

#12 楼

此解决方案对我有用:

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_value的记录。此其他解决方案显示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 ); 


#13 楼

我能够通过使用一个查询和适当的参数来解决此问题。 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