我正在尝试阻止将空值插入到我的 mongoDB集合中。有问题的字段如下所示:
MongoDB字段
"stadiumArr" : [
"Old Trafford",
"El Calderon",
...
]
(已映射)案例类的示例
case class FormData(_id: Option[BSONObjectID], stadiumArr: Option[List[String]], ..)
Scala表单示例
object MyForm {
val form = Form(
mapping(
"_id" -> ignored(Option.empty[BSONObjectID]),
"stadiumArr" -> optional(list(text)),
...
)(FormData.apply)(FormData.unapply)
)
}
我也正在使用Repeated Values functionality in Play Framework:
播放模板
@import helper._
@(myForm: Form[models.db.FormData])(implicit request: RequestHeader, messagesProvider: MessagesProvider)
@repeatWithIndex(myForm("stadiumArr"), min = 5) { (stadium, idx) =>
@inputText(stadium, '_label -> ("stadium #" + (idx + 1)))
}
这可以确保数组中是否至少有5个值;仍然会创建(至少)5个输入框。但是,如果在提交表单时一个(或多个)输入框为空,则仍会在数组中将空字符串添加为值,例如
"stadiumArr" : [
"Old Trafford",
"El Calderon",
"",
"",
""
]
基于从/向数据库转换类型的一些其他方法;我试过玩几个解决方案;如:
implicit val arrayWrite: Writes[List[String]] = new Writes[List[String]] {
def writes(list: List[String]): JsValue = Json.arr(list.filterNot(_.isEmpty))
}
..但这不起作用。有关如何防止空值插入数据库集合的任何想法?
答案 0 :(得分:0)
我已经为此而去了(当它被编写和测试时,它总是显而易见的):
implicit val arrayWrite: Writes[List[String]] = new Writes[List[String]] {
def writes(list: List[String]): JsValue = Json.toJson(list.filterNot(_.isEmpty).toIndexedSeq)
}
但我很想知道如何
。映射现有的Read而不是从头开始重新定义
as @cchantep建议
答案 1 :(得分:0)
在不知道您使用它的特定版本或库的情况下很难给出答案,但是由于您链接到2.6文档,我会假设您是' ;在那里使用。我要做的另一个假设是你正在使用reactive-mongo库。无论您是否正在使用该库的播放插件,我都会在这里给您两个不同的答案:
在该库中,没有插件,您已为您的案例类定义了BSONDocumentReader和BSONDocumentWriter。这可能是使用宏自动为您生成的,但无论如何获得它,这两个类都有可用于将读取/写入转换为另一个类的有用方法。所以,让我们说我为你定义了一个读者和作家:
import reactivemongo.bson._
case class FormData(_id: Option[BSONObjectID], stadiumArr: Option[List[String]])
implicit val formDataReaderWriter = new BSONDocumentReader[FormData] with BSONDocumentWriter[FormData] {
def read(bson: BSONDocument): FormData = {
FormData(
_id = bson.getAs[BSONObjectID]("_id"),
stadiumArr = bson.getAs[List[String]]("stadiumArr").map(_.filterNot(_.isEmpty))
)
}
def write(formData: FormData) = {
BSONDocument(
"_id" -> formData._id,
"stadiumArr" -> formData.stadiumArr
)
}
}
很棒,你说,有效!您可以在读取中看到我前进并过滤掉任何空字符串。因此,即使它在数据中,也可以清理它。这很好,但是请注意,我没有为写入做同样的事情。我这样做了,所以我可以告诉你如何使用一个名为afterWrite
的有用方法。所以假装读者/作者不是同一个班级并且是分开的,那么我可以这样做:
val initialWriter = new BSONDocumentWriter[FormData] {
def write(formData: FormData) = {
BSONDocument(
"_id" -> formData._id,
"stadiumArr" -> formData.stadiumArr
)
}
}
implicit val cleanWriter = initialWriter.afterWrite { bsonDocument =>
val fixedField = bsonDocument.getAs[List[String]]("stadiumArr").map(_.filterNot(_.isEmpty))
bsonDocument.remove("stadiumArr") ++ BSONDocument("stadiumArr" -> fixedField)
}
请注意cleanWriter
是隐式的,这意味着当对集合进行插入调用时,它将是选择使用的那个。
现在,这是一大堆工作,如果您正在使用插件/模块进行游戏,可以让您使用JSONCollection
,那么您可以通过定义play json { {3}}和Reads。如果您查看文档,您会看到读取特征具有有用的map
函数,您可以使用它将一个Read转换为另一个。
所以,你有:
val jsonReads = Json.reads[FormData]
implicit val cleanReads = jsonReads.map(formData => formData.copy(stadiumArr = formData.stadiumArr.map(_.filterNot(_.isEmpty))))
同样,因为只有干净的Reads是隐式的,mongo的收集方法才会使用它。
现在,所有这些都说,在数据库级别执行此操作是一回事,但实际上,我个人认为您应该在表单级别处理此问题。
val form = Form(
mapping(
"_id" -> ignored(Option.empty[BSONObjectID]),
"stadiumArr" -> optional(list(text)),
...
)(FormData.apply)(FormData.unapply)
)
主要是因为,惊喜,表格有办法解决这个问题。具体来说,Writes类本身。如果你看那里,你会找到一个transform
方法,你可以用来轻松过滤掉空值。只需在需要修改的映射上调用它,例如:
"stadiumArr" -> optional(
list(text).transform(l => l.filter(_.nonEmpty), l => l.filter(_.nonEmpty))
)
如果您不习惯阅读scaladoc中的签名,请详细了解此方法。
def
transform[B](f1: (T) ⇒ B, f2: (B) ⇒ T): Mapping[B]
说通过在类型transform
的某些映射上调用Mapping[T]
,您可以创建类型为Mapping[B]
的新映射。为此,您必须提供从一个转换为另一个的函数。所以上面的代码导致列表映射(Mapping[List[String]]
)成为Mapping[List[String]]
(这里的类型没有改变),但是当它这样做时它会删除所有空元素。如果我稍微破解这段代码,可能会更清楚:
def convertFromTtoB(list: List[String]): List[String] = list.filter(_.nonEmpty)
def convertFromBtoT(list: List[String]): List[String] = list.filter(_.nonEmpty)
...
list(text).transform(convertFromTtoB, convertFromBtoT)
您可能想知道为什么需要同时提供这两个,原因是当您调用Form.fill
并且表单填充了值时,将调用第二个方法,以便数据进入播放表单的格式期待。如果类型实际发生变化,则更为明显。例如,如果您有一个文本区域,人们可以在其中输入CSV,但您想将其映射到具有正确List [String]的表单模型,您可能会执行以下操作:
def convertFromTtoB(raw: String): List[String] = raw.split(",").filter(_.nonEmpty)
def convertFromBtoT(list: List[String]): String = list.mkString(",")
...
text.transform(convertFromTtoB, convertFromBtoT)
请注意,当我过去做过这个时,有时候我必须编写一个单独的方法,如果我不想完全指定所有类型,只需将其传入,但是你应该能够在这里工作,给出映射变换方法的文档和类型签名。
我建议在表单绑定中执行此操作的原因是因为表单/控制器应该是一个关注处理用户数据和清理的问题。但是你总是可以有多层清洁等等,安全性也不错!