¿Cómo extender WP_Query para incluir una tabla personalizada en la consulta?

26 abr 2012, 15:49:13
Vistas: 48.2K
Votos: 39

He estado varios días con este problema. Inicialmente era sobre cómo almacenar los datos de seguidores de un usuario en la base de datos, para lo cual recibí un par de buenas recomendaciones aquí en WordPress Answers. Después de seguir las recomendaciones, he añadido una nueva tabla así:

id  leader_id   follower_id
1   2           4
2   3           10
3   2           10

En la tabla anterior, la primera fila tiene un usuario con ID 2 que está siendo seguido por un usuario con ID 4. En la segunda fila, un usuario con ID 3 está siendo seguido por un usuario con ID 10. La misma lógica se aplica para la tercera fila.

Ahora, esencialmente quiero extender WP_Query para poder limitar los posts recuperados a aquellos que son únicamente de los líderes de un usuario. Entonces, tomando en cuenta la tabla anterior, si pasara el ID de usuario 10 a WP_Query, los resultados deberían contener solo posts del usuario con ID 2 y del usuario con ID 3.

He buscado mucho tratando de encontrar una respuesta. Tampoco he visto ningún tutorial que me ayude a entender cómo extender la clase WP_Query. He visto las respuestas de Mike Schinkel (extendiendo WP_Query) a preguntas similares, pero realmente no he entendido cómo aplicarlo a mis necesidades. Sería genial si alguien pudiera ayudarme con esto.

Enlaces a las respuestas de Mike según lo solicitado: Enlace 1, Enlace 2

7
Comentarios

Agrega un enlace a las respuestas de Mike, por favor.

kaiser kaiser
26 abr 2012 15:57:41

¿puedes dar un ejemplo de lo que estarías consultando? WP_Query es para obtener posts, y no logro entender cómo esto se relaciona con posts.

mor7ifer mor7ifer
26 abr 2012 15:58:43

@kaiser He actualizado la pregunta con enlaces a las respuestas de Mike.

John John
26 abr 2012 16:01:41

@m0r7if3r »Quiero extender WP_Query para poder limitar los posts recuperados solo a los del líder/líderes de un usuario«, similar a "obtener posts por autor".

kaiser kaiser
26 abr 2012 16:04:44

@John El problema es: ¿Cómo determinarías quién es el líder real? Supongo que puedes seguir a varias personas...

kaiser kaiser
26 abr 2012 16:05:35

@m0r7if3r Los posts son exactamente lo que necesito consultar. Pero los posts a recuperar deben ser de usuarios que estén listados como líderes de cierto usuario en la tabla personalizada. En otras palabras, quiero decirle a WP_Query que recupere todos los posts de todos los usuarios que sean líderes de un usuario con ID '10' en la tabla personalizada.

John John
26 abr 2012 16:08:46

@kaiser Ciertamente, un usuario podría seguir a muchos y por lo tanto el número de líderes para un usuario podría ser muchos. Así que, la idea es recuperar todas las publicaciones de todos los líderes de un usuario. No estoy seguro si get_posts_by_author SQL funciona con múltiples autores.

John John
26 abr 2012 16:15:01
Mostrar los 2 comentarios restantes
Todas las respuestas a la pregunta 5
0
23

Descargo de responsabilidad importante: la forma correcta de hacer esto NO es modificando la estructura de tu tabla, sino usando wp_usermeta. Así no necesitarás crear consultas SQL personalizadas para consultar tus posts (aunque aún necesitarás algo de SQL personalizado para obtener una lista de todos los que reportan a un supervisor particular - en la sección de Administración, por ejemplo). Sin embargo, dado que el OP preguntó sobre escribir SQL personalizado, aquí está la mejor práctica actual para inyectar SQL personalizado en una consulta existente de WordPress.

Si estás haciendo joins complejos, no puedes simplemente usar el filtro posts_where, porque necesitarás modificar el join, el select, y posiblemente las secciones group by u order by de la consulta también.

Tu mejor opción es usar el filtro 'posts_clauses'. Este es un filtro muy útil (¡que no debe ser abusado!) que te permite añadir/modificar las diversas partes del SQL que es generado automáticamente por las muchas líneas de código en el núcleo de WordPress. La firma del callback del filtro es: function posts_clauses_filter_cb( $clauses, $query_object ){ } y espera que devuelvas $clauses.

Las Cláusulas

$clauses es un array que contiene las siguientes claves; cada clave es una cadena SQL que será usada directamente en la sentencia SQL final enviada a la base de datos:

  • where
  • groupby
  • join
  • orderby
  • distinct
  • fields
  • limits

Si estás añadiendo una tabla a la base de datos (solo haz esto si absolutamente no puedes aprovechar post_meta, user_meta o taxonomías) probablemente necesitarás tocar más de una de estas cláusulas, por ejemplo, fields (la parte "SELECT" de la sentencia SQL), join (todas tus tablas, además de la de tu cláusula "FROM"), y quizás orderby.

Modificando las Cláusulas

La mejor manera de hacer esto es hacer una subreferencia de la clave relevante del array $clauses que obtuviste del filtro:

$join = &$clauses['join'];

Ahora, si modificas $join, en realidad estarás modificando directamente $clauses['join'] así que los cambios estarán en $clauses cuando lo devuelvas.

Preservando las Cláusulas Originales

Es probable (no, en serio, presta atención) que quieras preservar el SQL existente que WordPress generó para ti. Si no, probablemente deberías mirar el filtro posts_request en su lugar - esa es la consulta mySQL completa justo antes de ser enviada a la base de datos, así que puedes sobrescribirla completamente con la tuya. ¿Por qué querrías hacer esto? Probablemente no quieras.

Así que, para preservar el SQL existente en las cláusulas, recuerda añadir a las cláusulas, no asignarles (es decir: usa $join .= ' {NUEVAS COSAS SQL}'; no $join = '{SOBRESCRIBIR COSAS SQL}';. Nota que como cada elemento del array $clauses es una cadena, si quieres añadirle, probablemente querrás insertar un espacio antes de cualquier otro token de caracteres, de lo contrario probablemente crearás algún error de sintaxis SQL.

Puedes asumir que siempre habrá algo en cada una de las cláusulas, y así recordar empezar cada nueva cadena con un espacio, como en: $join .= ' my_table, o, siempre puedes añadir una pequeña línea que solo añada un espacio si lo necesitas:

$join = &$clauses['join'];
if (! empty( $join ) ) $join .= ' ';
$join .= "JOIN my_table... "; // <-- nota el espacio al final
$join .= "JOIN my_other_table... ";

return $clauses;

Eso es más una cuestión de estilo que otra cosa. Lo importante es recordar: ¡siempre deja un espacio ANTES de tu cadena si estás añadiendo a una cláusula que ya tiene algo de SQL!

Uniendo todo

La primera regla del desarrollo en WordPress es intentar usar tanta funcionalidad del núcleo como puedas. Esta es la mejor manera de asegurar que tu trabajo siga funcionando en el futuro. Supón que el equipo del núcleo decide que WordPress ahora usará SQLite u Oracle u otro lenguaje de base de datos. ¡Cualquier mySQL escrito a mano puede volverse inválido y romper tu plugin o tema! Mejor dejar que WP genere tanto SQL como sea posible por sí mismo, y solo añadir los bits que necesitas.

Así que lo primero es aprovechar WP_Query para generar tanto de tu consulta base como sea posible. El método exacto que usamos para hacer esto depende en gran medida de dónde se supone que aparece esta lista de posts. Si es una subsección de la página (no tu consulta principal) usarías get_posts(); si es la consulta principal, supongo que podrías usar query_posts() y terminarlo, pero la forma correcta de hacerlo es interceptar la consulta principal antes de que llegue a la base de datos (y consuma ciclos del servidor) así que usa el filtro request.

Bien, así que has generado tu consulta y el SQL está a punto de ser creado. Bueno, de hecho, ya ha sido creado, solo no enviado a la base de datos. Al usar el filtro posts_clauses, vas a añadir tu tabla de relaciones de empleados a la mezcla. Llamemos a esta tabla {$wpdb->prefix} . 'user_relationship', y es una tabla de intersección. (Por cierto, recomiendo que generalices esta estructura de tabla y la conviertas en una tabla de intersección adecuada con los siguientes campos: 'relationship_id', 'user_id', 'related_user_id', 'relationship_type'; esto es mucho más flexible y potente... pero me desvío).

Si entiendo lo que quieres hacer, quieres pasar un ID de Líder y luego ver solo los posts de los Seguidores de ese Líder. Espero haber entendido bien. Si no es correcto, tendrás que tomar lo que digo y adaptarlo a tus necesidades. Seguiré con tu estructura de tabla: tenemos un leader_id y un follower_id. Así que el JOIN será en {$wpdb->posts}.post_author como clave foránea al 'follower_id' en tu tabla 'user_relationship'.

add_filter( 'posts_clauses', 'filter_by_leader_id', 10, 2 ); // necesitamos el 2 porque queremos obtener todos los argumentos

function filter_by_leader_id( $clauses, $query_object ){
  // No sé cómo pretendes pasar el leader_id, así que asumamos que es un global
  global $leader_id;

  // En este ejemplo solo quiero afectar una consulta en la página de inicio.
  // Aquí es donde se usa $query_object, para ayudarnos a evitar afectar
  // TODAS las consultas (ya que TODAS las consultas pasan por este filtro)
  if ( $query_object->is_home() ){
    // Ahora, añadamos tu tabla al SQL
    $join = &$clauses['join'];
    if (! empty( $join ) ) $join .= ' '; // añade un espacio solo si es necesario (¡para nota!)
    $join .= "JOIN {$wpdb->prefix}employee_relationship EMP_R ON EMP_R.follower_id = {$wpdb->posts}.author_id";

    // Y asegurémonos de añadirlo a nuestros criterios de selección
    $where = &$clauses['where'];
    // En cualquier caso, siempre empiezas con AND, porque siempre hay una declaración '1=1' como la primera declaración de la cláusula WHERE que WP añade.
    // ¡Solo no olvides el espacio inicial!
    $where .= " AND EMP_R.leader_id={$leader_id}"; // asumiendo que $leader_id es siempre (int)

    // Y asumo que querrás los posts "agrupados" por id de usuario, así que modifiquemos la cláusula groupby
    $groupby = &$clauses['groupby'];
    // Necesitamos anteponer, así que...
    if (! empty( $groupby ) ) $groupby = ' ' . $groupby; // Para los presumidos
    $groupby = "{$wpdb->posts}.post_author" . $groupby;
  }

  // En cualquier caso, necesitamos devolver nuestras cláusulas...
  return $clauses;
}
2 may 2012 00:49:43
1
20

Estoy respondiendo a esta pregunta extremadamente tarde y me disculpo por ello. He estado demasiado ocupado con plazos de entrega para atender esto.

Un gran agradecimiento a @m0r7if3r y @kaiser por proporcionar las soluciones base que pude extender e implementar en mi aplicación. Esta respuesta proporciona detalles sobre mi adaptación de las soluciones ofrecidas por @m0r7if3r y @kaiser.

Primero, permítanme explicar por qué se hizo esta pregunta en primer lugar. A partir de la pregunta y los comentarios de la misma, se podría deducir que estoy intentando que WP_Query obtenga publicaciones de todos los usuarios (líderes) que un usuario dado (seguidor) sigue. La relación entre el seguidor y el líder se almacena en una tabla personalizada follow. La solución más común a este problema es extraer los ID de usuario de todos los líderes de un seguidor de la tabla follow y colocarlos en un array. Ver abajo:

global $wpdb;
$results = $wpdb->get_results($wpdb->prepare('SELECT leader_id FROM cs_follow WHERE follower_id = %s', $user_id));

foreach($results as $result)
    $leaders[] = $result->leader_id;

Una vez que tienes el array de líderes, podrías pasarlo como argumento a WP_Query. Ver abajo:

if (isset($leaders)) $authors = implode(',', $leaders); // Necesario ya que el argumento authors de WP_Query solo acepta una cadena con ID de autores de publicaciones separados por comas

$args = array(
    'post_type'         => 'post',
    'posts_per_page'    => 10,
    'author'            => $authors
);

$wp_query = new WP_Query( $args );

// El bucle normal de WordPress continúa

La solución anterior es la forma más sencilla de lograr los resultados deseados. Sin embargo, no es escalable. En el momento en que un seguidor sigue a decenas de miles de líderes, el array resultante de ID de líderes se volvería extremadamente grande y obligaría a tu sitio WordPress a usar 100MB - 250MB de memoria en cada carga de página, eventualmente colapsando el sitio. La solución al problema es ejecutar una consulta SQL directamente en la base de datos y obtener las publicaciones relevantes. Fue entonces cuando la solución de @m0r7if3r vino al rescate. Siguiendo la recomendación de @kaiser, me dispuse a probar ambas implementaciones. Importé alrededor de 47K usuarios desde un archivo CSV para registrarlos en una instalación de prueba limpia de WordPress. La instalación ejecutaba el tema Twenty Eleven. Luego, ejecuté un bucle for para que unos 50 usuarios siguieran a todos los demás. La diferencia en el tiempo de consulta para ambas soluciones fue asombrosa. La solución de @kaiser normalmente tomaba entre 2 y 5 segundos para cada consulta. La variación, supongo, ocurre porque WordPress almacena en caché las consultas para su uso posterior. Por otro lado, la solución de @m0r7if3r demostró un tiempo de consulta promedio de 0.02 ms. Para probar ambas soluciones, tenía la indexación ACTIVADA para la columna leader_id. Sin indexación, hubo un aumento dramático en el tiempo de consulta.

El uso de memoria con la solución basada en arrays fue de alrededor de 100-150 MB y bajó a 20 MB al ejecutar un SQL directo.

Tuve un problema con la solución de @m0r7if3r cuando necesitaba pasar el ID del seguidor a la función de filtro posts_where. Al menos, según mi conocimiento, WordPress no permite ningún medio para pasar una variable a las funciones de filtro. Puedes usar variables Globales, aunque quería evitarlas. Terminé extendiendo WP_Query para finalmente abordar el problema. Así que aquí está la solución final que implementé (basada en la solución de @m0r7if3r).

class WP_Query_Posts_by_Leader extends WP_Query {
    var $follower_id;

    function __construct($args=array()) {
        if(!empty($args['follower_id'])) {
            $this->follower_id = $args['follower_id'];
            add_filter('posts_where', array($this, 'posts_where'));
        }

        parent::query($args);
    }

    function posts_where($where) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'follow';
        $where .= $wpdb->prepare(" AND post_author IN (SELECT leader_id FROM " . $table_name . " WHERE follower_id = %d )", $this->follower_id);
        return $where;
    }
}


$args = array(
    'post_type'         => 'post',
    'posts_per_page'    => 10,
    'follower_id'       => $follower_id
);

$wp_query = new WP_Query_Posts_by_Leader( $args );

Nota: Finalmente probé la solución anterior con 1.2 millones de entradas en la tabla follow. El tiempo promedio de consulta se mantuvo alrededor de 0.060 ms.

22 may 2012 10:31:26
Comentarios

Nunca te dije cuánto aprecié la discusión sobre esta pregunta. Ahora que me di cuenta de que me la había perdido, he añadido un voto positivo :)

kaiser kaiser
28 feb 2014 23:44:04
11

Puedes hacer esto con una solución completamente en SQL usando el filtro posts_where. Aquí tienes un ejemplo de cómo hacerlo:

if( alguna condición ) 
    add_filter( 'posts_where', 'wpse50305_leader_where' );
    // lol, el ID de la pregunta es el mismo hacia adelante y hacia atrás

function wpse50305_leader_where( $where ) {
    $where .= $GLOBALS['wpdb']->prepare( ' AND post_author '.
        'IN ( '.
            'SELECT leader_id '.
            'FROM custom_table_name '.
            'WHERE follower_id = %s'.
        ' ) ', $follower_id );
    return $where;
}

Creo que también puede haber una forma de hacer esto con un JOIN, pero no se me ocurre cómo. Seguiré probando y actualizaré la respuesta si lo consigo.

Alternativamente, como sugirió @kaiser, puedes dividirlo en dos partes: obtener los líderes y hacer la consulta. Tengo la sensación de que esto podría ser menos eficiente, pero sin duda es la forma más comprensible de hacerlo. Tendrías que probar la eficiencia por ti mismo para determinar qué método es mejor, ya que las consultas SQL anidadas pueden volverse bastante lentas.

DE LOS COMENTARIOS:

Deberías colocar la función en tu functions.php y hacer el add_filter() justo antes de que se llame al método query() de WP_Query. Inmediatamente después, deberías hacer un remove_filter() para que no afecte a las demás consultas.

26 abr 2012 16:40:48
Comentarios

Editaste tu A y agregué prepare(). Espero que no te moleste la edición. Y sí: el rendimiento tiene que ser medido por OP. De todos modos: sigo pensando que esto debería ser simplemente usermeta y nada más.

kaiser kaiser
26 abr 2012 16:53:58

@m0r7if3r Gracias por intentar una solución. Acabo de publicar un comentario en respuesta a la respuesta de kaiser, con preocupaciones sobre posibles problemas de escalabilidad. Por favor, tómalo en consideración.

John John
26 abr 2012 16:54:08

@kaiser No me molesta en lo más mínimo, de hecho lo agradezco :)

mor7ifer mor7ifer
26 abr 2012 16:55:21

@m0r7if3r Gracias. Tener gente como tú en la comunidad es increíble :)

kaiser kaiser
26 abr 2012 16:59:43

@m0r7if3r Estuve probando esto y encuentro que hace que WP_Query devuelva todas las publicaciones. Por todas las publicaciones me refiero a publicaciones de todos los tipos (posts, adjuntos, páginas) de todos los usuarios. Este es el SQL que echo $wp_query->request; muestra para la consulta SQL_CALC_FOUND_ROWS cs_posts.* FROM cs_posts INNER JOIN cs_postmeta ON (cs_posts.ID = cs_postmeta.post_id) WHERE 1=1 GROUP BY cs_posts.ID ORDER BY cs_posts.post_date DESC LIMIT 0, 30

John John
26 abr 2012 22:17:48

Olvidé poner return $where;. ¿El código actualizado corrige el problema?

mor7ifer mor7ifer
26 abr 2012 22:33:34

¡Uf! Los errores tipográficos. Sí, ahora funciona. Ahora probaré implementar la solución de @kaiser y medir los tiempos. Por cierto, un efecto secundario que veo al usar esto es que comienza a afectar todas las consultas si hay múltiples en una sola página. Además, esto rompió las reescrituras de URL para mí cuando lo coloqué en functions.php. Mover el código justo antes de la llamada WP_Query eliminó la interferencia con las reescrituras de URL. Aunque no estoy seguro si colocarlo antes de WP_Query sería una mala práctica???

John John
26 abr 2012 23:14:55

Deberías poner la función en tu functions.php y hacer el add_filter() justo antes de que se llame al método query() de WP_Query. Inmediatamente después, deberías hacer remove_filter() para que no afecte a las otras consultas. No estoy seguro de cuál sería el problema con la reescritura de URL, he usado posts_where en muchas ocasiones y nunca he visto eso...

mor7ifer mor7ifer
26 abr 2012 23:21:42

@m0r7if3r Gracias por señalarlo. Continuando con las pruebas en los datos de muestra. Te mantendré informado.

John John
26 abr 2012 23:34:06

@m0r7if3r Tu último comentario sería una buena cita dentro de la Q :)

kaiser kaiser
27 abr 2012 14:30:27

Esta es definitivamente la mejor solución. ¡Bien hecho!

Travis van der Font Travis van der Font
5 oct 2015 16:58:07
Mostrar los 6 comentarios restantes
18

Etiqueta de Plantilla

Simplemente coloca ambas funciones en tu archivo functions.php. Luego ajusta la primera función y añade el nombre de tu tabla personalizada. Necesitarás algo de prueba/error para eliminar el ID del usuario actual dentro del array resultante (ver comentario).

/**
 * Obtener los "Líderes" del usuario actual
 * @param int $user_id El ID del usuario actual
 * @return array $query Los líderes
 */
function wpse50305_get_leaders( $user_id )
{
    global $wpdb;

    return $wpdb->query( $wpdb->prepare(
        "
            SELECT `leader_id`, `follower_id`
            FROM %s
                WHERE `follower_id` = %s
            ORDERBY `leader_id` ASC
        ",
        // Edita el nombre de la tabla
        "{$wpdb->prefix}nombre_tabla_personalizada",
        $user_id
    ) );
}

/**
 * Obtener un array de posts que contienen publicaciones  
 * de los "Líderes" que el usuario actual sigue
 * @return array $posts Posts que son del "Líder" actual
 */
function wpse50305_list_posts_by_leader()
{
    get_currentuserinfo();
    global $current_user;

    $user_id = $current_user->ID;

    $leaders = wpse50305_get_leaders( $user_id );
    // podría ser que necesites iterar sobre $leaders
    // y eliminar los IDs de seguidores

    return get_posts( array(
        'author' => implode( ",", $leaders )
    ) );
}

Dentro de la plantilla

Aquí puedes hacer lo que quieras con tus resultados.

foreach ( wpse50305_list_posts_by_leader() as $post )
{
    // haz algo con $post
}

NOTA No tenemos datos de prueba, etc. así que lo anterior es un poco de adivinanza. Asegúrate de que edites esta respuesta con lo que te funcionó, para tener un resultado satisfactorio para futuros lectores. Aprobaré la edición en caso de que tengas muy baja reputación. También puedes borrar esta nota después. Gracias.

26 abr 2012 16:28:29
Comentarios

Gracias por proporcionar una solución. Sin embargo, este enfoque tiene advertencias en términos de escalabilidad. El problema está en obtener el ID de los líderes como un array. El array podría volverse extremadamente grande dependiendo del número de usuarios que un usuario siga. He visto que esto consume una gran cantidad de memoria. Esto debe hacerse como una única consulta SQL que podría implicar realizar un JOIN en la tabla personalizada y la tabla de posts de WP y obtener los resultados. La mejor solución es extender WP_Query. El problema, no sé cómo hacerlo. Nuevamente gracias por intentarlo. Se agradecen comentarios.

John John
26 abr 2012 16:48:54

JOIN es mucho más costoso. Además: Como mencioné, no tenemos datos de prueba, así que por favor prueba ambas respuestas e ilumínanos con tus resultados.

kaiser kaiser
26 abr 2012 16:51:39

WP_Query en sí mismo trabaja con JOINs entre la tabla de posts y postmeta al realizar consultas. He visto que el uso de memoria en PHP aumenta a 70MB - 200MB por carga de página. Ejecutar algo así con muchos usuarios simultáneos requeriría una infraestructura extrema. Mi suposición sería que, dado que WordPress ya implementa una técnica similar, los JOINs deberían ser menos gravosos en comparación con trabajar con un array de IDs.

John John
26 abr 2012 17:00:33

No, lo siento, pero no. También hay intentos de eliminar los JOIN adicionales. Ejemplo: WP añade un JOIN de la tabla de términos al consultar múltiples taxonomías, lo cual es contraproducente para el rendimiento. Simplemente usa timer_start() antes de la consulta y timer_stop() después para medir la diferencia.

kaiser kaiser
26 abr 2012 17:04:32

Además: Asegúrate de tener el INDEX en la columna correcta. Esto hace una diferencia enorme en el rendimiento. En un proyecto que realicé, donde necesitaba hacer consultas por lat/lng, tengo una tabla adicional que es pequeña, bien indexada y funciona mucho más rápido que cualquier JOIN que probé. Para un conocimiento más profundo sobre los JOINs, lee esto.

kaiser kaiser
26 abr 2012 17:07:20

Ah, y sí, también depende de qué JOIN estés usando...

kaiser kaiser
26 abr 2012 17:09:12

¿Entonces tu consejo sería quedarse con el array en lugar de ejecutar un SQL con join, correcto?

John John
26 abr 2012 17:13:46

Sí, pero como dijo @m0r7if3r: Mídelo. Te di los enlaces. Solo tienes que cronometrarlo y mostrarnos qué obtuviste. Si no lo haces, bueno, entonces hemos hecho muchas bonitas suposiciones :)

kaiser kaiser
26 abr 2012 17:32:57

Definitivamente estoy probando ambas soluciones. Pero, para cronometrar esto, tendré que volcar la base de datos con datos de prueba, lo que tomará unas horas (poblaré la db con 50k usuarios). Lo haré y les mantendré informados.

John John
26 abr 2012 17:53:28

@John me alegra escucharlo. Realmente quiero saber los resultados.

kaiser kaiser
26 abr 2012 17:57:06

Ok, aquí están los resultados de las pruebas. Para esto, agregué alrededor de 47K usuarios desde un archivo csv. Luego, ejecuté un bucle for para que los primeros 45 usuarios siguieran a todos los demás usuarios. Esto resultó en 3,704,951 registros guardados en mi tabla personalizada. Inicialmente, la solución de @m0r7if3r me dio un tiempo de consulta de 95 segundos, que bajó a 0.020 ms después de activar la indexación en la columna leader_id. El total de memoria PHP consumida fue de alrededor de 20MB. Por otro lado, tu solución tomó entre 2 y 5 segundos para la consulta con indexación activada. El total de memoria PHP consumida fue de alrededor de 117MB.

John John
27 abr 2012 16:32:02

@John ¿Podrías agregar esto como una respuesta separada? Realmente me alegra que hayas hecho esta prueba y nos hayas proporcionado datos reales. :) Lo que apreciaría aún más es una prueba más "realista": Que cada usuario siga a un$leader_amount = rand( 0, 5 );y luego agregar el número de$leader_amountx$random_ids = rand( 0, 47000 );a cada usuario. Hasta ahora, lo que sabemos es: Mi solución sería extremadamente mala si un usuario sigue a todos los demás usuarios. Además: Tendrás que mostrar cómo hiciste la prueba y dónde exactamente agregaste los temporizadores. Esta es una pregunta única y realmente me gusta :)

kaiser kaiser
27 abr 2012 19:03:43

@John Enlacé y compartí tu pregunta y participación, y quería mencionar que todos están disfrutando mucho tu trabajo en ello (mira los votos positivos). :)

kaiser kaiser
27 abr 2012 19:14:44

Realizaré más pruebas basándome en tu sugerencia para hacer una prueba más realista y luego escribiré una respuesta con detalles. Sin embargo, para una prueba verdaderamente realista hay otras dos variables que actualmente no puedo simular. Primero: Al menos 20 publicaciones por cada usuario. Actualmente, solo probé con alrededor de 50 publicaciones entre 2-3 usuarios. Segundo: Un gran tráfico. Estas dos variables también afectarían los resultados obtenidos en la vida real.

John John
27 abr 2012 21:22:28

Estaba investigando más tu sugerencia para aleatorizar lo siguiente. Sin embargo, no pude entender los fragmentos de código que escribiste. Un fragmento completo sería más útil. Esto es lo que había hecho para que los primeros 50 usuarios siguieran a todos los demás: for ($j = 2; $j <= 52; $j++) { for ($i = ($j + 1); $i <= 47000; $i++) { $rows_affected = $wpdb->insert($table_name, array( 'leader_id' => $i, 'follower_id' => $j)); } }. Un bucle for anidado muy simple.

John John
27 abr 2012 21:52:29

Sobre el tráfico) Podrías usar las últimas versiones de apache, nginx, minificación, caché, compresión, etc. Cada una tendría un impacto en los resultados. Pero buscamos resultados en un entorno local, básico (sin plugins, tema por defecto) que pueda realmente ser ponderado.

kaiser kaiser
27 abr 2012 22:03:27

¿Recibiste una notificación de mi comentario sobre la aleatorización? Supongo que olvidé mencionar tu nombre en el comentario. De todos modos, supongo que el dueño del post fue notificado. Esperaré tus comentarios antes de proceder.

John John
27 abr 2012 22:33:28

Agrego otra respuesta (podemos procesar & modificar/editar en esa) porque el formato de código en los comentarios simplemente apesta :P

kaiser kaiser
28 abr 2012 12:11:12
Mostrar los 13 comentarios restantes
1

Nota: Esta respuesta es para evitar discusiones extendidas en los comentarios

  1. Aquí está el código del OP de los comentarios, para agregar el primer conjunto de usuarios de prueba. Debe modificarse a un ejemplo del mundo real.

    for ( $j = 2; $j <= 52; $j++ ) 
    {
        for ( $i = ($j + 1); $i <= 47000; $i++ )
        {
            $rows_affected = $wpdb->insert( $table_name, array( 'leader_id' => $i, 'follower_id' => $j ) );
        }
    }
    

    OP Sobre la Prueba Para esto agregué alrededor de 47K usuarios desde un archivo csv. Luego, ejecuté un bucle for para que los primeros 45 usuarios siguieran a todos los demás usuarios.

    • Esto resultó en 3,704,951 registros guardados en mi tabla personalizada.
    • Inicialmente, la solución de @m0r7if3r me dio un tiempo de consulta de 95 segs, que bajó a 0.020 ms después de activar la indexación en la columna leader_id. La memoria total de PHP consumida fue alrededor de 20MB.
    • Por otro lado, tu solución tomó entre 2 a 5 segs para la consulta con indexación activada. La memoria total de PHP consumida fue alrededor de 117MB.
  2. Mi respuesta a esta ↑ prueba:

    una prueba más "realista": Que cada usuario siga a $leader_amount = rand( 0, 5 ); y luego agregar el número de $leader_amount x $random_ids = rand( 0, 47000 ); a cada usuario. Hasta ahora lo que sabemos es: Mi solución sería extremadamente mala si un usuario sigue a todos los demás. Además: Debes mostrar cómo hiciste la prueba y dónde exactamente agregaste los temporizadores.

    También debo mencionar que el ↑ seguimiento de tiempos anterior no se puede medir realmente, ya que también tomaría el tiempo para calcular el bucle. Sería mejor recorrer el conjunto resultante de IDs en un segundo bucle.

proceso adicional aquí

28 abr 2012 12:14:37
Comentarios

Nota para quienes han estado siguiendo esta Q: Estoy en proceso de medir el rendimiento bajo varias condiciones y publicaré los resultados en un día o 3. Esta ha sido una tarea extremadamente consumidora de tiempo debido a la escala de datos de prueba que necesitan ser generados.

John John
30 abr 2012 09:47:23