我有一个 MongoDB 数据库,其中存储不同的文件,这些文件可以是 png,jpg,pdf,这里是一个例子:
{
"_id" : "id of the document",
"metadata" : {
"s" : "documents",
"u" : "id of the owner",
"d" : ""
},
"filename" : "El-jugador-Fedor-Dostoyevski.pdf",
"contentType" : "application/pdf",
}
我删除了一些不相关的字段,文档的类型由
contentType
字段给出,我想要的是使用 Scala 和 ReactiveMongo 获取每种类型文件的计数
我已经做了,但是做了三次咨询,这样:
def contentTypeStats(implicit ec: ExecutionContext): Future[ContentTypesDTO] = {
collectionFactory.collection().flatMap(collection => {
val filterDocuments = BSONDocument("metadata.s" -> BSONDocument("$ne" -> "thumbnail"))//don't want count thumbnails, only documents
val filterPNG = BSONDocument(filterDocuments, "contentType" -> "image/png")
val filterJPG = BSONDocument(filterDocuments, "contentType" -> "image/jpeg")
val filterPDF = BSONDocument(filterDocuments, "contentType" -> "application/pdf")
val countPNGFuture: Future[Long] = collection.count(Some(filterPNG))
val countJPGFuture: Future[Long] = collection.count(Some(filterJPG))
val countPDFFuture: Future[Long] = collection.count(Some(filterPDF))
for {
countPNG <- countPNGFuture
countJPG <- countJPGFuture
countPDF <- countPDFFuture
} yield {
ContentTypesDTO(
pngCount = countPNG,
jpgCount = countJPG,
pdfCount = countPDF
)
}
})
}
我只想通过一次咨询来完成此操作,在 MongoDB 中我这样做:
db.getCollection('myCollection').aggregate([
{$match: {'metadata.s': {$ne: 'thumbnail'}}},
{$group: {_id: "$contentType", count: {$sum: 1}} }
])
返回给我这个:
/* 1 */
{
"_id" : "image/png",
"count" : 5.0
}
/* 2 */
{
"_id" : "application/pdf",
"count" : 9.0
}
/* 3 */
{
"_id" : "image/jpeg",
"count" : 8.0
}
我尝试这样:
def contentTypeStats(implicit ec: ExecutionContext): Future[ContentTypesDTO] = {
collectionFactory.collection().flatMap(collection => {
import collection.AggregationFramework._
val result: Future[Option[BSONDocument]] = collection.aggregatorContext[BSONDocument](
pipeline = List(
Match(BSONDocument("metadata.s" -> BSONDocument("$ne" -> "thumbnail"))),
Group(BSONDocument("_id" -> "$contentType"))("count" -> SumAll)
)
).prepared.cursor.headOption
result.map {
case Some(doc) =>
println(doc.getAsOpt[String]("_id"))//here always return None
ContentTypesDTO(
pngCount = doc.getAsOpt[Long]("count").getOrElse(0L),
jpgCount = doc.getAsOpt[Long]("count").getOrElse(0L),
pdfCount = doc.getAsOpt[Long]("count").getOrElse(0L)
)//all have the same number
}
})
}
当请求
None
时,该方法返回 _id
并且计数字段随机给出一些先前的结果(5、8、9),它应该是访问每个 _id
的特定计数字段的方法应该是 image/png
或 image/jpeg
或 application/pdf
但如果我能得到 _id
该怎么办
我的解决方案中存在一些问题
val result: Future[Option[BSONDocument]] = collection.aggregatorContext[BSONDocument](
pipeline = List(
Match(BSONDocument("metadata.s" -> BSONDocument("$ne" -> "thumbnail"))),
Group(BSONDocument("_id" -> "$contentType"))("count" -> SumAll)
)
).prepared.cursor.headOption
这里我将
_id
映射为BSONDocument
,这就是为什么a无法获得_id
,所以解决方案就是以这种方式映射为BSONString
Group(BSONString("$contentType"))("count" -> SumAll)
Group
方法始终使用第一个参数创建 _id
字段。
第二个问题是返回结果,这里 .prepared.cursor.headOption
只返回创建组中的第一个 BSONDocument 。使用reactivemongo中的Cursor类来修复
.prepared.cursor.collect[List](-1, Cursor.FailOnError[List[BSONDocument]]())
返回给我一个 BSONDocument 列表
之后还将 ContentTypesDTO
更改为
case class ContentTypesDTO(contentType: String,
count: Long)
并使用
result
上的地图来获取 Seq[ContentTypesDTO]
这是最终的解决方案:
def contentTypeStats(implicit ec: ExecutionContext): Future[Seq[ContentTypeDetailsDTO]] = {
collectionFactory.collection().flatMap(collection => {
import collection.AggregationFramework.{Group, Match, SumAll}
val result: Future[List[BSONDocument]] = collection.aggregatorContext[BSONDocument](
List(
Match(BSONDocument("metadata.s" -> BSONDocument("$ne" -> "thumbnail"))),
Group(BSONString("$contentType"))("count" -> SumAll))
).prepared.cursor.collect[List](-1, Cursor.FailOnError[List[BSONDocument]]())
result.map(docList => {
docList.map {
doc =>
ContentTypeDetailsDTO(
contentType = doc.getAsOpt[String]("_id").getOrElse(""),
count = doc.getAsOpt[Long]("count").getOrElse(0L)
)
}
})
})
}
该方法返回此:
[
{
"contentType": "application/pdf",
"count": 9
},
{
"contentType": "image/svg",
"count": 1
},
{
"contentType": "image/png",
"count": 4
},
{
"contentType": "image/jpeg",
"count": 8
}
]