使用 scala mongo 驱动程序未将案例 class 的继承元素持久保存到 mongodb

inherited elements of a case class not getting persisted into mongodb using scala mongo driver

我想使用 mongo-scala-driver 2.7.0 坚持使用 mongo 数据库。我的案例class看起来有点像下面

case class App(
                  @BsonProperty("_id") id: String = "",
                  appName: String
                  createdBy: String,
                  createdAt: Date,
                  metadata: AppMetadata,
                )

sealed class AppMetadata {
  final val _t: String = this.getClass.getSimpleName
}

case class App1Metadata(clientId: String,
                        savedSearches: List[String]
                        
                        ) extends ScopeMetadata

case class App2Metadata(agencyId: String,
                        randomData: List[Int]
                        
                        ) extends ScopeMetadata

如您所见,应用的元数据可能因应用(App1 和 App2)而大不相同。因此,不可能有一个单一的元数据案例 class 可以被所有应用程序使用。因此,我决定为每个应用程序的元数据设置一个案例 class,如上所示,并从父特征扩展它们。我试图按照 scala mongo 驱动程序的文档 here and another Whosebug question here 来实现这一点。如您所见,我在密封特征中放置了一个 _t 字段。根据文档,这是必需的,因为它将在与 Bson 之间对文档进行解码和编码时用作提示。

这是我的编解码器的样子

def customCodeRegistry() = {
    fromProviders(
      Macros.createCodecProviderIgnoreNone[App](),
      Macros.createCodecProviderIgnoreNone[App1Metadata](),
      Macros.createCodecProviderIgnoreNone[App2Metadata]()
    )
  }


val customCodecRegistry2 = CodecRegistries.fromProviders(Macros.createCodecProvider[ScopeMetadata]())

val codecRegistry = fromRegistries(customCodeRegistry(), DEFAULT_CODEC_REGISTRY,customCodecRegistry2)

我坚持简单地使用

override def addApp(app: App): Future[String] = {
    for {
      _ <- collection.insertOne(app).toFuture()
    } yield user.id
  }

保存在 mongo 数据库中的文档缺少 _t field 内部元数据对象,根据文档,这是 Mongo Scala Driver 转换 bson 所必需的到正确的元数据大小写 class,因此当我尝试使用

获取文档时
override def getUser(email: String): Future[Option[User]] = {
    collection.find(equal(EMAIL_FIELD, email)).headOption()
  }

我在错误消息中收到以下内容

Could not decode sealed case class. Missing '_t' field.

但是,如果我显式地将 _t 字段添加到元数据对象的 mongo 中,其名称为 class,则应该将其解码为,一切正常,我得到正确的大小写 class 在获取操作期间。我不明白为什么虽然使用 mongo insert ,但 _t field 没有被插入

AFAIU 文档,您不需要为每个子类型提供编解码器,_t 应该会自动添加。

docs 说:

you only need create a CodecProvider for the parent sealed trait/class. Internally an extra field (_t) is stored alongside the data

所以像这样的东西对你来说应该足够了:

sealed trait AppMetadata 
case class App1Metadata(clientId: String, savedSearches: List[String]) extends AppMetadata
case class App2Metadata(agencyId: String, randomData: List[Int]) extends AppMetadata

fromRegistries(fromProviders(classOf[AppMetadata]), DEFAULT_CODEC_REGISTRY)

假设 ScopeMetadataAppMetadata 相同。