Usar REGEXP en la clave meta_query de WP_Query
Sé que puedo usar REGEXP en WP_Query de esta forma:
$query = new WP_Query(array(
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'custom_fields',
'value' => 'foo[(][0-9][)]', // con expresiones regulares
'compare' => 'REGEXP',
),
),
));
Pero necesito usar expresiones regulares también en la clave. Algo así:
$query = new WP_Query(array(
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'custom_fields[(][0-9][)]', // con expresiones regulares
'value' => 'foo',
'compare' => 'REGEXP',
),
),
));
¿Existe alguna forma de lograr esto quizás con un filtro? ¿O tengo que construirlo todo yo mismo con una consulta SQL personalizada?

Aquí tienes una idea experimental:
Supongamos que tenemos:
post A con el campo personalizado
location1
como UK - Londonpost B con el campo personalizado
location2
como France - Parispost C con el campo personalizado
location3
como USA - New York
Entonces podríamos usar, por ejemplo:
$args = [
'meta_query' => [
'relation' => 'OR',
[
'key' => "^location[0-9]",
'_key_compare' => 'REGEXP',
'value' => 'London',
'compare' => 'LIKE',
],
[
'key' => 'location%',
'_key_compare' => 'LIKE',
'value' => 'Paris',
'compare' => 'LIKE'
],
[
'key' => 'location3',
'value' => 'New York',
'compare' => 'LIKE'
]
]
];
donde admitimos el argumento personalizado _key_compare
con el siguiente plugin:
<?php
/**
* Plugin Name: Extended Meta Key Search In WP_Query
* Description: Custom '_key_compare' argument as REGEXP, RLIKE or LIKE
* Plugin URI: http://wordpress.stackexchange.com/a/193841/26350
* Plugin Author: Birgir Erlendsson (birgire)
* Version: 0.0.3
*/
add_action( 'pre_get_posts', function( $q )
{
// Verificar la meta query:
$mq = $q->get( 'meta_query' );
if( empty( $mq ) )
return;
// Inicialización:
$marker = '___tmp_marker___';
$rx = [];
// Recopilar todas las sub meta queries que usan REGEXP, RLIKE o LIKE:
foreach( $mq as $k => $m )
{
if( isset( $m['_key_compare'] )
&& in_array( strtoupper( $m['_key_compare'] ), [ 'REGEXP', 'RLIKE', 'LIKE' ] )
&& isset( $m['key'] )
) {
// Marcar la clave con una cadena única para asegurar los reemplazos posteriores:
$m['key'] .= $marker . $k; // Hacer que el marcador temporal añadido sea único
// Modificar la variable de consulta original correspondiente:
$q->query_vars['meta_query'][$k]['key'] = $m['key'];
// Recopilarlo:
$rx[$k] = $m;
}
}
// Nada que hacer:
if( empty( $rx ) )
return;
// Obtener acceso al SQL generado de la meta query:
add_filter( 'get_meta_sql', function( $sql ) use ( $rx, $marker )
{
// Ejecutar solo una vez:
static $nr = 0;
if( 0 != $nr++ )
return $sql;
// Modificar la parte WHERE donde reemplazamos los marcadores temporales:
foreach( $rx as $k => $r )
{
$sql['where'] = str_replace(
sprintf(
".meta_key = '%s' ",
$r['key']
),
sprintf(
".meta_key %s '%s' ",
$r['_key_compare'],
str_replace(
$marker . $k,
'',
$r['key']
)
),
$sql['where']
);
}
return $sql;
});
});
donde añadimos marcadores únicos en cada meta clave para los reemplazos de cadenas.
Nota que esto no admite el escape de caracteres regex, como \(
y \\
.

Me encantan estos parámetros personalizados adicionales y divertidos que estás añadiendo. ;-)

Quizás como los ayudantes de Santa - usualmente hacen que todo funcione mágicamente al final ;-) @PieterGoosen

Wow amigo... esto es increíble. Pero desafortunadamente no está funcionando :( Tengo nombres de claves como custom_field_language(0)language
en mi base de datos y mi expresión regular custom_field_languages[(][0-9][)]language
no funciona con tu plugin. ¿Alguna idea?

parece que la función replace al final de tu plugin no está funcionando correctamente.

gracias, lo he solucionado. Simplemente usé $count++
en una construcción de cadena cuando olvidé que tenía que usar $count
dos veces ;-) No recomendaría usar caracteres especiales en los nombres de las claves, aparte del guion bajo. Estás usando paréntesis en tu clave. Tienen un significado especial con expresiones regulares. Así que tendrías que escaparlos con \(
y \)
en REGEXP
o RLIKE
, pero el escape no está soportado en mi plugin. Podrías probar con LIKE
en su lugar usando custom_field_language(%)language
. @PhilippKühn

sí. muchas gracias. ¡tu código corregido funciona! pero hay otro problema. si uso otro meta_query siempre obtengo cero resultados con cualquier relación. como este 'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'custom_field_location',
'value' => 'Berlin',
'compare' => '=',
),
array(
'key' => 'custom_field_languages[(][0-9][)]language',
'_key_compare' => 'REGEXP',
'value' => 'ak',
'compare' => 'LIKE',
),
)

si imprimo $sql['where']
puedo ver que cada clave se reemplaza con la de REGEXP. @birgire

Esto fue otro problema con $count
;-) Por favor revisa la respuesta actualizada, donde uso una clave de array en su lugar. @PhilippKühn

oh no... descubrí que esto no soporta estas nuevas consultas meta anidadas introducidas en wp 4.1 :( una historia interminable... @birgire

aja, buen punto, tal vez solo lo subiré a GitHub en las próximas semanas e intentaré extenderlo allí ;-) @PhilippKühn

¡Esto sería genial! Hasta entonces he encontrado una solución alternativa un poco chapucera. Pero estaría encantado de usar tu script porque es una forma mucho más limpia. @birgire

@birgire ¿alguna vez pensaste en subir esto a GitHub, colega?

Tu respuesta funciona perfectamente en el primer nivel del array, por ejemplo:
$args['meta_query'][] = array(
'key' => 'tour_itinerario_ciudades_repeater_%_tour_ciudades_nombre',
'_key_compare' => 'LIKE',
'value' => 'MEXICO',
'compare' => 'LIKE',
);
Necesito hacer algunas modificaciones para que funcione en el segundo nivel del array:
$args['meta_query'][] = array(
'relation' => 'OR',
array(
'key' => 'tour_itinerario_ciudades_repeater_%_tour_ciudades_nombre',
'_key_compare' => 'LIKE',
'value' => 'CONDESA',
'compare' => 'LIKE',
),
array(
'key' => 'tour_itinerario_ciudades_repeater_%_tour_ciudades_nombre',
'_key_compare' => 'LIKE',
'value' => 'Ciudad 1',
'compare' => 'LIKE',
)
);
Ahora,
add_action('pre_get_posts', function( $q ) {
// Verificar la meta query:
$mq = $q->get('meta_query');
if (empty($mq))
return;
// Inicializar:
$marker = '___tmp_marker___';
$rx = [];
// Recolectar todas las sub meta queries que usan REGEXP, RLIKE o LIKE:
// Solo funciona para el primer nivel del array
foreach ($mq as $k => $m) {
if (isset($m['_key_compare']) && in_array(strtoupper($m['_key_compare']), [ 'REGEXP', 'RLIKE', 'LIKE']) && isset($m['key'])
) {
// Marcar la clave con un string único para asegurar los reemplazos posteriores:
$m['key'] .= $marker . $k; // Hacer único el marcador temporal añadido
// Modificar la variable de consulta original correspondiente:
$q->query_vars['meta_query'][$k]['key'] = $m['key'];
// Recolectarla:
$rx[$k] = $m;
}
}
// código personalizado para hacerlo funcionar con argumentos en arrays multidimensionales
foreach ($mq as $k => $m) {
foreach ($m as $k_i => $m_i) {
if (count($m_i) >= 3) {
if (isset($m_i['_key_compare']) && in_array(strtoupper($m_i['_key_compare']), [ 'REGEXP', 'RLIKE', 'LIKE']) && isset($m_i['key'])
) {
// Marcar la clave con un string único para asegurar los reemplazos posteriores:
$m_i['key'] .= $marker . $k_i; // Hacer único el marcador temporal añadido
// Modificar la variable de consulta original correspondiente:
$q->query_vars['meta_query'][$k][$k_i]['key'] = $m_i['key'];
// Recolectarla:
$rx[$k][$k_i] = $m_i;
}
}
}
}
// Nada que hacer:
if (empty($rx))
return;
// Obtener acceso al SQL generado de la meta query:
add_filter('get_meta_sql', function( $sql ) use ( $rx, $marker ) {
// Ejecutar solo una vez:
static $nr = 0;
if (0 != $nr++)
return $sql;
// Modificar la parte WHERE donde reemplazamos los marcadores temporales:
//PRIMER NIVEL
foreach ($rx as $k => $r) {
$sql['where'] = str_replace(
sprintf(
".meta_key = '%s' ", $r['key']
), sprintf(
".meta_key %s '%s' ", $r['_key_compare'], str_replace(
$marker . $k, '', $r['key']
)
), $sql['where']
);
}
//SEGUNDO NIVEL
foreach ($rx as $k => $r) {
//TODO: probar con varios casos ya que puede tener errores
if (!isset($r['key'])) {//FORZAR LA ENTRADA
foreach ($r as $k_i => $r_i) {
$sql['where'] = str_replace(
sprintf(
".meta_key = '%s' ", $r_i['key']
), sprintf(
".meta_key %s '%s' ", $r_i['_key_compare'], str_replace(
$marker . $k_i, '', $r_i['key']
)
), $sql['where']
);
}
}
}
var_dump($sql);
return $sql;
});
}); Solo por si alguien necesita una respuesta similar,
GRACIAS DE NUEVO
