【问题标题】:Passing different object models as a parameter to a method in scala将不同的对象模型作为参数传递给scala中的方法
【发布时间】:2018-05-05 07:41:01
【问题描述】:

我真的很纠结scala 中的类型关系 以及如何有效地使用它们。我目前正在尝试了解如何使用它们仅编辑 Mongo 集合 中的某些字段。这意味着将仅包含这些字段的特定 object 传递给一个方法(在阅读 about variances 之后)我认为我可以这样做:

   abstract class DocClass
   case class DocPart1(oId: Option[BSONObjectID], name: String, other: String) extends DocClass
   case class DocPart2(city: String, country: String) extends DocClass

使用调用更通用方法的方法为:

   def updateMultipleFields(oId: Option[BSONObjectID], dataModel: DocClass): Future[Result] = serviceClientDb.updateFields[T](collectionName, dataModel, oId)
   // updateFields updates the collection by passing *dataModel* into the collection, i.e. Json.obj("$set" -> dataModel)

所以dataModel 可以是DocPart1DocPart2 对象。我急于不使用 type parameterupdateMultipleFields 上(正如interesting article 可能建议的那样),因为这导致我在将这些传递给项目中其他文件中的此方法时遇到更多问题。我这样做是为了遵守 DRY 并维护高效的数据库操作。

我在这个问题上转了一圈 - 任何人都可以对此有所了解吗?

在@SerGr 的 cmets 之后编辑

所以要完全清楚;我正在使用 Play/Scala/ReactiveMongo Play JSON(documented here),并且我有一个包含很多字段的 MongoDB 集合。

   case class Doc(oId: Option[BSONObjectID], name: String, city: String, country: String, city: String, continent: String, region: String, region: String, latitude: Long, longitude: Long)

要创建一个新文档,我已将 Doc(上图)自动映射到集合结构(在 Play - like this)并创建了一个表单(以插入/更新 集合) - 一切正常!

但是在编辑文档时;我只想更新一些字段(这样所有字段都不会更新)。因此,我创建了多个case classes 将这些字段划分为更小的模型(如DocPart1DocPart2 的示例)并将表单数据映射到一个。这导致我将这些作为参数传递给updateMultipleFields 方法,如上所示。我希望这更有意义。

【问题讨论】:

  • 不清楚你想要什么。您是否有一个对象和几组在不同场景中更新的不同字段,并且您希望将每个这样的集合分组为显式类型?或者您是否有一些集合,出于某种原因存储了应该以不同方式更新的不同类型的对象?或者这种代码还有其他场景吗?
  • @SerGr - 感谢您的回复。请参阅 cmets,如果您需要进一步说明,请告诉我。 ATB
  • 耶稣,你能不能说一下你用什么库来访问 Mongo?了解它为此类部分更新提供的 API 非常重要。
  • @SerGr - 我使用 ReactiveMongo Play JSON 作为documented here

标签: scala types polymorphism covariance subtype


【解决方案1】:

我不确定我是否正确理解您的需求。仍然这里有一些可能是它的代码。假设我们将FullDoc 类定义为:

case class FullDoc(_id: Option[BSONObjectID], name: String, other: String)

我们有 2 个部分更新定义为:

sealed trait BaseDocPart

case class DocPart1(name: String) extends BaseDocPart

case class DocPart2(other: String) extends BaseDocPart

还假设我们有一个访问 Mongo 集合的访问器:

def docCollection: Future[JSONCollection] = ...

所以如果我了解您的要求,您需要的是这样的:

def update[T <: BaseDocPart](oId: BSONObjectID, docPart: T)(implicit format: OFormat[T]) = {
  docCollection.flatMap(_.update(BSONDocument("_id" -> oId),
    JsObject(Seq("$set" -> Json.toJson(docPart)))))
}

本质上,主要技巧是使用泛型T &lt;: BaseDocPart 并传递implicit format: OFormat[T],这样即使在类型擦除之后,我们也可以将BaseDocPart 的特定子代转换为JSON。

这是一些额外的测试代码(我在控制台应用程序中使用的)

  implicit val fullFormat = Json.format[FullDoc]
  implicit val part1Format = Json.format[DocPart1]
  implicit val part2Format = Json.format[DocPart2]

  def insert(id: Int) = {
    val fullDoc = FullDoc(None, s"fullDoc_$id", s"other_$id")
    val insF: Future[WriteResult] = docCollection.flatMap(_.insert(fullDoc))
    val insRes = Await.result(insF, 2 seconds)
    println(s"insRes = $insRes")
  }

  def loadAndPrintAll() = {
    val readF = docCollection.flatMap(_.find(Json.obj()).cursor[FullDoc](ReadPreference.primaryPreferred).collect(100, Cursor.FailOnError[Vector[FullDoc]]()))
    val readRes = Await.result(readF, 2 seconds)
    println(s"readRes =\n${readRes.mkString("\n")}")
  }

  def loadRandomDocument(): FullDoc = {
    val readF = docCollection.flatMap(_.find(Json.obj()).cursor[FullDoc](ReadPreference.primaryPreferred).collect(100, Cursor.FailOnError[Vector[FullDoc]]()))
    val readRes = Await.result(readF, 2 seconds)
    readRes(Random.nextInt(readRes.length))
  }

  def updateWrapper[T <: BaseDocPart](oId: BSONObjectID, docPart: T)(implicit writer: OFormat[T]) = {
    val updateRes = Await.result(update(oId, docPart), 2 seconds)
    println(s"updateRes = $updateRes")
  }

  // pre-fill with some data    
  insert(1)
  insert(2)
  insert(3)
  insert(4)
  val newId: Int = ((System.currentTimeMillis() - 1511464148000L) / 100).toInt
  println(s"newId = $newId")

  val doc21: FullDoc = loadRandomDocument()
  println(s"doc21 = $doc21")
  updateWrapper(doc21._id.get, DocPart1(s"p1_modified_$newId"))

  val doc22: FullDoc = loadRandomDocument()
  println(s"doc22 = $doc22")
  updateWrapper(doc22._id.get, DocPart2(s"p2_modified_$newId"))

  loadAndPrintAll()

【讨论】:

    猜你喜欢
    • 2022-01-22
    • 2015-11-22
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多