Ordenar por múltiples campos de metadatos

2

Tengo dos campos personalizados:

  • meta_key1 es booleano ( 0 o 1 ).
  • meta_key2 es un valor de fecha, por ejemplo, 20150623 .

No todos los campos meta_key2 tienen un valor explícito. Parece que están siendo tratados como una cadena vacía '' .

Me gustaría encontrar todas las publicaciones que tengan un valor meta_key2 (fecha) que sea mayor que hoy o que tengan un valor meta_key1 que sea verdadero.

Este es el orden que me gustaría que mostraran.

  
  1. key2.meta_value >= today ASC
  2.   
  3. key1.meta_value = TRUE ordered by date ASC (aquellos con fechas dadas primero).
  4.   

Parte del problema es que los campos con un parámetro de fecha vacía parecen tratarse como 0 y, por lo tanto, aparecen primero en el orden de ASC . He intentado usar COALESCE para explicar esto y he tenido algunos éxitos, pero estoy colgado de una parte. No puedo obtener las publicaciones que key1.meta_value = FALSE y key2.meta_value is >= today para mostrar en el orden de ASC .

Aquí está mi consulta:

$meta_key1 = 'prog_ongoing';
$meta_key2 = 'prog_date_start';
$start_date = date('Ymd');

$postids = $wpdb->get_col( $wpdb->prepare( 
"
SELECT      DISTINCT key1.post_id
FROM        $wpdb->postmeta key1
INNER JOIN  $wpdb->postmeta key2
            ON key2.post_id = key1.post_id
            AND key2.meta_key = %s
WHERE       key1.meta_key = %s
            AND key1.meta_value is TRUE
            OR key2.meta_value >= %d
ORDER BY    COALESCE(NULLIF(key1.meta_value, 0), 0) DESC, COALESCE(NULLIF(key2.meta_value, ''), $start_date) ASC, key2.meta_value ASC 
",
$meta_key2,
$meta_key1,
$start_date
) );

No estoy seguro de que mi declaración COALESCE en key1.meta_value esté haciendo algo. Gracias por cualquier información sobre esto.

    
pregunta eleclair 23.06.2015 - 16:18

3 respuestas

2

Un problema con la consulta es que la auto-unión con el ambiguo WHERE le da un conjunto de datos cruzado (que está enmascarado por el DISTINCT ), por lo que sería más sencillo usar wp_post como base para adjunte las uniones que coincidan exactamente con las claves, por ejemplo,

    SELECT      p.ID, key1.meta_value as prog_ongoing, key2.meta_value as prog_date_start
    FROM        $wpdb->posts p
    INNER JOIN  $wpdb->postmeta key1
                ON key1.post_id = p.ID
                AND key1.meta_key = %s
    INNER JOIN  $wpdb->postmeta key2
                ON key2.post_id = p.ID
                AND key2.meta_key = %s

lo que da un conjunto de datos lineal. Luego puede agregar (o no) la cláusula WHERE para restringir los datos:

    WHERE       key1.meta_value IS TRUE OR key2.meta_value >= %d

y para el ORDER BY use una orden de campo único de la declaración CASE :

    ORDER BY    CASE
                    WHEN key2.meta_value >= %d THEN CONCAT('A', key2.meta_value)
                    WHEN key1.meta_value AND key2.meta_value THEN CONCAT('B', key2.meta_value)
                    WHEN key1.meta_value THEN 'C'
                    ELSE 'D'
                END ASC

o algo similar, lo anterior requiere que prepare args sea:

    $meta_key1,
    $meta_key2,
    $start_date, $start_date

Podría usar el filtro posts_orderby para hacer algo similar usando WP_Query (aunque usa un método que produce conjuntos de datos cruzados, lo que requiere que use un GROUP BY , lo que puede complicar las cosas). Por ejemplo

$args = array(
    'posts_per_page' => -1,
    'post_type' => 'cpt_program',
    'meta_query' => array(
        'ongoing' => array(
            'key' => 'prog_ongoing',
        ),
        'start_date' => array(
            'key' => 'prog_date_start',
        )
    ),
);

add_filter( 'posts_orderby', $func = function ( $orderby, $query ) {
    $start_date = date('Ymd');
    global $wpdb;
    $orderby = $wpdb->prepare(
        "
        CASE
            WHEN mt1.meta_value >= %d THEN CONCAT('A', mt1.meta_value)
            WHEN {$wpdb->postmeta}.meta_value AND mt1.meta_value THEN CONCAT('B', mt1.meta_value)
            WHEN {$wpdb->postmeta}.meta_value THEN 'C'
            ELSE 'D'
        END ASC
        "
        , $start_date
    );
    return $orderby;
}, 10, 2 );
$query = new WP_Query( $args );
remove_filter( 'posts_orderby', $func, 10, 2 );
    
respondido por el bonger 24.06.2015 - 20:28
3

Primer paso para una solución. Utiliza la nueva meta clasificación que se introdujo en 4.2:

 <?php
    $args = array(
        'posts_per_page' => -1,
        'meta_query' => array(
            'relation' => 'OR',
            'ongoing' => array(
                'key'     => 'prog_ongoing',
                'value'   => 1
            ),
            'start_date' => array(
                array(
                    'key' => 'prog_date_start',
                    'value'   => date('Ymd'),
                    'type'    => 'numeric',
                    'compare' => '>='
                )
            )
        ),
        'orderby' => 'start_date ongoing',
        'order'   => 'ASC',
    );

    $programs = new WP_Query($args);
    ?>
    <?php while($programs->have_posts()): $programs->the_post(); ?>
        <h1><?php the_title(); ?></h1>
    <?php endwhile; ?>

Creo que tengo la lógica correcta, pero hágame saber si de otra manera

EDITAR

Puede consultar la nueva sintaxis para clasificar por varios campos personalizados en Wordpress 4.2 en la siguiente marca

respondido por el Manny Fleurmond 24.06.2015 - 02:54
0

¿Por qué no usar WP_Query() ? Lote más simple de esta manera:

    <?php
    $args = array(
        'posts_per_page' => -1,
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key'     => 'prog_ongoing',
                'value'   => 1
            ),
            array(
                'relation' => 'AND',
                array(
                    'key' => 'prog_date_start',
                    'value'   => date('Ymd'),
                    'type'    => 'numeric',
                    'compare' => '<='
                ),
                array(
                   'key' => 'prog_date_start',
                    'value'   => 1,
                    'type'    => 'numeric',
                    'compare' => '>'
                )
            )
        ),
        'orderby' => 'meta_value_num date',
        'order'   => 'ASC',
        'meta_key' => 'prog_date_start'
    );

    $programs = new WP_Query($args);
    ?>
    <?php while($programs->have_posts()): $programs->the_post(); ?>
        <h1><?php the_title(); ?></h1>
    <?php endwhile; ?>
    
respondido por el passatgt 23.06.2015 - 17:40

Lea otras preguntas en las etiquetas