按元值排序,但包含没有一个的帖子


37

我一直在使用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

我已经注释掉了订单材料,现在我所拥有的是:

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

使用此代码,查询似乎返回所有没有item_price关键字的帖子,并且没有任何包含关键字的帖子。IE浏览器的问题现在被扭转了。

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

编辑:...三年后...:PI再次出现此问题。我尝试了所有给出的答案,但没有任何效果。不知道为什么有些人似乎认为自己在工作,但至少对我没有用。

我最终得到的解决方案是使用save_post过滤器-确保所有帖子都具有我要排序的自定义字段。我必须这样做有点烦人,但是只要您尽早这样做,您可能就不会有任何问题。

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

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

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

第一:meta_querytax_query永远array( array() ),因为它们组合多个阵列。第二-如我的回答中所述-您需要使用meta_value_num数字。可能还需要实际定义meta_value_num(请参见WP_Query-Codex页面条目)。最后,它没有意义的,orderASC DESC方向。那不可能 空格分隔符仅适用于该分隔符orderby,您不能告诉它对第一ASC和第二个空格进行排序DESC。这就是posts_clauses过滤器的作用。
kaiser

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

感谢您的帮助,我会尽力帮助您。据我所知,ASC DESC这样做的原因是它按meta_valuein ASCdatein进行排序DESC
powerbuoy

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

Answers:


4

有两种可能的解决方案:

1.所有帖子都有meta

我在这里找到的最佳解决方案是给其余帖子/产品赋予零价格。您可以手动执行此操作,也可以遍历所有帖子,如果价格为空,则进行更新。

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

2.多重查询

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

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


1
三年后,我又遇到了同样的问题: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' ); 

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

实际例子

/**
 * 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(要查询的meta键的)排序。$query->set( 'orderby', 'meta_value title' );(当多个帖子的met​​a键值相同时,按元值排序,然后按标题排序)。这应该在pre_get_posts钩子中使用传递的$query变量来完成。请记住,询问的问题是如何按元值排序,而不忽略没有该元键值的帖子。
noahmason

@powerbuoy参见更新的实际示例
noahmason

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

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

8

此方法将返回所有帖子,包括有和没有请求的帖子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 EXISTSprevent WHERE子句可防止筛选出缺少meta字段的帖子。为此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会用一些随机的其他 meta值(比方说,_edit_last这似乎是1经常在我的数据库,或者完全是任意其他一些内部WordPress的元数据),其wp_postmeta.meta_value在该ORDER BY条款。因此,尽管此方法很接近并且可能适用于某些数据,但它还是被破坏了。因此,我只能说,如果您的item_price值碰巧与MySQL为丢失的帖子选择的随机元字段不冲突,那么item_price这可能对您来说很好。如果您只需要保证您的帖子与item_price相对于彼此正确排序,而不考虑其他帖子的排序,可能还可以。但是我认为这只是wordpress的一个缺点。请纠正我,我希望我错了,有办法解决这个问题;-)。

似乎,对于INNER JOIN wp_postmetapostmetameta_key给定帖子中缺少时,MySQL从与该帖子关联的多个行中选择一个随机行。从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_keyCAN永远NULL

因此,我想我不得不说,这已经在wordpress的内置文件中实现了,WP_Query因为它已被记录(在wordpress-3.9.1中)。烦。因此,如果您实际上需要此功能才能正常工作,则可能需要挂接到其他地方的wordpress中,然后直接修改生成的SQL


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

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

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

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

2

我想我有解决办法。

您可以使用两个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');

1
这里的问题是空字符串比较,将其删除,它'value' => '', 也可以进行第二次比较,NOT EXISTS并且不需要最后一个设置指令
nodws

2

这里每个人都必须解决的问题与元查询的顺序有关。为了正确排序,您需要将“ NOT EXISTS”查询放在“ 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 );

我在WordPress Codex上找到了标题为“ orderby”且带有多个“ meta_key ”的描述: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),导致没有帖子显示。但是,此答案是朝着至少要用词 ;-) 表示的步骤迈出的一步。
2014年

1
哦,有趣的是,如果您只添加'relation' => 'OR'到,那么此答案确实有效meta_query。古怪的东西o_o。
binki 2014年

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

0

我想@kaiser试图做的是告诉查询通过应用一种假人,以返回有Meta键的所有帖子where条件,以过滤任何这些员额。因此,如果您知道自定义字段可以采用的所有值都是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而不是“”的帖子。
powerbuoy

0

我最终得到了一些破解(IMHO)来解决这个问题,但就我而言,它确实为我完成了工作。

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

下面是一个例子。在某些情况下,我不得不在每个函数的顶部都进行转义,因为它将把它添加到所有使用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。可以将其视为数字吗?
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 '02

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 meta_value的记录。此其他解决方案显示ASC顺序,并在最后显示null:

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 ); 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.