meta_query con valores meta como serializar arrays

36

Estoy trabajando en un proyecto en el que estoy creando un tipo de publicación personalizado y los datos personalizados ingresados a través de meta box asociados con mi tipo de publicación personalizada. Por la razón que sea, decidí codificar las meta cajas de tal manera que las entradas en cada metabox sean parte de una matriz. Por ejemplo, estoy almacenando longitud y latitud:

<p> 
    <label for="latitude">Latitude:</label><br /> 
    <input type="text" id="latitude" name="coordinates[latitude]" class="full-width" value="" /> 
</p> 
<p>     
    <label for="longitude">Longitude:</label><br /> 
    <input type="text" id="longitude" name="coordinates[longitude]" class="full-width" value="" /> 
</p>

Por la razón que sea, me gustó la idea de tener una entrada singular postmeta para cada metabox. En el gancho save_post , guardo los datos así:

update_post_meta($post_id, '_coordinates', $_POST['coordinates']);

Hice esto porque tengo tres metaboxes y me gusta tener solo 3 valores postmeta para cada publicación; Sin embargo, ahora me he dado cuenta de un problema potencial con esto. Es posible que desee utilizar WP_Query para extraer solo ciertas publicaciones basadas en estos valores meta. Por ejemplo, es posible que desee obtener todas las publicaciones que tengan valores de latitud superiores a 50. Si tuviera estos datos en la base de datos individualmente, tal vez utilizando la clave latitude , haría algo como:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>'
        )
    )
 );
$query = new WP_Query( $args );

Ya que tengo la latitud como parte de _coordinates postmeta, esto no funcionaría.

Entonces, mi pregunta es, ¿hay alguna forma de utilizar meta_query para consultar una matriz serializada como la que tengo en este escenario?

    
pregunta tollmanz 09.05.2011 - 04:06

12 respuestas

35

No, no es posible.

Le recomiendo encarecidamente que no realice la serialización de sus datos y modifique su rutina de guardado. Algo similar a esto debería convertir sus datos al nuevo formato:

$args = array(
    'post_type' => 'my-post-type',
    'meta_key' => '_coordinates',
    'posts_per_page' => -1
 );
$query = new WP_Query( $args );
if($query->have_posts()){
    while($query->have_posts()){
        $query->the_post();
        $c = get_post_meta($post->id,'_coordinates',true);
        add_post_meta($post->ID,'_longitude',$c['longitude']);
        add_post_meta($post->ID,'_latitude',$c['latitude']);
        delete_post_meta($post->ID,'_coordinates',$c);
    }
}

Entonces podrás consultar como quieras con teclas individuales

Si necesita almacenar múltiples longitudes y latitudes múltiples, puede almacenar múltiples meta de publicación con el mismo nombre. Simplemente use el tercer parámetro de get_post_meta , y los devolverá a todos como una matriz

¿Por qué no puede consultar datos serializados?

MySQL lo ve como una simple cadena y no puede dividirse en datos estructurados. Romperlo en datos estructurados es exactamente lo que hace el código anterior

Es posible que pueda consultar por partes parciales de la fecha, pero esto será muy poco confiable, costoso, lento y muy frágil, con muchos casos de borde. Los datos serializados no están destinados a consultas SQL, y no se formatean de manera regular y constante.

Aparte de los costos de las búsquedas de cadenas parciales, las metacontasiones posteriores son lentas y los datos serializados pueden cambiar dependiendo de la cantidad de contenido, lo que hace que la búsqueda sea increíblemente costosa, si no imposible, dependiendo del valor que esté buscando

Una nota sobre el almacenamiento de registros / entidades / objetos como objetos serializados en Meta

Es posible que desee almacenar un registro de transacción en meta meta, o algún otro tipo de estructura de datos en meta de usuario, y luego encontrar el problema anterior.

La solución aquí no es dividirlo en meta meta individual, sino darse cuenta de que, para empezar, nunca debería haber sido meta, sino un tipo de publicación personalizada. Por ejemplo, un registro o registro puede ser un tipo de publicación personalizada, con la publicación original como padre o unida a través de un término de taxonomía

    
respondido por el Tom J Nowell 20.08.2012 - 17:19
23

También me encuentro con esta situación. Aquí lo que hice:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => sprintf(':"%s";', $value),
            'compare' => 'LIKE'
        )
    )
);

Espero esta ayuda

    
respondido por el rabni 16.04.2015 - 14:38
10

Realmente perderá la capacidad de consultar sus datos de manera eficiente al serializar entradas en la base de datos de WP.

El ahorro de rendimiento general y la ganancia que cree que está logrando mediante la serialización no se notará en gran medida. Es posible que obtenga un tamaño de base de datos ligeramente más pequeño, pero el costo de las transacciones de SQL será elevado si alguna vez consulta esos campos y trata de compararlos de alguna manera útil y significativa.

En su lugar, guarde la serialización para los datos que no pretende consultar en esa naturaleza, sino que solo accederá de forma pasiva mediante la llamada directa a la API de WP get_post_meta() : desde esa función puede desempaquetar una entrada serializada para acceder sus propiedades de matriz también.

De hecho, se asignó el valor de verdadero como en;

$meta = get_post_meta( $post->ID, 'key', true );

Devolverá los datos como una matriz, a la que podrá acceder para iterar según lo normal.

Puede concentrarse en otras optimizaciones de bases de datos / sitios, como almacenamiento en caché, CSS y JS, y usar dichos servicios como CDN si lo necesita. Para nombrar solo algunos ... El Códice de WordPress es un buen punto de partida para descubrir más sobre ese tema: AQUÍ

    
respondido por el userabuser 20.08.2012 - 16:36
3

Acabo de lidiar con campos serializados y puedo consultarlos. No usar el meta_query pero usar una consulta SQL.

global $wpdb; 

$search = serialize('latitude').serialize(50);

$query = $wpdb->prepare("SELECT 'post_id'
FROM 'wp_postmeta'
WHERE 'post_id' IN (SELECT 'ID' FROM 'wp_posts' WHERE 'post_type' = 'my-post-type')
AND 'meta_key' = '_coordinates'
AND 'meta_value' LIKE '%s'",'%'.$search.'%');

$ids = $wpdb->get_col($query);

$args = array(
    'post__in' => $ids
    'post_type' => 'team' //add the type because the default will be 'post'
);

$posts = get_posts($args);

La consulta busca primero la publicación con el tipo de publicación correspondiente, por lo que la cantidad de registros de wp_postmeta será menor para filtrar. Luego agregué una declaración where para reducir aún más las filas al filtrar en meta_key

Los ID terminan bien en una matriz según sea necesario para get_posts.

PS. MySQL v5.6 o superior es necesario para un buen rendimiento de subconsulta

    
respondido por el Tomas 22.08.2013 - 11:41
1

Este ejemplo realmente me ayudó. Es específicamente para el complemento S2Members (que serializa los metadatos del usuario). Pero le permite consultar una parte de una matriz serializada dentro de la meta_key.

Funciona usando la función REGEXP de MySQL.

Aquí está la fuente

Aquí está el código que consulta a todos los usuarios que viven en los Estados Unidos. Lo modifiqué fácilmente para consultar uno de mis campos de registro personalizados y lo hice funcionar en poco tiempo.

  <?php
global $wpdb;
$users = $wpdb->get_results ("SELECT 'user_id' as 'ID' FROM '" . $wpdb->usermeta . 
          "' WHERE 'meta_key' = '" . $wpdb->prefix . "s2member_custom_fields' AND 
           'meta_value' REGEXP '.*\"country_code\";s:[0-9]+:\"US\".*'");
if (is_array ($users) && count ($users) > 0)
    {
        foreach ($users as $user)
            {
                $user = /* Get full User object now. */ new WP_User ($user->ID);
                print_r($user); /* Get a full list of properties when/if debugging. */
            }
    }
?>
    
respondido por el BC Smith 12.12.2012 - 19:43
1

Creo que hay 2 soluciones que pueden tratar de resolver el problema de los resultados que se almacenan como Cadena y Enteros. Sin embargo, es importante decir, como han señalado otros, que no es posible garantizar la integridad de los resultados almacenados como Integer, porque como estos valores se almacenan como matrices serializadas, el índice y los valores se almacenan exactamente con el mismo patrón. Ejemplo:

array(37,87);

se almacena como una matriz serializada, como esta

a:2:{i:0;i:37;i:1;i:87;}

Note el i:0 como la primera posición de la matriz y i:37 como el primer valor. El patrón es el mismo. Pero vayamos a las soluciones

1) Solución REGEXP

Esta solución funciona para mí independientemente del valor meta que se esté guardando como cadena o número / id. Sin embargo, utiliza REGEXP , que no es tan rápido como usar LIKE

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '\;i\:' . $value . '\;|\"' . $value . '\";',
            'compare' => 'REGEXP'
        )
    )
);

2) Solución LIKE

No estoy seguro de la diferencia de rendimiento, pero esta es una solución que usa LIKE y también funciona tanto para números como para cadenas

 $args = array(
        'post_type' => 'my-post-type',
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key' => 'latitude',
                'value' => sprintf(':"%s";', $value),
                'compare' => 'LIKE'
            ),
            array(
                'key' => 'latitude',
                'value' => sprintf(';i:%d;', $value),
                'compare' => 'LIKE'
            )
        )
    );
    
respondido por el Pablo S G Pacheco 29.03.2018 - 21:07
0

Después de leer un montón de sugerencias para ejecutar un filtrado WP_Query mediante arreglos serializados, he aquí cómo lo hice finalmente: creando una matriz de valores separados por comas usando implode junto con una consulta SQL personalizada $wpdb utilizando FIND_IN_SET para buscar en la lista separada por comas el valor solicitado.

(esto es similar a la respuesta de Tomas, pero es un poco menos intensivo en rendimiento para la consulta SQL)

1. En functions.php:

En el archivo functions.php (o donde esté configurando el meta box) en el uso de la función yourname_save_post()

update_post_meta($post->ID, 'checkboxArray', implode(",", $checkboxArray)); //adding the implode

para crear la matriz que contiene valores separados por comas.

También querrá cambiar la variable de salida en la función de construcción de meta box de yourname_post_meta() admin a

$checkboxArray = explode(",", get_post_custom($post->ID)["checkboxArray"][0]); //adding the explode

2. En la plantilla del archivo PHP:

Prueba: si ejecuta un get_post_meta( $id ); debería ver checkboxArray como una matriz que contiene sus valores separados por comas en lugar de una matriz serializada.

Ahora, construimos nuestra consulta SQL personalizada usando $wpdb .

global $wpdb;

$search = $post->ID;

$query = "SELECT * FROM wp_posts
          WHERE FIND_IN_SET( $search, (
              SELECT wp_postmeta.meta_value FROM wp_postmeta
              WHERE wp_postmeta.meta_key = 'blogLocations'
              AND wp_postmeta.post_id = wp_posts.ID )
          )
          AND ( wp_posts.post_type = 'post' )
          AND ( wp_posts.post_status = 'publish' );";

$posts = $wpdb->get_results($query);

foreach ($posts as $post) {
    //your post content here
}

Note que FIND_IN_SET , ahí es donde ocurre la magia.

Ahora ... ya que estoy usando SELECT * , esto devuelve todos los datos de la publicación y dentro del foreach puede hacer un eco de lo que desea (haga un print_r($posts); si no sabe lo que está incluido. No configura "el bucle" para usted (lo prefiero de esta manera), pero se puede modificar fácilmente para configurar el bucle si lo prefiere (mire setup_postdata($post); en el códice, es probable que deba cambiar SELECT * para seleccionar solo las ID de publicación y $wpdb->get_results al tipo $wpdb correcto - vea el códice para $wpdb también para obtener información sobre que tema).

Whelp, tomó un poco de esfuerzo, pero como wp_query no admite hacer 'compare' => 'IN' serializado o valores separados por comas, ¡esta shim es tu mejor opción!

Espero que esto ayude a alguien.

    
respondido por el Gifford N. 22.01.2015 - 23:10
0

Si usa el operador de comparación like en su meta consulta, debería funcionar bien para mirar dentro de una matriz serializada.

$wp_user_search = new WP_User_Query(array(
    'meta_query' => array(
        array(
            'key'     => 'wp_capabilities',
            'value'   => 'subscriber',
            'compare' => 'not like'
            )
        )
    )
);

resultados en:

[query_where] => WHERE 1=1 AND (
  ( wp_usermeta.meta_key = 'wp_capabilities' 
  AND CAST(wp_usermeta.meta_value AS CHAR) NOT LIKE '%subscriber%' )
    
respondido por el benklocek 03.07.2015 - 23:14
0

Si mis metadatos son de tipo matriz, usaré este método para la consulta por meta:

$args = array(
    'post_type' => 'fotobank',
    'posts_per_page' => -1,
    'meta_query' => array(
            array(
                   'key' => 'collections',
                   'value' => ':"'.$post->ID.'";',
                   'compare' => 'LIKE'
            )
     )
);
$fotos = new WP_Query($args);
    
respondido por el Den Media 07.07.2016 - 08:50
0

Tengo curiosidad por las respuestas anteriores, donde meta_query apuntó a la clave latitude en lugar de _coordinates . Tenía que ir y probar si realmente era posible en las consultas de metadatos dirigirse a una clave específica dentro de una matriz serializada. :)

Eso obviamente no fue el caso.

Entonces, tenga en cuenta que la clave correcta para apuntar es _coordinates en lugar de latitude .

$args = array(
     'post_type' => 'my-post-type',
     'meta_query' => array(
         array(
             'key' => '_coordinates',
             'value' => sprintf(':"%s";', $value),
             'compare' => 'LIKE'
         )
     )
 );

NOTAS:

  1. Este enfoque solo permite orientar coincidencias exactas. Entonces, cosas como todas las latitudes mayores de 50 no son posibles.

  2. Para incluir coincidencias de subcadenas, se podría usar 'value' => sprintf(':"%%%s%%";', $value), . (no ha probado)

respondido por el jgangso 06.07.2017 - 13:57
-1

Tengo la misma pregunta. Tal vez usted necesita el parámetro 'tipo'? Echa un vistazo a esta pregunta relacionada: Consulta de campo personalizado: el valor meta es una matriz

Quizás intente:

    $args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>',
            'type' => 'numeric'
        )
    )
    );
    
respondido por el user4356 12.05.2011 - 19:44
-1

Me encontré con algo similar mientras utilizaba el plugin Magic Fields. Esto podría hacer el truco

$values_serialized = serialize(array('50'));
$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => $values_serialized,
            'compare' => '>'
        )
    )
);
    
respondido por el Seth Stevenson 12.05.2011 - 23:10

Lea otras preguntas en las etiquetas