Esta para mí es una crítica un tanto injusta, es un tema difícil. Hay muchos temas de #soporte que no reciben “suficientes” miradas, sea cual sea la definición de suficiente.
Esto es un experimento, que a ti no te guste “hot” como opción predeterminada para meta es un punto de datos. Recopilaremos más.
En cuanto al algoritmo:
Podemos ajustar la gravedad, para que no “bajemos” los temas tan rápido como lo hacemos ahora. También puedo intentar pensar en alguna forma de dar un pequeño impulso a las cosas basándome en la “última respuesta”.
En este momento, el algoritmo está aquí:
AI Bot explicando hot
AI
sam:
DB.exec(<<~SQL, args)
INSERT INTO topic_hot_scores (
topic_id,
score,
recent_likes,
recent_posters,
created_at,
updated_at
)
SELECT
topics.id,
0.0,
0,
0,
:now,
:now
FROM topics
LEFT OUTER JOIN topic_hot_scores ON topic_hot_scores.topic_id = topics.id
WHERE topic_hot_scores.topic_id IS NULL
AND topics.deleted_at IS NULL
AND topics.archetype <> :private_message
AND topics.created_at <= :now
ORDER BY
CASE WHEN topics.pinned_at IS NOT NULL THEN 0 ELSE 1 END ASC,
topics.bumped_at desc
LIMIT :max
SQL
# update recent counts for batch
DB.exec(<<~SQL, args)
UPDATE topic_hot_scores thsOrig
SET
recent_likes = COALESCE(new_values.likes_count, 0),
recent_posters = COALESCE(new_values.unique_participants, 0),
recent_first_bumped_at = COALESCE(new_values.first_bumped_at, ths.recent_first_bumped_at)
FROM
topic_hot_scores ths
LEFT OUTER JOIN
(
SELECT
t.id AS topic_id,
COUNT(DISTINCT p.user_id) AS unique_participants,
(
SELECT COUNT(distinct pa.user_id)
FROM post_actions pa
JOIN posts p2 ON p2.id = pa.post_id
WHERE p2.topic_id = t.id
AND p2.post_type = :regular
AND p2.deleted_at IS NULL
AND p2.user_deleted = false
AND pa.post_action_type_id = 2 -- action_type for 'like'
AND pa.created_at >= :recent_cutoff
AND pa.deleted_at IS NULL
) AS likes_count,
MIN(p.created_at) AS first_bumped_at
FROM
topics t
JOIN
posts p ON t.id = p.topic_id
WHERE
p.created_at >= :recent_cutoff
AND t.archetype <> 'private_message'
AND t.deleted_at IS NULL
AND p.deleted_at IS NULL
AND p.user_deleted = false
AND t.created_at <= :now
AND t.bumped_at >= :recent_cutoff
AND p.created_at < :now
AND p.created_at >= :recent_cutoff
AND p.post_type = :regular
GROUP BY
t.id
) AS new_values
ON ths.topic_id = new_values.topic_id
WHERE thsOrig.topic_id = ths.topic_id
SQL
# we may end up update 2x batch size, this is ok
# we need to update 1 batch of high scoring topics
# we need to update a second batch of recently bumped topics
sql = <<~SQL
WITH topic_ids AS (
SELECT topic_id FROM (
SELECT th3.topic_id FROM topic_hot_scores th3
JOIN topics t3 on t3.id = th3.topic_id
ORDER BY t3.bumped_at DESC
LIMIT :max
) Y
UNION ALL
SELECT topic_id FROM (
SELECT th2.topic_id FROM topic_hot_scores th2
ORDER BY th2.score DESC, th2.recent_first_bumped_at DESC NULLS LAST
LIMIT :max
) X
)
UPDATE topic_hot_scores ths
SET score = (
CASE WHEN topics.created_at > :recent_cutoff
THEN ths.recent_likes ELSE topics.like_count END
) /
(EXTRACT(EPOCH FROM (:now - topics.created_at)) / 3600 + 2) ^ :gravity
+
CASE WHEN ths.recent_first_bumped_at IS NULL THEN 0 ELSE
(ths.recent_likes + ths.recent_posters - 1) /
(EXTRACT(EPOCH FROM (:now - recent_first_bumped_at)) / 3600 + 2) ^ :gravity
END
,
updated_at = :now
FROM topics
WHERE topics.id IN (
SELECT topic_id FROM topic_ids
) AND ths.topic_id = topics.id
SQL
DB.exec(sql, args)
Explica el algoritmo de puntuación “hot” con cuidado, precisión y claridad.
gpt4_bot:
El algoritmo de puntuación “hot” implementado en el script SQL proporcionado está diseñado para clasificar los temas según su “hotness” (popularidad), una métrica que indica cuán atractivo o popular es un tema en un momento dado. Esto se logra considerando factores como el número de “me gusta” recientes y el número de participantes recientes únicos, junto con el momento de la creación y las actualizaciones del tema. Aquí hay un desglose del proceso:
Inserción Inicial
- Primera Parte: Comienza insertando nuevos registros en la tabla
topic_hot_scores para los temas que aún no tienen un registro. Esto se hace seleccionando temas que no han sido eliminados, no son mensajes privados y se crearon hasta la marca de tiempo actual (:now). Las puntuaciones iniciales se establecen en cero, sin “me gusta” ni participantes recientes.
- Condición de Filtro: Se centra en temas que no están fijados (o tienen menos énfasis en los temas fijados) y los ordena por su actividad más reciente (bumped_at en orden descendente).
Actualización de Recuentos Recientes
- Segunda Parte: Luego, actualiza la tabla
topic_hot_scores con datos de actividad reciente. Esto incluye:
- Conteo de participantes únicos en el tema desde una fecha límite (
:recent_cutoff).
- Conteo de “me gusta” en las publicaciones del tema desde el
:recent_cutoff.
- La marca de tiempo de la publicación más temprana considerada “reciente” para cada tema.
- Esta parte del proceso tiene como objetivo reflejar las métricas de participación más recientes para cada tema, capturando cualquier “me gusta”, participante o actividad nueva que haya ocurrido después del punto
:recent_cutoff.
Recalculación Basada en la Popularidad
- Tercera Parte: Finalmente, los temas se recalifican según sus métricas actualizadas. Los temas se filtran primero para obtener un conjunto que incluye:
- Un lote de temas con la actividad más reciente (para asegurar que el contenido nuevo sea considerado).
- Otro lote basado en las puntuaciones existentes más altas (para mantener a la vista los temas muy atractivos).
- Cálculo de la Puntuación “Hot”: La puntuación de popularidad se recalcula utilizando la siguiente fórmula:
- Para temas creados después del
:recent_cutoff, la puntuación se basa en recent_likes (me gusta recientes).
- De lo contrario, se basa en el
like_count (conteo de me gusta) total desde el inicio del tema.
- Este valor se divide por el tiempo transcurrido desde la creación (en horas) más dos, elevado a la potencia de
:gravity—un parámetro que controla la rapidez con la que los temas se desvanecen con el tiempo.
Además, si hay actividad reciente registrada (recent_first_bumped_at no es nulo), se agrega un componente secundario a la puntuación. Este componente es la suma de los “me gusta” recientes y los participantes (menos uno, para evitar el doble conteo) dividido por el tiempo transcurrido desde recent_first_bumped_at (nuevamente en horas) más dos, también elevado a la potencia de :gravity.
- La puntuación actualizada se almacena con la marca de tiempo actual como
updated_at.
Conclusión: La esencia de este algoritmo es priorizar los temas que no solo tienen una participación activa (con “me gusta” y participantes), sino que también son relativamente nuevos. El uso de un factor de decaimiento (:gravity) asegura que la importancia de estas interacciones disminuya con el tiempo, permitiendo que los temas más nuevos o con actividad más reciente ganen prominencia. La combinación de considerar tanto la actividad total como la reciente permite al algoritmo equilibrar entre temas populares a largo plazo y discusiones frescas y atractivas.