You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell中如何将自定义数据类型转换为BSON Document?

我完全懂你在Data.Bson.Mapping上卡壳的感觉——这个包的文档确实有点过于简洁,尤其是自定义类型和Document互转这块,示例少得可怜。让我结合你的场景,一步步拆解解决方法:

第一步:先搞定自定义类型的Mapping实例

要让自定义类型能转换成Document,核心是为它实现Mapping类型类。最便捷的方式是用Template Haskell的deriveMapping自动生成实例,比如先定义你的类型:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}

import Data.Bson.Mapping
import Data.Text (Text)
import Database.MongoDB (ObjectId)

-- 你的自定义类型
data User = User
  { userId   :: ObjectId
  , userName :: Text
  , userAge  :: Int
  } deriving (Show, Eq, Generic)

-- 自动生成Mapping实例
deriveMapping ''User

如果需要手动控制字段名(比如把Haskell的驼峰名转成MongoDB的下划线名),也可以手动写Mapping实例:

instance Mapping User where
  toDoc (User uid name age) = 
    [ "user_id" =: uid
    , "user_name" =: name
    , "user_age" =: age
    ]
  fromDoc doc = User
    <$> doc ! "user_id"
    <*> doc ! "user_name"
    <*> doc ! "user_age"
第二步:把自定义类型转成Document

有了Mapping实例后,直接用toDocument函数就能完成转换,这才是你要的核心操作:

sampleUser :: User
sampleUser = User (ObjectId "60d21b4667d0d8992e610c85") "Lou Süsslin" 28

-- 转换为Document
userDocument :: Document
userDocument = toDocument sampleUser
关于你提到的selectFields:别搞混场景!

你说尝试用selectFields但不知道怎么把Expression (Q Exp)转成Document——这里的关键是:selectFields不是用来做类型转Document的,它是用来**构建MongoDB查询的投影(Projection)**的,也就是指定查询时只返回哪些字段。

要把它的结果转换成实际的Document,需要结合mkProjection和Template Haskell的拼接语法$(...)(因为selectFields返回的是编译时表达式):

import Database.MongoDB (Query(..), select, project)

-- 生成只返回userName和userAge的投影Document
userProjection :: Document
userProjection = $(mkProjection $ selectFields ''User ["userName", "userAge"])

-- 用这个投影构建查询
userQuery :: Query
userQuery = select [] "users" { project = userProjection }
总结一下
  1. 先为自定义类型生成/编写Mapping实例,这是转换的基础
  2. 用toDocument直接把类型实例转成Document(这是你最初要的功能)
  3. selectFields是查询投影工具,需要配合Template Haskell拼接才能得到实际的Document

内容的提问来源于stack exchange,提问作者Lou Süsslin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:17:31