WP Query Args - Título o Valor Meta

18 feb 2015, 02:59:25
Vistas: 22.2K
Votos: 12

¿Cómo puedo hacer una consulta por meta_value o título?

Si establezco meta_values,

$args['meta_query'] = array(
   'relation' => 'OR',
    array(
        'key' => 'model_name',
        'value' => $thesearch,
        'compare' => 'like'
    )
);

automáticamente se agregan como AND.

Esta búsqueda sería:

WHERE title = 'Search' AND (model_name = 'Search' OR ...)

Necesito

WHERE title = 'Search' OR (model_name = 'Search' OR ...)
1
Comentarios

Intenté agregar $args['relation'] = 'OR'; pero no es reconocido. Parece que me falta entender cómo controlar la lógica condicional a través de args.

Bryan Bryan
18 feb 2015 03:04:49
Todas las respuestas a la pregunta 3
16
28

Ten en cuenta que la parte relation en el argumento meta_query solo se utiliza para definir la relación entre las consultas sub meta.

Puedes probar esta configuración:

$args = [ 
    '_meta_or_title' => $thesearch,   // ¡Nuestro nuevo argumento personalizado!
    'meta_query'    => [
        [
            'key'     => 'model_name',
            'value'   => $thesearch,
            'compare' => 'like'
        ]
    ],
];

donde hemos introducido un argumento personalizado _meta_or_title para activar la consulta meta O título.

Esto es compatible con el siguiente plugin:

<?php
/**
 *  Plugin Name:   Consulta Meta OR Título en WP_Query
 *  Description:   Activado a través del argumento '_meta_or_title' de WP_Query 
 *  Plugin URI:    http://wordpress.stackexchange.com/a/178492/26350
 *  Plugin Author: Birgir Erlendsson (birgire)
 *  Version:       0.0.1
 */

add_action( 'pre_get_posts', function( $q )
{
    if( $title = $q->get( '_meta_or_title' ) )
    {
        add_filter( 'get_meta_sql', function( $sql ) use ( $title )
        {
            global $wpdb;

            // Solo se ejecuta una vez:
            static $nr = 0; 
            if( 0 != $nr++ ) return $sql;

            // Modificar parte WHERE:
            $sql['where'] = sprintf(
                " AND ( %s OR %s ) ",
                $wpdb->prepare( "{$wpdb->posts}.post_title = '%s'", $title ),
                mb_substr( $sql['where'], 5, mb_strlen( $sql['where'] ) )
            );
            return $sql;
        });
    }
});
18 feb 2015 07:12:24
Comentarios

¡Guau! ¡Respuesta increíble! Esto debería estar en el núcleo.

Bryan Bryan
18 feb 2015 22:06:29

La respuesta me llevó a mitad de camino. http://wordpress.stackexchange.com/questions/178574/acf-relationship-field-search-filtering ¿Alguna idea sobre esta siguiente?

Bryan Bryan
18 feb 2015 22:16:36

He usado esta respuesta para crear una consulta para un valor meta específico en la página edit.php de un tipo de contenido personalizado. No consulto el título en absoluto, porque mi tipo de contenido personalizado no tiene título. Por lo tanto, reemplacé " AND ( %s OR %s ) " por " OR ( %s ) " en la consulta SQL y eliminé por completo la línea $wpdb->prepare( "{$wpdb->posts}.post_title = '%s'", $title ),. ¿Es este un enfoque válido?

BdN3504 BdN3504
17 may 2015 13:30:57

@BdN3504 Me alegra saber que te resultó algo útil. Si te entiendo correctamente, tus modificaciones deberían funcionar.

birgire birgire
17 may 2015 16:20:48

gracias por la respuesta. mis modificaciones sí funcionan, solo que no estoy seguro si es el enfoque correcto. en realidad quiero eliminar los parámetros de título y contenido en la consulta y solo consultar los metadatos. aunque el enfoque actual funciona, no es el más práctico

BdN3504 BdN3504
17 may 2015 22:32:03

@BdN3504 ok genial, no conozco una forma nativa (sin usar filtros) para cambiar AND -> OR para toda la consulta de metadatos, pero por supuesto puedes modificar las relaciones de la consulta de metadatos de muchas maneras dentro de meta_query de WP_Query.

birgire birgire
17 may 2015 22:40:43

Gran truco. Si quieres una búsqueda de subcadena para el título, reemplaza post_title = '%s' con post_title like '%%%s%%'.

jarnoan jarnoan
30 sept 2015 13:11:47

¿Cómo haría una consulta con el valor del título y la taxonomía en lugar del valor meta?

Jeff Jeff
13 oct 2018 18:11:23

@jeff Una vez escribí un plugin para combinar consultas: https://github.com/birgire/wp-combine-queries. También aquí experimenté con búsqueda (título, extracto, contenido) o consulta de taxonomía. Aquí encontré otra respuesta similar con la que experimenté. También podemos escribir dos WP_Query separados, luego recolectar y combinar los IDs de posts resultantes en un tercer WP_Query (si es necesario y si el ordenamiento en PHP no es suficiente) mediante el parámetro de entrada post__in.

birgire birgire
13 oct 2018 20:39:24

@birgire Opté por la ruta de las dos consultas separadas y funcionó, gracias.

Jeff Jeff
13 oct 2018 22:39:17

@Jeff Me alegra saber que eso funcionó para ti.

birgire birgire
13 oct 2018 22:42:18

@biergie ¡Gracias por esta increíble solución! Funciona genial, pero mi búsqueda no devuelve resultados cuando uso _meta_or_title + meta query + tax query. Cuando uso tu filtro personalizado y añado un tax_query, el elemento $sql['where'] está vacío. ¿Tienes alguna idea de cómo solucionarlo?

chris.ribal chris.ribal
6 jun 2019 15:50:42

@biergie Gracias. No lo he probado en un tiempo, pero deberías asegurarte de que no haya otros plugins/código afectándolo y, por ejemplo, ejecutarlo con el modo de depuración activado.

birgire birgire
7 jun 2019 13:05:50

@birgire - solo me preguntaba si tú o alguien podría indicar dónde usas el $args. gracias.

v3nt v3nt
29 jun 2020 19:48:55

El $q->get( '_meta_or_title' ) es igual que $args['_meta_or_title'].

birgire birgire
30 jun 2020 15:01:01

¡Solución espectacular! Sugiero que edites para incluir el comentario de jarnoan sobre usar LIKE en lugar de =, ya que es un consejo muy útil pero fácilmente pasado por alto

Kai Qing Kai Qing
22 oct 2020 05:53:00
Mostrar los 11 comentarios restantes
1

No encontré una solución para buscar múltiples palabras clave que puedan estar mezcladas en el título de la publicación, la descripción Y/O en una o varias metas, así que hice mi propia adición a la función de búsqueda.

Todo lo que necesitas es agregar el siguiente código en function.php, y cada vez que uses el argumento 's' en WP_Query() y quieras buscar también en una o varias metas, simplemente añades un argumento 's_meta_keys' que sea un array con las claves de meta(s) en las que deseas buscar:

/************************************************************************\
|**                                                                    **|
|**  Permite que la función de búsqueda de WP_Query() busque          **|
|**  múltiples palabras clave en metas además de post_title y         **|
|**  post_content                                                     **|
|**                                                                    **|
|**  Por rAthus @ Arkanite                                            **|
|**  Creado: 2020-08-18                                               **|
|**  Actualizado: 2020-08-19                                          **|
|**                                                                    **|
|**  Solo usa el argumento habitual 's' y añade un argumento          **|
|**  's_meta_keys' que contenga un array de las claves de meta(s)     **|
|**  en las que deseas buscar :)                                      **|
|**                                                                    **|
|**  Ejemplo :                                                        **|
|**                                                                    **|
|**  $args = array(                                                   **|
|**      'numberposts'  => -1,                                        **|
|**      'post_type' => 'post',                                       **|
|**      's' => $MI_CADENA_DE_BUSQUEDA,                              **|
|**      's_meta_keys' => array('META_KEY_1','META_KEY_2');           **|
|**      'orderby' => 'date',                                         **|
|**      'order'   => 'DESC',                                         **|
|**  );                                                               **|
|**  $posts = new WP_Query($args);                                    **|
|**                                                                    **|
\************************************************************************/
add_action('pre_get_posts', 'mi_busqueda_personalizada'); // añade la función especial de búsqueda en cada consulta get_posts (esto incluye WP_Query())
function mi_busqueda_personalizada($query) {
    if ($query->is_search() and $query->query_vars and $query->query_vars['s'] and $query->query_vars['s_meta_keys']) { // si estamos buscando usando el argumento 's' y añadimos un argumento 's_meta_keys'
        global $wpdb;
        $search = $query->query_vars['s']; // obtiene la cadena de búsqueda
        $ids = array(); // inicia el array de IDs de publicaciones coincidentes por palabra clave buscada
        foreach (explode(' ',$search) as $term) { // divide las palabras clave y busca resultados coincidentes para cada una
            $term = trim($term); // elimina espacios innecesarios
            if (!empty($term)) { // verifica que la palabra clave no esté vacía
                $query_posts = $wpdb->prepare("SELECT * FROM {$wpdb->posts} WHERE post_status='publish' AND ((post_title LIKE '%%%s%%') OR (post_content LIKE '%%%s%%'))", $term, $term); // busca en título y contenido como lo hace la función normal
                $ids_posts = [];
                $results = $wpdb->get_results($query_posts);
                if ($wpdb->last_error)
                    die($wpdb->last_error);
                foreach ($results as $result)
                    $ids_posts[] = $result->ID; // recoge los IDs de publicaciones coincidentes
                $query_meta = [];
                foreach($query->query_vars['s_meta_keys'] as $meta_key) // ahora construye una consulta para buscar en cada clave de meta deseada
                    $query_meta[] = $wpdb->prepare("meta_key='%s' AND meta_value LIKE '%%%s%%'", $meta_key, $term);
                $query_metas = $wpdb->prepare("SELECT * FROM {$wpdb->postmeta} WHERE ((".implode(') OR (',$query_meta)."))");
                $ids_metas = [];
                $results = $wpdb->get_results($query_metas);
                if ($wpdb->last_error)
                    die($wpdb->last_error);
                foreach ($results as $result)
                    $ids_metas[] = $result->post_id; // recoge los IDs de publicaciones coincidentes
                $merged = array_merge($ids_posts,$ids_metas); // fusiona los IDs de título, contenido y metas resultantes de ambas consultas
                $unique = array_unique($merged); // elimina duplicados
                if (!$unique)
                    $unique = array(0); // si no hay resultados, añade un ID "0" de lo contrario se devolverán todas las publicaciones
                $ids[] = $unique; // añade el array de IDs coincidentes al array principal
            }
        }
        if (count($ids)>1)
            $intersected = call_user_func_array('array_intersect',$ids); // si hay varias palabras clave, mantén solo los IDs que se encuentren en todos los arrays coincidentes
        else
            $intersected = $ids[0]; // de lo contrario, mantén el array único de IDs coincidentes
        $unique = array_unique($intersected); // elimina duplicados
        if (!$unique)
            $unique = array(0); // si no hay resultados, añade un ID "0" de lo contrario se devolverán todas las publicaciones
        unset($query->query_vars['s']); // elimina la consulta de búsqueda normal
        $query->set('post__in',$unique); // añade un filtro por ID de publicación en su lugar
    }
}

Ejemplo de uso:

$busqueda = "palabras clave a buscar";

$args = array(
    'numberposts'   => -1,
    'post_type' => 'post',
    's' => $busqueda,
    's_meta_keys' => array('short_desc','tags');
    'orderby' => 'date',
    'order'   => 'DESC',
);

$posts = new WP_Query($args);

Este ejemplo buscará las palabras clave "palabras clave a buscar" en los títulos de las publicaciones, descripciones y en las claves de meta 'short_desc' y 'tags'.

Las palabras clave pueden encontrarse en uno o varios de los campos, en cualquier orden, y devolverá cualquier publicación que tenga todas las palabras clave en cualquiera de los campos designados.

Obviamente, puedes forzar la búsqueda en una lista de claves de meta que incluyas en la función y deshacerte de los argumentos adicionales si quieres que TODAS las consultas de búsqueda incluyan estas claves de meta :)

¡Espero que esto ayude a cualquiera que se enfrente al mismo problema que yo!

18 ago 2020 21:01:43
Comentarios

Esto parece bastante sólido. ¿Puedes ayudar a intentar incluir campos personalizados del autor [usuario] de una publicación?

Adeerlike Adeerlike
30 mar 2022 15:39:08
1

Soy bastante nuevo en WP, no he probado demasiado este enfoque que se me ocurrió. Quizás puedas ayudarme a verificar si estoy en lo correcto. La solución que he encontrado hasta ahora es implementar la misma lógica de meta_query, solo haciendo algunos reemplazos.

Primero, el uso:

$args = array(
    'lang' => 'pt', //esta función no entra en conflicto (ejemplo: polylang)
    'post_type' => 'produtos', 
    'post_status'   => 'publish',
    'posts_per_page' => 10,
    'paged' =>  1,
    'fields' => 'ids',
);
$args['meta_query] = [
    ['relation'] => 'OR'; //cualquier relación que desees
    [
        'key' => 'acf_field', //cualquier campo personalizado (uso regular)
        'value' => $search, //cualquier valor (uso regular)
        'compare' => 'LIKE', //cualquier comparación (uso regular)
    ],
    [
        'key' => 'post_title', //establece el contenido de post de WordPress que desees ('post_content', 'post_title' y 'post_excerpt')
        'value' => $search, //cualquier valor
        'compare' => 'LIKE', //probado con 'LIKE' y '=', funciona genial y no veo otras necesidades.
    ],
    [
        'key' => 'post_exerpt', // puedes agregar tantas veces como necesites
        'value' => $search_2,
        'compare' => 'LIKE', 
    ],
];
$the_query = new WP_Query( $args ); //solo consulta
wp_reset_postdata(); //limpia tu consulta

Para que funcione, agrega esta función al functions.php de tu tema

function post_content_to_meta_queries($where, $wp_query){
    global $wpdb;

    //si no hay metaquery, ¡adiós!
    $meta_queries = $wp_query->get( 'meta_query' );
    if( !$meta_queries || $meta_queries == '' ) return $where;

    //si solo una relación
    $where = str_replace($wpdb->postmeta . ".meta_key = 'post_title' AND " . $wpdb->postmeta . ".meta_value", $wpdb->posts . ".post_title", $where);
    $where = str_replace($wpdb->postmeta . ".meta_key = 'post_content' AND " . $wpdb->postmeta . ".meta_value", $wpdb->posts . ".post_content", $where);
    $where = str_replace($wpdb->postmeta . ".meta_key = 'post_excerpt' AND " . $wpdb->postmeta . ".meta_value", $wpdb->posts . ".post_excerpt", $where);

    ////para relaciones anidadas

    //cuenta el número de meta queries para posibles reemplazos
    $number_of_relations = count($meta_queries);

    //reemplaza 'WHERE' usando la lógica de nombres multidimensionales de postmeta usada por el núcleo de WordPress
    $i = 1;
    while($i<=$number_of_relations && $number_of_relations > 0){
        $where = str_replace("mt".$i.".meta_key = 'post_title' AND mt".$i.".meta_value", $wpdb->posts . ".post_title", $where);
        $where = str_replace("mt".$i.".meta_key = 'post_content' AND mt".$i.".meta_value", $wpdb->posts . ".post_content", $where);
        $where = str_replace("mt".$i.".meta_key = 'post_excerpt' AND mt".$i.".meta_value", $wpdb->posts . ".post_excerpt", $where);
        $i++;
    }

    return $where;
}

add_filter('posts_where','post_content_to_meta_queries',10,2);

¡Estoy bastante seguro de que se puede mejorar! ¡Espero que ayude!

30 jul 2021 22:43:56
Comentarios

Actualización: He estado usando esta solución durante el último año y medio. Un salvavidas para búsquedas complejas junto con ACF.

Jeff Oliva Jeff Oliva
19 dic 2022 15:04:36