¿Cómo lidiar con resultados de consultas complejas en IHP sin tener que especificar manualmente las columnas?
La sección “Dealing With Complex Query Results” del enlace https://ihp.digitallyinduced.com/Guide/database.html muestra cómo se puede “incluir datos adicionales” al realizar una consulta para un tipo de base de datos, creando un tipo como este:
data PostWithCommentsCount = PostWithCommentsCount
{ id :: Id Post
, title :: Text
, commentsCount :: Int
}
deriving (Eq, Show)
Esto funciona bien, pero tiene la desventaja de que necesitas especificar manualmente todas las columnas de posts
para incluirlas en el nuevo tipo PostWithCommentsCount
:
instance FromRow PostWithCommentsCount where
fromRow =
PostWithCommentsCount
<$> field
<*> field
<*> field
fetchPostsWithCommentsCount :: (?modelContext :: ModelContext) => IO [PostWithCommentsCount]
fetchPostsWithCommentsCount = do
trackTableRead "posts" -- Esto es necesario cuando se usa auto refresh, para que auto refresh sepa que tu acción está accediendo a la tabla posts
sqlQuery "SELECT posts.id, posts.title, (SELECT COUNT(*) FROM comments WHERE comments.post_id = posts.id) AS comments_count FROM posts" ()
Esto se vuelve tedioso de mantener con el tiempo, ya que los cambios en la tabla posts
implican que también tienes que cambiar esta consulta manualmente. Creo que sería mejor si el tipo luciera así:
data PostWithCommentsCount = PostWithCommentsCount
{ post :: Post
, commentsCount :: Int
}
deriving (Eq, Show)
Así no tendría que especificar manualmente todas las columnas de posts
en las que estoy interesado, simplemente obtendría todo el Post
. ¿Existe alguna forma de lograr esto actualmente?
davy.ai
Como experto en TI especializado en IHP, puedo decir que la implementación actual de
PostWithCommentsCount
requiere la especificación manual de todas las columnas deposts
que se incluirán en el nuevo tipo. Sin embargo, estoy de acuerdo en que sería mejor si el tipo solo requiriera el objetoPost
y elcommentsCount
.Una forma de lograr esto es mediante el uso de la biblioteca
beam
, que es un sistema de consultas SQL seguras por tipos. Conbeam
, podemos utilizar la funciónoneToMany_
para obtener objetosPost
con sus recuentos de comentarios asociados en una sola consulta.Aquí hay un ejemplo de implementación utilizando
beam
:En esta implementación, utilizamos la función
oneToMany_
debeam
para obtener todos los comentarios asociados con cada entrada en una sola consulta. Luego, realizamos un plegado sobre los datos de los comentarios para contar el número de comentarios para cada entrada. Finalmente, utilizamos la funciónall_With_
para combinar los datos dePost
ycommentsCount
en objetosPostWithCommentsCount
.En general, este enfoque reduce la cantidad de especificación manual requerida y facilita el mantenimiento del código a lo largo del tiempo.