Sort on meta value but include posts that don’t have one

I’ve been modifying the built in WP search using the pre_get_posts filter, allowing the user to sort the posts (including a bunch of custom post types) by different fields.

The problem I’m having though is that when I tell WP to sort by a meta value it will exclude all posts that don’t have that meta value set. This causes the number of results to change if you change sorting from say “Price” to “Date” because “Posts” don’t have “Price” set but “Items” do.

This is not what I want, so I’d like to know if there’s a way to include ALL posts – even those that lack the meta value I’m sorting on – and put the one’s without the value last.

I know how to sort on more than one field but that doesn’t help.

Thanks

Seems I’m not the only one with this question: Way to include posts both with & without certain meta_key in args for wp_query? but there’s no solution there.

Update

I’ve tried the answer but not sure if I understood correctly, here’s what I have right now:

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

The meta value is a number (it is used to store a price as the name suggests)

Update 2

I’ve commented out the order-stuff and all I have now is this:

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

With this code the query seems to return all posts that don’t have the item_price key and none of the posts that have it. I.E. the problem is now reversed.

If I add in the order-code as well I get 0 results.

Edit: …three years later… 😛 I had this issue again. I tried all the answers given and none work. Not sure why some people seem to think they work but they don’t work for me at least.

The solution I ended up with is using the save_post filter – making sure all posts have the custom field I wish to sort on. It’s a bit annoying I have to do it, but at as long as you do it early on you’ll likely have no issues.

In this case I was building a “view counter” on posts and wanted users to be able to sort on the most read posts. Again, posts that have never been viewed (I guess that’s pretty unlikely – but still) disappeared when sorting on the view count. I added this bit of code to make sure all posts have a view count:

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

1
14

There’s two possible solutions to this:

1. All posts have meta

The best solution I have found here is to give the rest of the posts/products an item price of 0. You can do this manually, or loop through all the posts and if the price is empty then update it.

To make this manageable in the future you can hook into save_post and give them a value when they are first added (only if it’s blank).

2. Multiple Queries

You could run the first query as you’re doing and store the IDs of the posts returned. You could then run another query for all posts and orderby date, excluding the IDs return from the first query.

You can then print out the two results separately order and you’ll get the desired results.

Leave a Comment