防止插入Mongo集合的数组中的空值
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了防止插入Mongo集合的数组中的空值相关的知识,希望对你有一定的参考价值。
我试图阻止空值插入我的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))
}
..但这不起作用。有关如何防止空值插入数据库集合的任何想法?
如果不知道您正在使用的特定版本或库,很难给出答案,但由于您链接到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
s然后你可以通过定义播放json Reads和Writes。如果你查看文档,你会看到read trait有一个有用的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)
)
主要是因为,惊喜,形式有办法对付这个。具体来说,mapping类本身。如果你看那里你会找到一个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)
请注意,当我在过去完成此操作时,有时候我必须编写一个单独的方法,如果我不想完全指定所有类型,只需将其传入,但是你应该可以在这里工作了映射的转换方法的文档和类型签名。
我建议在表单绑定中这样做的原因是因为表单/控制器应该是关注处理用户数据和清理的问题。但你总是可以有多层清洁等等,安全起见并不坏!
我已经离开了这个(当它被编写和测试时总是显而易见的):
implicit val arrayWrite: Writes[List[String]] = new Writes[List[String]] {
def writes(list: List[String]): JsValue = Json.toJson(list.filterNot(_.isEmpty).toIndexedSeq)
}
但我有兴趣知道如何
.map现有的Read而不是从头开始重新定义
正如@cchantep建议的那样
以上是关于防止插入Mongo集合的数组中的空值的主要内容,如果未能解决你的问题,请参考以下文章
Apache Spark:如何使用 Java 在 dataFrame 中的空值列中插入数据