¿Cómo consultar posts por clave meta parcial?

26 mar 2016, 05:00:10
Vistas: 23.1K
Votos: 11

Tengo una función que almacena el estado "like" de una entrada como meta post. Quiero asociar ese "like" con el usuario que lo dio, así que configuré un campo personalizado llamado "like_status_{user_id}" (donde {user_id} es el id del usuario actualmente conectado) que almaceno como 0 o 1. Así que para una entrada con varios "likes" habría varios valores meta en la base de datos configurados así:

'meta_key' = 'like_status_0'
'meta_value' = 1
'meta_key' = 'like_status_2'
'meta_value' = 1
'meta_key' = 'like_status_34'
'meta_value' = 1

...y así sucesivamente.

Potencialmente hay miles de likes en una entrada específica. ¿Cómo ejecutaría una consulta que mostrara si alguien más también le dio like a esa entrada?

Estaba pensando en algo como esto:

$query = new WP_Query(array(
    'meta_key' => 'like_status_{user_id}',
    'meta_value' => 1,
));

Estoy tratando de enviar una notificación a todos los que han dado like a una entrada cuando alguien más le da like... algo como: "¡Hey, alguien más le dio like a esa entrada que te gustó. ¡Deberías ir a verla!" Pero necesito una manera de averiguar si alguien más le ha dado like a esa entrada y si es así, quiénes serían para poder notificarles.

Si no es posible, ¿podrías sugerir una mejor manera de almacenar estos datos como post_meta mientras se mantiene la eficiencia de actualizar rápidamente el estado de like de un solo usuario en una entrada?

0
Todas las respuestas a la pregunta 5
3
12

Es bastante difícil responder concretamente a tu pregunta. La primera parte es fácil, sin embargo. Recientemente hice algo similar en stackoverflow.

Las meta claves se comparan y coinciden exactamente. WP_Query no tiene forma de ajustar este comportamiento con un parámetro simple, pero siempre podemos introducir uno nosotros mismos y luego ajustar la cláusula posts_where para hacer una comparación LIKE en las meta claves.

EL FILTRO

Este es solo un filtro básico, ajústalo según sea necesario.

add_filter( 'posts_where', function ( $where, \WP_Query $q )
{ 
    // Verifica nuestra variable de consulta personalizada
    if ( true !== $q->get( 'wildcard_on_key' ) )
        return $where;

    // Filtramos la cláusula
    $where = str_replace( 'meta_key =', 'meta_key LIKE', $where );

    return $where;
}, 10, 2 );

Como puedes ver, el filtro solo se activa cuando establecemos nuestro nuevo parámetro personalizado, wildcard_on_key en true. Cuando esto se cumple, simplemente cambiamos el comparador = por el comparador LIKE.

Solo una nota sobre esto, las comparaciones LIKE son inherentemente más costosas de ejecutar que otras comparaciones.

LA CONSULTA

Puedes consultar tus publicaciones de la siguiente manera para obtener todas las publicaciones con meta claves like_status_{user_id}.

$args = [
    'wildcard_on_key' => true,
    'meta_query'      => [
        [
            'key'   => 'like_status_',
            'value' => 1,
        ]
    ]
];
$query = new WP_Query( $args );

OTRA PREGUNTA

Los campos personalizados no tienen impacto en el rendimiento, puedes leer mi publicación sobre este tema aquí. Sin embargo, me preocupa que digas que cada publicación puede tener cientos o miles de "me gusta". Esto puede afectarte en el rendimiento al obtener y almacenar en caché una gran cantidad de datos de campos personalizados. También puede saturar tu base de datos con una gran cantidad de datos de campos personalizados innecesarios, lo que dificulta su mantenimiento.

No soy un gran fanático de almacenar datos serializados en campos personalizados, ya que no se pueden buscar ni ordenar por datos serializados. Sin embargo, sugeriría almacenar todos los ID de usuario en un array bajo un solo campo personalizado. Puedes simplemente actualizar el array con el ID del usuario cuando a un usuario le guste una publicación. Obtener los datos del campo personalizado y recorrer el array de ID's para hacer algo con ellos es fácil. Solo echa un vistazo a get_post_meta().

Actualizar un campo personalizado también es fácil. Para eso, deberás consultar update_post_meta(), no sé cómo creas tus campos personalizados, pero update_post_meta() es definitivamente algo que querrás usar.

Si necesitas enviar correos electrónicos o notificaciones push cuando se actualiza un campo personalizado, tienes los siguientes hooks disponibles para trabajar. (Consulta update_metadata() para contexto)

CONCLUSIÓN

Antes de publicar esto, nuevamente, antes de que tomes la ruta de serialización, asegúrate de que no necesitarás ordenar por los datos serializados o buscar datos particulares dentro de los datos serializados.

26 mar 2016 09:09:10
Comentarios

¡Gracias por tu explicación sobre el rendimiento de post_meta! Muy útil.

codescribblr codescribblr
26 mar 2016 20:25:14

Esta debería ser la respuesta aceptada, siempre es mejor usar filtros en lugar de consultas personalizadas. También, ten en cuenta que si estás usando get_posts en lugar de WP_Query, necesitas pasar suppress_filters => false o no activará el filtro. Para realizar el LIKE en la clave meta, también necesitas poner % delante y detrás de la clave en el array dependiendo del tipo de búsqueda LIKE que quieras hacer.

Earle Davies Earle Davies
31 jul 2016 20:10:30

¿Y cómo lo filtrarías si quieres consultar posts pero EXCLUIR todos los posts que tengan una clave de post meta por prefijo? (ej. excluir todos los posts que tengan un post meta LIKE 'my_prefix_' ?

gordie gordie
1 abr 2017 18:47:42
1

Desde WordPress 5.1 ahora es posible usar meta queries como: Consulta de metadatos en WordPress

27 feb 2019 09:59:05
Comentarios

El escape de guiones bajos parece ser un problema con este método, pero por lo demás, se ve bastante bien. Gracias por resaltarlo.

Jake Jake
24 abr 2019 00:20:40
4

Desafortunadamente no puedes realizar un meta_query usando una comparación LIKE en el valor de meta_key cuando usas WP_Query. Ya he pasado por esto...

En su lugar, tienes un par de opciones si deseas mantener las relaciones de "like" como meta del post y no como meta del usuario o meta en una tabla personalizada.

Opción 1

  • no requiere modificación de tu esquema de meta
  • usa la clase wpdb para realizar una consulta personalizada

Ejemplo:

//cuando un usuario da like a un post...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "like_status_{$current_user_id}", 1, false);

//más tarde en la solicitud...
global $wpdb;

$results = $wpdb->get_results(
    "
    SELECT meta_key 
    FROM {$wpdb->prefix}postmeta 
    WHERE meta_key 
    LIKE 'like_status_%'
    ",
    ARRAY_N
);

$results = array_map(function($value){

    return (int) str_replace('like_status_', '', $value[0]);

}, $results);

array_walk($results, function($notify_user_id, $key){

    //aplicar a todos los usuarios excepto al que dio like al post
    if ( $notify_user_id !== $current_user_id ) {
        //lógica de notificación aquí...           
    }

});

Nota: la lógica podría simplificarse aún más si lo deseas.

Opción 2

  • requiere que cambies tu esquema de meta
  • requiere que almacenes el ID del usuario como valor meta
  • te permite usar WP_Query junto con meta_query

La Opción 2 requiere que cambies tu clave meta de like_status_{user_id} a algo universal como like_status o liked_by_user_id, donde en lugar de almacenar el valor 1 contra la clave, almacenes el ID del usuario como valor.

//cuando un usuario da like a un post...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "liked_by_user_id", $current_user_id, false);

//más tarde en la solicitud
$args = array(
    'post_type'  => 'post', //o un tipo de post de tu elección
    'posts_per_page' => -1,
    'meta_query' => array(
        array(
            'key' => 'liked_by_user_id',
            'value' => 0,
            'type' => 'numeric'
            'compare' => '>'
        )
    )
);

$query = new WP_Query($args);   

array_walk($query->posts, function($post, $key){

    $user_ids = get_post_meta($post->ID, 'liked_by_user_id');

    array_walk($user_ids, function($notify_user_id, $key){
        
        //notificar a todos los usuarios excepto al que dio like al post
        if ( $notify_user_id !== $current_user_id ) {
                
            //lógica de notificación aquí...
            //obtener usuario ej. $user = get_user_by('id', $notify_user_id);
            
        }

    });

});
26 mar 2016 09:11:51
Comentarios

Desde la versión 5.1, revisa mi respuesta a continuación

K. Tromp K. Tromp
5 nov 2019 00:13:14

@K.Tromp ¡Hurra!

Adam Adam
5 nov 2019 12:50:55

Por favor actualiza la respuesta aceptada para reflejar las últimas capacidades de WP o marca la respuesta de @K.Tromp como la más actualizada

zumek zumek
5 may 2020 05:23:40

Obsoleto ahora puedes.

Michael Rogers Michael Rogers
5 ene 2021 00:20:43
1

Si más adelante deseas extender esto, con estadísticas más detalladas, características, etc., entonces otra alternativa podría ser: tabla(s) personalizada(s)

  • ventajas: Adaptadas a tus necesidades y pueden ser indexadas para un mejor rendimiento.

  • desventajas: Más trabajo

También podría haber una solución alternativa utilizando una taxonomía personalizada, que podría ofrecer un mejor rendimiento en las consultas que las consultas de metadatos de entradas, debido a cómo están indexadas las tablas principales.

Estoy intentando enviar una notificación a todos los que han dado "me gusta" a una entrada cuando alguien más da "me gusta" a esa entrada... algo como: "¡Oye, a alguien más le gustó esa entrada que te gustó. Deberías ir a verla!" Pero necesito una manera de averiguar si alguien más ha dado "me gusta" a esa entrada y, de ser así, quiénes serían para poder notificarlos.

No estoy seguro de qué tipo de notificaciones te refieres aquí, pero esto puede volverse rápidamente engorroso.

Ejemplo: Un usuario que da "me gusta" a ~ 1000 entradas y cada entrada recibe ~ 1000 "me gusta", entonces hay 1M de notificaciones en proceso, ¡solo para ese usuario! Si estas son notificaciones por correo electrónico, es posible que el proveedor de hosting no esté contento y el usuario se volvería loco. Eso también podría ser costoso con un servicio de correo electrónico de terceros.

26 mar 2016 13:18:18
Comentarios

En realidad solo envío las notificaciones una vez por persona por publicación. Así que es menos de lo que parece, aunque sigue siendo mucho. La razón por la que estoy intentando usar tablas integradas es porque me gustaría poder usar la API REST estándar de WP en el futuro en una aplicación real con estos datos.

codescribblr codescribblr
26 mar 2016 20:23:06
0
-1

Según la documentación de WP_Meta_Query, puedes usar el argumento compare dentro del argumento meta_query de WP_Query. Sin embargo, solo puedes comparar con el value y no con el key, por lo que quizás quieras reconsiderar cómo estructuras esto.

Un argumento like se vería así:

$arguments = array(
    'meta_query' => array(
        array(
            'key' => 'foo',
            'value' => 'ba',
            'compare' => 'LIKE'
        )
    )
);

$query = new WP_Query($arguments);

Dado que no puedes hacer una búsqueda 'LIKE' en el key, te sugiero que añadas los posts que gustan en el meta del usuario y hagas una búsqueda con WP_User_Query para encontrar usuarios que les haya gustado ese post:

$arguments = array(
    'meta_query' => array(
        array(
            'key' => 'liked_post',
            'value' => '<post_id>'
        )
    )
);

$users = new WP_User_Query($arguments);
26 mar 2016 05:39:55