0

我想使用 mongo-scala-driver 2.7.0 坚持使用 mongo db。我的案例类看起来有点如下

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)而异。因此,不可能有一个可供所有应用程序使用的单一元数据案例类。因此,我决定为每个应用程序的元数据创建一个案例类,如上所示,并从父 trait 扩展它们。我试图在此处遵循 scala mongo 驱动程序的文档和此处的另一个 stackoverflow 问题实现此目的。如您所见,我_t在密封特性中放置了一个字段。根据文档,这是必需的,因为它将在将文档解码和编码到 Bson 和从 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 db 中的文档缺少_t field内部元数据对象,根据文档,这是必要的,以便 Mongo Scala Driver 将 bson 转换为正确的元数据案例类,因此当我尝试使用获取文档时

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 中,并使用它应该解码到的类的名称,那么一切正常,并且在 get 操作期间我得到了正确的案例类。我不明白为什么虽然使用 mongo insert ,但_t field没有插入

4

1 回答 1

1

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

文档说:

您只需要CodecProvider为父密封特征/类创建一个。在内部,一个额外的字段 ( _t) 与数据一起存储

所以这样的事情对你来说应该足够了:

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.

于 2021-06-08T09:39:03.730 回答