udemy php mysql social-networking

php - udemy 6



MySQL inteligente GRUPO BY para flujos de actividad (4)

Estoy creando un flujo de actividad para nuestro sitio, y he logrado algunos avances decentes con algo que funciona bastante bien.

Es alimentado por dos mesas:

corriente :

  • id - ID de elemento de flujo único
  • user_id : ID del usuario que creó el elemento de transmisión
  • object_type - Tipo de objeto (actualmente ''vendedor'' o ''producto'')
  • object_id : ID interna del objeto (actualmente, la ID del vendedor o la ID del producto)
  • action_name : la acción tomada contra el objeto (actualmente ''comprar'' o ''corazón'')
  • stream_date : marca de tiempo en la que se creó la acción.
  • hidden - Booleano si el usuario ha elegido ocultar el elemento.

sigue :

  • id - id única de Follow
  • user_id : la ID del usuario que inicia la acción ''Seguir''.
  • following_user : el ID del usuario que está siendo seguido.
  • followed : marca de tiempo que se ejecutó la acción de seguimiento.

Actualmente estoy usando la siguiente consulta para extraer contenido de la base de datos:

Consulta:

SELECT stream.*, COUNT(stream.id) AS rows_in_group, GROUP_CONCAT(stream.id) AS in_collection FROM stream INNER JOIN follows ON stream.user_id = follows.following_user WHERE follows.user_id = ''1'' AND stream.hidden = ''0'' GROUP BY stream.user_id, stream.action_name, stream.object_type, date(stream.stream_date) ORDER BY stream.stream_date DESC;

Esta consulta realmente funciona bastante bien, y al usar un poco de PHP para analizar los datos que devuelve MySQL, podemos crear un flujo de actividad agradable con las mismas acciones del mismo usuario agrupadas si el tiempo entre las acciones no es demasiado grande (ver ejemplo abajo).

Mi pregunta es, ¿cómo puedo hacer esto más inteligente? Actualmente se agrupa por un eje, actividad de "usuario", cuando hay varios elementos por un usuario en particular dentro de un cierto período de tiempo que MySQL sabe para agruparlos.

¿Cómo puedo hacer esto aún más inteligente y agruparlo por otro eje, como "object_id", por lo que si hay varias acciones para el mismo objeto en secuencia, estos elementos están agrupados, pero mantienen la lógica de agrupación que tenemos actualmente para agrupar acciones / objetos por usuario? . ¿Y implementando esto sin duplicación de datos?

Ejemplo de múltiples objetos que aparecen en secuencia:

Entiendo que las soluciones a problemas como este pueden ser muy complejas, muy rápidamente, pero me pregunto si hay una solución elegante y bastante simple para esto (con suerte) en MySQL.


Algunas observaciones sobre los resultados deseados:

Algunos de los artículos están agregados (Jack Sprat de corazón, siete vendedores) y otros están detallados (Lord Nelson fletó el Golden Hind). Probablemente necesite tener un UNION en su consulta que reúna estas dos clases de elementos de dos subconsultas separadas.

Utiliza una función de aproximación de marca de tiempo bastante burda para agrupar sus elementos ... DATE() . Es posible que desee utilizar un esquema más sofisticado y modificable ... como este, tal vez

GROUP BY TIMESTAMPDIFF(HOUR,CURRENT_TIME(),stream_date) DIV hourchunk

Esto te permitirá agrupar cosas por partes de edad. Por ejemplo, si usas 48 para hourchunk cosas que son de 0 a 48 horas. A medida que agrega tráfico y acción a su sistema, es posible que desee disminuir el valor de la hourchunk .


En Fashiolista hemos abierto nuestro enfoque para construir sistemas de alimentación. https://github.com/tschellenbach/Feedly Actualmente es la biblioteca de código abierto más grande destinada a resolver este problema. (pero escrito en Python)

El mismo equipo que creó Feedly también ofrece una API alojada, que maneja la complejidad para usted. Eche un vistazo a getstream.io Hay clientes para PHP, Node, Ruby y Python. https://github.com/tbarbugli/stream-php También ofrece soporte para agregaciones definidas personalizadas, que está buscando.

Además, eche un vistazo a esta publicación de alta escalabilidad donde explicamos algunas de las decisiones de diseño involucradas: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic-feeds.html

Este tutorial te ayudará a configurar un sistema como el feed de Pinterest usando Redis. Es muy fácil empezar.

Para obtener más información sobre el diseño de feeds, recomiendo leer algunos de los artículos en los que basamos a Feedly:


Hemos resuelto un problema similar utilizando el enfoque de ''vista materializada'': estamos utilizando una tabla dedicada que se actualiza en el evento insertar / actualizar / eliminar. Todas las actividades del usuario se registran en esta tabla y se preparan previamente para una selección y representación sencillas.

El beneficio es una selección simple y rápida, la desventaja es un poco más lenta de insertar / actualizar / eliminar ya que la tabla de registro también debe actualizarse.

Si este sistema está bien diseñado, es una solución ganadora.

Esto es bastante fácil de implementar si está utilizando ORM con eventos posteriores a la inserción / actualización / eliminación (como Doctrine)


Mi impresión es que necesita agrupar por usuario, como lo hace, pero también, después de esa agrupación, por acción.

Me parece que necesitas una subconsulta como esta:

SELECT *, -- or whatever columns SUM(actions_in_group) AS total_rows_in_group, GROUP_CONCAT(in_collection) AS complete_collection FROM ( SELECT stream.*, -- or whatever columns COUNT(stream.id) AS actions_in_user_group, GROUP_CONCAT(stream.id) AS actions_in_user_collection FROM stream INNER JOIN follows ON stream.user_id = follows.following_user WHERE follows.user_id = ''1'' AND stream.hidden = ''0'' GROUP BY stream.user_id, date(stream.stream_date) ) GROUP BY object_id, date(stream.stream_date) ORDER BY stream.stream_date DESC;

Su consulta inicial (ahora la interna) se agrupa por usuario, pero luego los grupos de usuarios se agrupan por acciones idénticas, es decir, se juntarían productos idénticos comprados o ventas de un vendedor.