Сортировать по мета-значению, но включать посты, которые не имеют

37

Я модифицировал встроенный поиск WP с помощью pre_get_postsфильтра, позволяющего пользователю сортировать сообщения (включая несколько пользовательских типов сообщений) по разным полям.

Проблема, с которой я столкнулся, заключается в том, что когда я говорю WP сортировать по мета-значению, он исключает все посты, для которых это мета-значение не установлено. Это приводит к изменению количества результатов, если вы измените сортировку, скажем, «Цена» на «Дата», потому что для «Сообщений» не установлена ​​«Цена», а для «Предметов».

Это не то, что я хочу, поэтому я хотел бы знать, есть ли способ включить ВСЕ посты - даже те, в которых отсутствует мета-значение, по которому я сортирую, - и поместить одно без значения last.

Я знаю, как сортировать по нескольким полям, но это не помогает.

Благодарность

Кажется, я не единственный, кто задается этим вопросом: как включить в аргументы для wp_query сообщения с мета-ключом и без него? но там нет решения.

Обновить

Я попробовал ответ, но не уверен, правильно ли я понял, вот что у меня сейчас:

<?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ключа, и ни одного из сообщений, у которых его есть. То есть проблема сейчас решена.

Если я добавлю код заказа, я получу 0 результатов.

Редактировать: ... три года спустя ... : У ПИ снова возникла эта проблема. Я перепробовал все ответы, и ни один не работал. Не уверен, почему некоторые люди думают, что они работают, но они, по крайней мере, не работают для меня.

Решение, которое я выбрал, заключается в использовании save_postфильтра - убедитесь, что все сообщения имеют настраиваемое поле, по которому я хочу отсортировать. Это немного раздражает, я должен это делать, но если вы сделаете это рано, у вас, вероятно, не будет проблем.

В этом случае я строил «счетчик просмотров» для постов и хотел, чтобы пользователи могли сортировать наиболее читаемые посты. Опять же, посты, которые никогда не просматривались (я думаю, это довольно маловероятно - но все же) исчезали при сортировке по количеству просмотров. Я добавил этот фрагмент кода, чтобы убедиться, что все сообщения имеют количество просмотров:

add_action('save_post', function ($postId) {
    add_post_meta($postId, '_sleek_view_count', 0, true);
});
powerbuoy
источник
Пожалуйста, покажите нам свой код. Облегчает ответ.
Кайзер
Во- первых: meta_queryи tax_queryэто всегдаarray( array() ) , как они сочетают в себе несколько массивов. Второе - как уже упоминалось в моем ответе - нужно использовать meta_value_numдля чисел. С тем же успехом может потребоваться определение meta_value_num(см. WP_QueryЗапись страницы -Codex). Наконец, это не имеет смысла orderв направлении ASC и DESC направлении. Это невозможно. Разделитель пространства работает только для, orderbyи вы не можете сказать ему, чтобы отсортировать первое ASCи второе DESC. Для этого и нужен posts_clausesфильтр.
Кайзер
И убедитесь, что ваши meta_value_numзаписи являются действительными числами. Слишком часто видели, что кто-то заявляет, что это число, но фактически сохраняет его в виде строки в базе данных.
Кайзер
Спасибо за вашу помощь, я попробую это и вернусь к вам. Причина ASC DESCнастолько , что он сортирует на meta_valueв ASCи dateв DESC, насколько я могу сказать , что это работает.
PowerBoy
1
@Howdy_McGee это правильно. Некоторые из моих пользовательских типов сообщений имеют этот набор значений. Некоторые нет. А встроенные типы сообщений (например, POST и PAGE) - нет. Поэтому всякий раз, когда я пытаюсь отсортировать это настраиваемое поле, отображаются только сообщения с настраиваемым полем.
PowerBoy

Ответы:

4

Есть два возможных решения этого:

1. Все сообщения имеют мета

Лучшее решение, которое я нашел здесь, это дать остальным постам / продуктам цену товара 0. Вы можете сделать это вручную или просмотреть все посты, и если цена пуста, обновите ее.

Чтобы сделать это управляемым в будущем, вы можете подключиться save_postи дать им значение при первом добавлении (только если оно пустое).

2. Несколько запросов

Вы можете выполнить первый запрос, как вы делаете, и сохранить идентификаторы возвращенных сообщений. Затем вы можете выполнить другой запрос для всех сообщений и даты заказа, исключая идентификаторы, возвращаемые из первого запроса.

Затем вы можете распечатать два результата отдельно, и вы получите желаемые результаты.

Стивен Джонс
источник
1
Три года спустя, и у меня снова возникла та же проблема: P Пришлось использовать save_postметод (я обновил свой вопрос с помощью кода, который я использовал).
PowerBoy
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умолчанию и не будет игнорировать сообщения без значения для этого ключа.

noahmason
источник
Просто читая код, все, что кажется, - это получать посты, которые имеют, custom_meta_keyи получать посты, которые не имеют custom_meta_key. Не стесняйтесь включать фактический рабочий пример с сортировкой.
PowerBoy
1
Вы правы, это все, что он делает, но строка ниже отвечает за упорядочение по meta_value (мета-ключа, который запрашивается). $query->set( 'orderby', 'meta_value title' );(Порядок по мета-значению, а затем по заголовку, когда несколько сообщений имеют одинаковое значение для мета-ключа). Это должно быть сделано в pre_get_postsхуке, используя переданную $queryпеременную in . Имейте в виду, что заданный вопрос состоял в том, как упорядочить по мета-значению, не игнорируя при этом посты, которые не имеют значения для этого мета-ключа.
noahmason
@powerbuoy Смотрите обновленный практический пример
noahmason
Хорошо, я попробую в следующий раз, когда столкнусь с этой проблемой.
powerbuoy
1
Работал для меня в обычном get_posts()звонке, чтобы пихать посты с _featuredмета в начало, а затем упорядочивать по дате после этого. Благодарность!
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 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довольно часто встречается в моей базе данных или некоторые другие внутренние метаданные WordPress, которые являются совершенно произвольными) для своихwp_postmeta.meta_value в ORDER BYположение. Таким образом, хотя этот метод близок и может работать для определенных данных, он не работает. Итак, все, что я могу сказать, это то, что если ваши item_priceзначения не конфликтуют со случайными мета-полями, которые MySQL выбирает для отсутствующих сообщений item_price, это может хорошо работать для вас. Если все, что вам нужно, это гарантия того, что ваши сообщения сitem_priceправильно упорядочены относительно друг друга без учета порядка других постов, это может быть в порядке. Но я думаю, что это просто недостаток в WordPress. Пожалуйста, поправьте меня, я надеюсь, что я не прав, и есть способ решить эту проблему ;-).

Похоже, что для INNER JOIN wp_postmetaMySQL выбирается случайная строка из множества postmetaстрок, связанных с сообщением, когда 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' и unaliased wp_postmetaвсегда является INNER JOINвместо когда - либо LEFT JOIN, то естьwp_postmeta.meta_value и wp_postmeta.meta_keyможет ,никогда не будет NULL.

Поэтому я должен сказать, что это невозможно с помощью встроенного в WordPress, WP_Queryкак это теперь задокументировано (в WordPress-3.9.1). Беспокоить. Так что если вам действительно нужно, чтобы это работало правильно, вам, вероятно, нужно подключиться к WordPress в другом месте и напрямую изменить сгенерированный SQL .

Бинки
источник
Выглядит очень перспективно! Я попробую это в следующий раз, когда у меня будет эта проблема. Я хотел бы дать вам ответ прямо сейчас, но предпочел бы подтвердить, что сначала он работает для меня.
Powerbuoy
Это мешало мне все показывать. Ничего не показало после реализации этого.
Джейк
@ Джейк Да, здесь то же самое. Имел сегодня этот вопрос снова и попробовал это. Возвращает 0 результатов.
Powerbuoy
Какую версию WordPress вы, ребята, используете? Я думаю, что этот пост описывает, как использовать внутренний недокументированный API, который не поддерживается WordPress, и поэтому, вероятно, работает, только если вы используете WordPress-3.9.1 или не слишком много версий, кроме этого.
Бинки
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');
Рафаэль М31
источник
1
Проблема здесь в сравнении пустых строк, удалите его, и оно 'value' => '', также будет работать, NOT EXISTSи второе сравнение должно быть, и последняя команда set не требуется
nodws
2

Проблема, с которой все здесь сталкиваются, связана с порядком мета-запросов. Для правильной сортировки вам нужно будет поставить перед запросом «НЕ СУЩЕСТВУЕТ» «СУЩЕСТВУЕТ».

Причина этого заключается в том, что WordPress использует значение meta_value последнего оператора «LEFT JOIN» в предложении «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,
]);
Павел
источник
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');

rjpedrosa
источник
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

KittMedia
источник
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 с заголовком « orderby» с несколькими «meta_key's »: 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_keyи keyзначения. В противном случае вы не можете сравнить с несуществующими значениями, что должно позволить запрашивать оба вида сообщений. Это что-то вроде взлома, но пока это работает ...

кайзер
источник
Спасибо за ваш ответ, пожалуйста, проверьте мой обновленный вопрос, я не уверен, что правильно вас понял.
PowerBoy
Я до сих пор не сделал эту работу, поэтому, если у вас есть решение, я хотел бы знать, что я делаю неправильно. Кроме того, я устанавливаю вознаграждение на SO, если вы хотите требовать его: stackoverflow.com/questions/17016770/…
powerbuoy
1
Две вещи. 'your_keys_name'и 'your_meta_key'оба должны быть одной и той же строкой, а не отличаться друг от друга, иначе похоже, что вы неправильно поняли вопрос. Во-вторых, я проверил это на своей локальной установке, и он исключает все посты, где существует ключ (через meta_query), и исключает любые посты, где ключ отсутствует (через meta_key), в результате чего посты не отображаются. Тем не менее, этот ответ является шагом к чему-то, что слова, по крайней мере ;-).
Бинки
1
О, интересно, этот ответ работает, если вы просто добавите 'relation' => 'OR'к meta_query. Дурацкие штучки о_о.
Бинки
@binki Просто внесите изменения в мой вопрос и измените биты, которые, по вашему мнению, следует изменить. Это управляемый сообществом сайт :)
kaiser
0

Я думаю, что @kaiser пытался сделать так, чтобы запрос возвращал все посты, которые имеют этот мета-ключ, применяя своего рода фиктивное условие условия, чтобы не фильтровать ни одну из этих постов. Поэтому, если вы знаете, что все значения, которые могут принимать ваши настраиваемые поля, это x, y, z, вы можете сказать «ГДЕ 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, а это не ''.
PowerBoy
0

Я закончил тем, что обошел это с небольшим количеством взлома (IMHO), но это сделало работу для меня в моем случае.

Вы можете подключиться к фильтрам posts_join_paged и posts_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,
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;

    }

}

Однако это решение сначала показывает записи с нулевым 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 ); 
jpussacq
источник