Play-Mongo 正在参加 2020 年度 OSC 中国开源项目评选,请投票支持!
Play-Mongo 在 2020 年度 OSC 中国开源项目评选 中已获得 {{ projectVoteCount }} 票,请投票支持!
投票让它出道
已投票
Play-Mongo 获得 2020 年度 OSC 中国开源项目评选「最佳人气项目」 !
Play-Mongo 获得 2020 年度 OSC 中国开源项目评选「最佳人气项目」「最积极运营项目」 !
Play-Mongo 获得 2020 年度 OSC 中国开源项目评选「最积极运营项目」 !
授权协议 Apache
开发语言 Scala 查看源码 »
操作系统 跨平台
软件类型 开源软件
开源组织
地区 国产
提 交 者 joymufeng
适用人群 未知
收录时间 2018-06-10

软件简介

play-mongo 介绍

play-mongo 是一个专门为 Play Framework 开发的 Mongodb 模块, 旨在为 Play Framework 提供一种简洁的 Mongodb 访问方式。 该项目基于 Mongodb 官方的 Mongodb Scala Driver 开发,并且提供了更多实用功能,例如,

  • 更简洁多样的数据库交方式

  • 自动识别模型类(Model),自动编解码

  • 自动完成 JsValue 和 BsonValue 互转

  • 更方便的 GridFS 交互

  • Change Stream 转 Akka Stream.

  • 支持关联查询(Relationship Query)

Getting Started

打开build.sbt,添加如下依赖,

libraryDependencies += "cn.playscala" % "play-mongo_2.12" % "0.1.0"

打开 conf/application.conf, 添加数据库连接,

mongodb.uri = "mongodb://user:password@host:port/play-community?authMode=scram-sha1"

然后需要配置 Model 位置, 配置代码需要在应用启动之前执行,

Mongo.setModelsPackage("models")

建议将上述代码放置在顶层包路径下的默认 Module 类中,

class Module extends AbstractModule {
  override def configure() = {
    Mongo.setModelsPackage("models")
    bind(classOf[InitializeService]).asEagerSingleton
  }
}

至此便可以将 Mongo 实例注入到任意需要的地方,

@Singleton
class Application @Inject()(cc: ControllerComponents, mongo: Mongo) extends AbstractController(cc) {}

Model and Collection

Model 类使用 @Entity 注解标注, 一个 model 实例表示 mongodb collection 中的一个文档, 一个 mongodb collection 在概念上类似于关系数据库的一张表。

@Entity("common-user")
case class User(_id: String, name: String, password: String, addTime: Instant)

@Entity 注解参数用于指定关联的 mongodb collection 名称, 如果未指定,则默认为 Model 类名称。 作为约定,Model 类使用 _id 字段作为唯一标识, 该字段同时也是 mongodb collection 的默认主键。

我们可以通过两种方式访问 mongodb collection, 第一种方式是使用 model 类,

mongo.find[User]().list().map{ users => ... }

这里的参数类型 User 不仅用于指定关联的 mongodb collection, 而且用于指明返回的结果类型。 这意味着查询操作将会在 common-user collection 上执行, 并且返回的结果类型是 User。 需要注意的是,在该方式下无法改变返回的结果类型。

第二种方式是使用 mongo.getCollection 方法,

mongo.collection("common-user").find[User]().list().map{ user => }

在这里, find 方法上的参数类型 User 仅仅用于指定返回的结果类型, 我们可以通过更改该参数类型设置不同的返回结果类型,

mongo.collection("common-user").find[JsObject]().list().map{ jsObjList => }
mongo.collection("common-user").find[CommonUser](Json.obj("userType" -> "common")).list().map{ commonUsers => }

当然,我们也可以使用 model 类指定关联的 mongodb collection,

mongo.collection[User].find[User]().list().map{ user => }

第1个参数类型 User 用于指定关联的 mongodb collection, 第2个参数类型 User 用于指定返回的结果类型。 我们仍然可以通过改变第2个参数类型从而改变返回的结果类型。

常用操作

以下示例代码默认执行了 import play.api.libs.json.Json._ 导入, 所以 Json.obj() 可以被简写为 obj() 。

Create

// 插入 Model
mongo.insert[User](User("0", "joymufeng", "123456", Instant.now))

// 插入 Json
val jsObj = obj("_id" -> "0", "name" -> "joymufeng", "password" -> "123456", "addTime" -> Instant.now)
mongo.collection[User].insert(jsObj)
mongo.collection("common-user").insert(jsObj)

Update

mongo.updateById[User]("0", obj("$set" -> obj("password" -> "123321")))
mongo.updateOne[User](obj("_id" -> "0"), obj("$set" -> obj("password" -> "123321")))

mongo.collection[User].updateById("0", obj("$set" -> obj("password" -> "123321")))
mongo.collection[User].updateOne(obj("_id" -> "0"), obj("$set" -> obj("password" -> "123321")))

mongo.collection("common-user").updateById("0", obj("$set" -> obj("password" -> "123321")))
mongo.collection("common-user").updateOne(obj("_id" -> "0"), obj("$set" -> obj("password" -> "123321")))

Query

mongo.findById[User]("0") // Future[Option[User]]
mongo.find[User](obj("_id" -> "0")).first // Future[Option[User]]

mongo.collection[User].findById[User]("0") // Future[Option[User]]
mongo.collection[User].find[User](obj("_id" -> "0")).first // Future[Option[User]]

mongo.collection[User].findById[JsObject]("0") // Future[Option[JsObject]]
mongo.collection[User].find[JsObject](obj("_id" -> "0")).first // Future[Option[JsObject]]

mongo.collection("common-user").findById[User]("0") // Future[Option[User]]
mongo.collection("common-user").find[User](obj("_id" -> "0")).first // Future[Option[User]]

mongo.collection("common-user").findById[JsObject]("0") // Future[Option[JsObject]]
mongo.collection("common-user").find[JsObject](obj("_id" -> "0")).first // Future[Option[JsObject]]

Delete

mongo.deleteById[User]("0")
mongo.deleteOne[User](obj("_id" -> "0"))

mongo.collection[User].deleteById("0")
mongo.collection[User].deleteOne(obj("_id" -> "0"))

mongo.collection("common-user").deleteById("0")
mongo.collection("common-user").deleteOne(obj("_id" -> "0"))

Upload and Download Files

// Upload and get the fileId
mongo.gridFSBucket.uploadFromFile("image.jpg", "image/jpg", new File("./image.jpg")).map{ fileId =>
  Ok(fileId)
}

// Download file by fileId
mongo.gridFSBucket.findById("5b1183fed3ba643a3826325f").map{
  case Some(file) =>
    Ok.chunked(file.stream.toSource)
      .as(file.getContentType)
  case None =>
    NotFound
}

Change Stream

我们可以通过 toSource 方法将 Change Stream 转换成 Akka Source,之后便会有趣很多。例如下面的代码拥有如下几个功能:

  • 将从 Change Stream 接收到的元素进行缓冲,以方便批处理,当满足其中一个条件时便结束缓冲向后传递:

    • 缓冲满10个元素

    • 缓冲时间超过了1000毫秒

  • 对缓冲后的元素进行流控,每秒只允许通过1个元素

mongo
  .collection[User]
  .watch()
  .fullDocument
  .toSource
  .groupedWithin(10, 1000.millis)
  .throttle(elements = 1, per = 1.second, maximumBurst = 1, ThrottleMode.shaping)
  .runForeach{ seq => 
    // ...
  }

Relationship Query

@Entity("common-article")
case class Article(_id: String, title: String, content: String, authorId: String)

@Entity("common-author")
case class Author(_id: String, name: String)

mongo.find[Article].fetch[Author]("authorId").list().map{ _.map{ t =>
    val (article, author) = t
  }
}

对于满足查询条件的每一个 article , 将会根据匹配条件 article.authorId == author._id 拉取关联的 author。

Class, Json 和 Bson

在处理 Json 时要格外小心,因为 Json 使用 JsNumber 表示所有数值类型,但是 Bson 拥有更加丰富的数值类型,这导致了 Json 和 Bson 之间的转换过程是不可逆的,因为双方的类型信息并不对称。 下面我们仔细分析常见的几个场景。在讨论中将会用到如下的 Model 定义:

@Entity("common-user")
case class User(_id: String, name: String, setting: UserSetting)
case class UserSetting(gender: String, age: Int)

Class -> Bson

我们经常使用下面代码将一个 Model 类实例插入 mongodb ,

mongo.insert[User].insert(User("0", "joymufeng", UserSetting("male", 32)))

在调用底层驱动的插入操作之前,需要先将 User 转换成 Bson 。这个转换过程是可逆的,当从 mongodb 读取数据时,可以成功地将 Bson 转换回 User

Json -> Bson

当使用 Json DSL 构建一个 JsObject 对象时,所有的数值类型(例如 Byte, Short, Int, Long, Float 和 Double)均会被转换成 JsNumber 类型(内部使用BigDecimal存储数据),数值的具体类型在这个转换过程中丢失了。 在调用底层驱动程序前,Json 将会被转换为 BsonJsNumber 将会被转换为 BsonDecimal128。当从数据库读取写入的数据时,我们没办法恢复已经丢失的数值类型信息。 例如我们通常会执行如下更新操作,

mongo.update[User](Json.obj("_id" -> "0"), Json.obj("$set" -> UserSetting("male", 18)))
// Or
mongo.update[User](Json.obj("_id" -> "0"), Json.obj("$set" -> Json.obj("setting" -> Json.obj("gender" -> "male", "age" -> 18))))

不管是 UserSetting("male", 32), 还是 Json.obj("gender" -> "male", "age" -> 18) 最终都会被转换为 Json.obj("gender" -> JsString("male"), "age" -> JsNumber(BigDecimal(18))。 所以,在更新操作执行完成后, user.setting.age 字段在数据库中的类型为 NumberDecimal, 当执行读取操作时便会发生类型转换错误,

mongo.findById[User]("0")
// [BsonInvalidOperationException: Invalid numeric type, found: DECIMAL128]

当试图将 BigDecimal 转换为 Int 时出错了. 因为在这个转换过程中会导致数值精度丢失。 为了解决这个问题, 我们在转换 JsNumber 时尽量将其转换为较窄的数值类型,以保证其可以被安全地转换回来。 例如 Json.obj("age" -> JsNumber(18.0))会被转换为 BsonDocument("age", BsonInt32(18))

展开阅读全文

代码

的 Gitee 指数为
超过 的项目

评论 (1)

加载中
joymufeng软件作者
打分: 力荐
play-mongo 0.2.0 发布了,新增了 implicit macro,可以简化 play json 的开发。
2018/07/01 11:08
回复
举报
更多评论
发表于数据库专区
2018/08/07 06:42

Play-Mongo 0.3.0,重构自动生成 Json Formats 模块

Play-Mongo 0.3.0 已发布。 Play-Mongo 0.2.0 版本通过 implicit macro 实现自动生成 Json Formats,代码如下: package object models {   implicit def formats[T <: Product]: Format[T] = macro JsonFormatMacro.materializeJsonFormat[T] } 但是在实际使用中发现,sbt 的增量编译和热加载有时会导致自动生成失效。故 Play-Mongo 0.3.0 版本使用 macro annotation 重新实现了该功能,用法如下: package o...

0
1
发表于数据库专区
2018/07/01 11:13

Play Mongo 0.2.0 发布,新增 implicit macro 方法

0.2.0 更新日志: 新增一个 implicit macro 方法,可以自动为 case classes 生成相应的 Format 对象。 新功能介绍 借助 Play Json 提供的Json.format宏,我们可以很方便地为 case class 提供隐式的Reads和Writes, import models._ import play.api.libs.json.Format package object models {   implicit val emailFormat = Json.format[Email]   implicit val personFormat = Json.format[Person]   ....

4
3
没有更多内容
加载失败,请刷新页面
点击加载更多
加载中
下一页
发表于服务端专区
2018/07/01 10:54

Play Mongo 模块简介

## Play Mongo 是什么? [Play Mongo](https://github.com/playcommunity/play-mongo "Play Mongo") 是一个专门为 [Play Framework](https://www.playframework.com/) 开发的 MongoDB 模块, 该项目基于 MongoDB 官方的 Scala 驱动,并且提供了更多的实用功能,例如, - 更简洁多样的数据库交方式 - 自动识别模型类(Model),自动编解码 - 自动完成 JsValue 和 BsonValue 互转 - 更方便的 GridFS 交互 - Change Stream 转 Akka S...

0
0
发表于软件架构专区
2018/07/01 11:01

使用 Play Mongo 简化 Play Json 开发

[Play Mongo](https://github.com/playcommunity/play-mongo "Play Mongo") 是一个专门为 [Play Framework](https://www.playframework.com/) 开发的 MongoDB 模块,该模块提供了一个隐式方法可以简化 Play Json 的开发工作。 通常情况下,我们需要借助 Play Json 提供的`Json.format`宏为 case class 提供隐式的Reads和Writes, ``` import models._ import play.api.libs.json.Format package object models { implicit val ...

0
0
发表了博客
2020/10/28 11:51

mongo

mongo 如:如果你想创建一个“myTest”的数据库,先运行use myTest命令,之后就做一些操作(如:db.createCollection(‘user’)),这样就可以创建一个名叫“myTest”的数据库,该库下有一个表名为"user" 一、数据库常用命令 1、Help查看命令提示 代码如下: help db.help(); db.yourColl.help(); db.youColl.find().help(); rs.help(); 2、切换/创建数据库 代码如下: use yourDB; 当创建一个集合(table)的时候会自动创建当前数据...

0
0
发表于服务端专区
2020/06/20 23:08

用docker部署mongo和mongo-express

1. 拉取镜像 2. 运行相关镜像 * 这里采用docker-compose合并,yml文件配置如下: ``` version: '3.1' services: mongo: image: mongo // 镜像名 restart: always ports: - 27017:27017 mongo-express: image: mongo-express restart: always ports: - 8081:8081 ``` * 直接运行 `docker-compose up`, 如果有多个yml文件最好指明下要运行的yml模板文件,例如: ` docker-compose -f youFileName.yml up` , `-f` 表示制定compose模...

0
0
发表于服务端专区
2015/11/16 11:12

mongo

## 概念 ### 文档 **文档是MongoDB的核心** **注:** 1. 文档中键值对是无序的,不同的文档只是因为ID区分 1. 文档中的键值不但区分大小写,还区分类型 1. 文档的键是字符串。(除少数情况外,可以是任意的UTF-8字符) - _开始的键是保留的 - 键不能含有空字符 - .和$保留 最好不用 1. 文档的键不能重复 ### 集合 1. 文档类似于关系行数据库中的行,集合类似于关系型数据库中的表 1. 集合是无模式的:集合里面可以放置任何文档 **...

0
0
发表于AI & 大数据专区
2019/08/05 17:28

mongo-engine配置mongo加密信息

config配置 对于mongo有加密填写username, password, authentication_source 多库配置用alias区分(不填则默认为default), 在后面使用时会用到 MONGODB_SETTINGS = [ { "db": "openplatform_datacenter", "host": ["localhost:27017", "localhost:27018"], "username": "", "password": "", "authentication_source": "admin", }, { "db": "test", ...

0
0
2018/02/24 21:28

Mongo概括

Mongo简介 mongo是文档数据库,json存储结构,提供了灵活的数据类型,可以快速上手,快速开发,随着数据量的增加,有复制集,sharding模式用来水平扩展。但是他的查询模式在单机模式,复制集模式到sharding模式中有变更,即sharding模式下sharding key查询与复制集模式下任意条件查询不是很符合,相当于关系型数据库的分库分表,还是需要更改应用程序中查询语句 PS:根据个人的NoSQL经验,我对于mongo的态度是用于快速开发,适合...

0
0
2018/05/06 16:09

mongo索引

----------mongo系列文章------------- Mongo概括 NoSQL概述-从Mongo和Cassandra谈谈NoSQL Mongo连接分析 mongo实现自增id Spring Repository解析---以Mongo Repository为例 ----------mongo系列文章------------- 摘要 mongo 的索引非常强大,和关系型数据库索引没什么区别。这里主要介绍mongo索引基本知识和mongo本人在索引上的犯的错。 索引种类 单字段索引 复合索引复合索引各个字段的顺序应该是精确匹配字段(=xxx),排序字段...

0
0
发表于数据库专区
2014/10/05 14:29

mongo总结

看完了基本操作,占个坑继续补充。。。 安装不用说了,mongo下载最新版本,解压就可以使用 启动服务 sudo ./mongod --dbpath=/data/server/mongodb/data/ --logpath=/data/server/mongodb/dblogs --fork 启动的时候指定数据文件地址,日志文件地址,--fork或者最末尾加&表示后台运行 启动客户端,进入mongo shell,shell是一个强大的javascript解释器,可以执行任何javascript代码,同时他是一个mongo客户端 ./mongo 简单的mon...

0
1
没有更多内容
加载失败,请刷新页面
点击加载更多
加载中
下一页
暂无内容
1 评论
5 收藏
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部