使用 Play Framework 2.5 的 ReactiveMongo 数据库转储

ReactiveMongo database dump with Play Framework 2.5

我正在尝试将我的 mongo 数据库转储到 json 对象中,但是因为我对数据库的查询是异步的,所以我遇到了问题。

我数据库中的每个集合都包含用户数据,每个集合名称都是一个用户名。

因此,当我想获取所有用户数据时,我会恢复所有集合名称,然后遍历它们以逐个恢复每个集合。

def databaseDump(prom : Promise[JsObject]) = {
    for{
      dbUsers <- getUsers
    } yield dbUsers

var rebuiltJson = Json.obj()
var array = JsArray()
res.map{ users =>
  users.map{ userNames =>
    if(userNames.size == 0){
      prom failure new Throwable("Empty database")
    }
    var counter = 0
    userNames.foreach { username =>
      getUserTables(username).map { tables =>
         /* Add data to array*/
           ...
        counter += 1
        if(counter == userNames.size){
          /*Add data to new json*/
             ...
          prom success rebuiltJson
        }

      } 
    }
  }
}

这有点管用,但有时即使尚未恢复所有数据,也会成功触发 promise。这是因为我的计数器变量不是可靠的解决方案。

有没有办法遍历所有用户,查询数据库,等待所有数据恢复后,再成功触发promise?我尝试使用 for comprehension 但没有找到方法。有没有办法将整个 mongo 数据库转储到一个 Json : { username : data, username : data ..} ?

users/tables 术语让我感到困惑,所以我编写了一个新函数,将数据库转储到单个 JsObject

// helper function to find all documents inside a collection c
// and return them as a single JsArray
def getDocs(c: JSONCollection)(implicit ec: ExecutionContext) = c.find(Json.obj()).cursor[JsObject]().jsArray()

def dumpToJsObject(db: DefaultDB)(implicit ec: ExecutionContext): Future[JsObject] = {
  // get a list of all collections in the db
  val collectionNames = db.collectionNames
  val collections = collectionNames.map(_.map(db.collection[JSONCollection](_)))

  // each entry is a tuple collectionName -> content (as JsArray)
  val namesToDocs = collections.flatMap {
    colls => Future.sequence(colls.map(c => getDocs(c).map(c.name -> _)))
  }

  // convert to a single JsObject
  namesToDocs.map(JsObject(_))
}

我还没有测试过(稍后会测试),但是这个功能至少应该给你一个大概的概念。您将获得数据库中所有集合的列表。对于每个集合,您执行查询以获取该集合中的所有文档。文档列表被转换成一个JsArray,最后所有的集合组成一个JsObject,集合名称作为键。

如果目标是将数据写入输出流(local/file 或网络),有副作用。

import scala.concurrent.{ ExecutionContext, Future }
import reactivemongo.bson.BSONDocument
import reactivemongo.api.{ Cursor, MongoDriver, MongoConnection }

val mongoUri = "mongodb://localhost:27017/my_db"

val driver = new MongoDriver
val maxDocs = Int.MaxValue // max per collection

// Requires to have an ExecutionContext in the scope
// (e.g. `import scala.concurrent.ExecutionContext.Implicits.global`)
def dump()(implicit ec: ExecutionContext): Future[Unit] = for {
  uri <- Future.fromTry(MongoConnection.parseURI(mongoUri))
  con = driver.connection(uri)
  dn <- Future(uri.db.get)
  db <- con.database(dn)
  cn <- db.collectionNames
  _  <- Future.sequence(cn.map { collName =>
    println(s"Collection: $collName")

    db.collection(collName).find(BSONDocument.empty). // findAll
      cursor[BSONDocument]().foldWhile({}, maxDocs) { (_, doc) =>
        // Replace println by appropriate side-effect
        Cursor.Cont(println(s"- ${BSONDocument pretty doc}"))
      }
  })
} yield ()

如果使用 JSON 序列化包,只需将 BSONDocument 替换为 JsObject(例如 BSONDocument.empty ~> Json.obj())。

如果从Scala REPL测试,粘贴之前的代码后,可以执行如下。

dump().onComplete {
  case result =>
    println(s"Dump result: $result")
    //driver.close()
}