es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿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?

Tags:  

Answer

  1. Avatar for 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 de posts que se incluirán en el nuevo tipo. Sin embargo, estoy de acuerdo en que sería mejor si el tipo solo requiriera el objeto Post y el commentsCount.

    Una forma de lograr esto es mediante el uso de la biblioteca beam, que es un sistema de consultas SQL seguras por tipos. Con beam, podemos utilizar la función oneToMany_ para obtener objetos Post con sus recuentos de comentarios asociados en una sola consulta.

    Aquí hay un ejemplo de implementación utilizando beam:

    import qualified Database.Beam as B
    import qualified Database.Beam.Postgres as BP
    
    data PostWithCommentsCount = PostWithCommentsCount
        { post :: Post
        , commentsCount :: Int
        }
        deriving (Eq, Show)
    
    -- Define una consulta beam que obtiene entradas y sus conteos de comentarios asociados
    postsWithCommentsCountQuery :: B.SqlSelect BP.Postgres (PostWithCommentsCount B.QExpr, B.QExpr Int)
    postsWithCommentsCountQuery = B.all_With_
        (\(post, commentCount) -> PostWithCommentsCount post commentCount)
        (B.postgres $ B.all_ (posts db))
        (B.postgres $ B.fold_ (\_ -> B.as_ @Int "comments_count") 0 $
            B.oneToMany_ (comments db) commentsPostId postsId)
    
    -- Utiliza la consulta beam para obtener objetos PostWithCommentsCount
    fetchPostsWithCommentsCount :: (?modelContext :: ModelContext) => IO [PostWithCommentsCount]
    fetchPostsWithCommentsCount = do
        trackTableRead "posts"
        B.runBeamPostgres (connContext ?modelContext) $
          map fst <$> B.runSelectReturningList postsWithCommentsCountQuery
    

    En esta implementación, utilizamos la función oneToMany_ de beam 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ón all_With_ para combinar los datos de Post y commentsCount en objetos PostWithCommentsCount.

    En general, este enfoque reduce la cantidad de especificación manual requerida y facilita el mantenimiento del código a lo largo del tiempo.

Comments are closed.