SDP(9):MongoDB-Scala - data access and modeling
MongoDB是一种文件型数据库,对数据格式没有硬性要求,所以可以实现灵活多变的数据存储和读取。MongoDB又是一种分布式数据库,与传统关系数据库不同的是,分布式数据库不支持table-join,所以在设计数据库表结构方面与关系数据库有很大的不同。分布式数据库有一套与传统观念不同的数据模式,在设计库表结构时必须从满足各种数据抽取的需要为主要目的。关系数据库设计要求遵循范式模式(normalization)库表结构,在抽取数据时再通过table-join联结关系表。因为分布式数据库不支持table-join,在读取跨表数据时就需要多次抽取,影响数据处理的效率。MongoDB作为文件型数据库最大的特点就是容许嵌入Document:我们可以把相关联的Document嵌入在另一个关联Document中,这样就可以一次性读取全部数据,实现反范式(denormalization)的数据模式了。这方面MongoDB比Cassandra更加优胜。MongoDB支持灵活多样的索引方式,使它成为提供高效数据读取的分布式数据库最佳选择。另外,MongoDB还通过提供sort、aggregation、map-reduce来支持丰富强大的大数据统计功能。
在使用MongoDB前我们必须熟悉它的数据模式和设计理念:在大数据时代的今天,数据的产生和使用发生了质的变化,传统关系数据库数据模式已经无法满足现代信息系统的要求。比如,在设计个人信息表时要考虑有些人有两个地址,有些甚至没有地址,又有些有传真号,还有这个那个的其它特点等等。在关系数据库模式设计中我们必须作出取舍,牺牲一些属性。但MongoDB的文件类数据库特点容许不同的数据格式,能实现完整的数据采集与储存。下面是一个采购单的Document设计:
val po1 = Document (
"ponum" -> "po18012301",
"vendor" -> "The smartphone compay",
"podate" -> podate1,
"remarks" -> "urgent, rush order",
"handler" -> pic,
"podtl" -> Seq(
Document("item" -> "sony smartphone", "price" -> 2389.00, "qty" -> , "packing" -> "standard"),
Document("item" -> "ericson smartphone", "price" -> 897.00, "qty" -> , "payterm" -> "30 days")
)
) val po2 = Document (
"ponum" -> "po18022002",
"vendor" -> "The Samsung compay",
"podate" -> podate2,
"podtl" -> Seq(
Document("item" -> "samsung galaxy s8", "price" -> 2300.00, "qty" -> , "packing" -> "standard"),
Document("item" -> "samsung galaxy s7", "price" -> 1897.00, "qty" -> , "payterm" -> "30 days"),
Document("item" -> "apple iphone7", "price" -> 6500.00, "qty" -> , "packing" -> "luxury")
)
)
po1和po2都在podtl键嵌入了多条采购项目Document。首先,po1与po2有结构上的不同:po1多出了remarks、handler这两个键。嵌入的Document各自也有不同的结构。在这个例子里我特别加了date、binary、array类型的使用示范:
val ca = Calendar.getInstance()
ca.set(,,)
val podate1 = ca.getTime
ca.set(,,)
val podate2 = ca.getTime val pic = FileToByteArray("/users/tiger-macpro/sample.png", seconds)
MongoDB的Date是java.util.Date,可以用Calendar来操作。再看看下面类型转换中的数据类型对应:
case class PO (
ponum: String,
podate: java.util.Date,
vendor: String,
remarks: Option[String],
podtl: Option[BsonArray],
handler: Option[BsonBinary]
)
def toPO(doc: Document): PO = {
val ks = doc.keySet
PO(
ponum = doc.getString("ponum"),
podate = doc.getDate("podate"),
vendor = doc.getString("vendor"),
remarks = {
if (ks.contains("remarks"))
Some(doc.getString("remarks"))
else
None
},
podtl = {
if (ks.contains("podtl"))
doc.get("podtl").asInstanceOf[Option[BsonArray]]
else
None
},
handler = {
if (ks.contains("handler"))
doc.get("handler").asInstanceOf[Option[BsonBinary]]
else
None
}
)
} case class PODTL(
item: String,
price: Double,
qty: Int,
packing: Option[String],
payTerm: Option[String]
)
def toPODTL(podtl: Document): PODTL = {
val ks = podtl.keySet
PODTL(
item = podtl.getString("item"),
price = podtl.getDouble("price"),
qty = podtl.getInteger("qty"),
packing = {
if (ks.contains("packing"))
Some(podtl.getString("packing"))
else None
},
payTerm = {
if(ks.contains("payterm"))
Some(podtl.getString("payterm"))
else None
}
)
}
注意BsonBinary和BsonArray这两个类型和它们的使用方法。我们可以用嵌入Document的键作为查询条件:
poCollection.find(equal("podtl.qty",)).toFuture().onComplete {
case Success(docs) => docs.map(toPO).foreach (showPO)
println("-------------------------------")
case Failure(e) => println(e.getMessage)
}
我们可以用toPO和toPODTL把po,podtl对应到case class,然后用强类型方式来使用它们:
def showPO(po: PO) = {
println(s"po number: ${po.ponum}")
println(s"po date: ${po.podate.toString}")
println(s"vendor: ${po.vendor}")
if (po.remarks != None)
println(s"remarks: ${po.remarks.get}")
po.podtl match {
case Some(barr) =>
val docs = barr.getValues.asScala.toList
docs.map { dc =>
toPODTL(dc.asInstanceOf[org.bson.BsonDocument])
}.foreach { doc: PODTL =>
print(s"==>Item: ${doc.item} ")
print(s"price: ${doc.price} ")
print(s"qty: ${doc.qty} ")
doc.packing.foreach(pk => print(s"packing: ${pk} "))
doc.payTerm.foreach(pt => print(s"payTerm: ${pt} "))
println("")
}
case _ =>
}
po.handler match {
case Some(bs) =>
val fileName = s"/users/tiger-macpro/${po.ponum}.png"
ByteArrayToFile(bs.getData,fileName)
println(s"picture saved to ${fileName}")
case None => println("no picture provided")
}
}
poCollection.find(equal("podtl.qty",)).toFuture().onComplete {
case Success(docs) => docs.map(toPO).foreach (showPO)
println("------------------------------")
case Failure(e) => println(e.getMessage)
}
poCollection.find().toFuture().onComplete {
case Success(docs) => docs.map(toPO).foreach (showPO)
println("-------------------------------")
case Failure(e) => println(e.getMessage)
}
试运行显示结果如下:
po number: po18022002
po date: Wed Jan :: HKT
vendor: The Samsung compay
==>Item: samsung galaxy s8 price: 2300.0 qty: packing: standard
==>Item: samsung galaxy s7 price: 1897.0 qty: payTerm: days
==>Item: apple iphone7 price: 6500.0 qty: packing: luxury
no picture provided
-------------------------------
po number: po18012301
po date: Wed Nov :: HKT
vendor: The smartphone compay
remarks: urgent, rush order
==>Item: sony smartphone price: 2389.0 qty: packing: standard
==>Item: ericson smartphone price: 897.0 qty: payTerm: days
picture saved to /users/tiger-macpro/po18012301.png
po number: po18022002
po date: Wed Jan :: HKT
vendor: The Samsung compay
==>Item: samsung galaxy s8 price: 2300.0 qty: packing: standard
==>Item: samsung galaxy s7 price: 1897.0 qty: payTerm: days
==>Item: apple iphone7 price: 6500.0 qty: packing: luxury
no picture provided
------------------------------
下面是本次示范的源代码:
build.sbt
name := "learn-mongo" version := "0.1" scalaVersion := "2.12.4" libraryDependencies := Seq(
"org.mongodb.scala" %% "mongo-scala-driver" % "2.2.1",
"com.lightbend.akka" %% "akka-stream-alpakka-mongodb" % "0.17"
)
FileStreaming.scala
import java.nio.file.Paths
import akka.stream.{Materializer}
import akka.stream.scaladsl.{FileIO, StreamConverters}
import scala.concurrent.{Await}
import akka.util._
import scala.concurrent.duration._
object FileStreaming {
def FileToByteBuffer(fileName: String, timeOut: FiniteDuration)(
implicit mat: Materializer):ByteBuffer = {
val fut = FileIO.fromPath(Paths.get(fileName)).runFold(ByteString()) { case (hd, bs) =>
hd ++ bs
}
(Await.result(fut, timeOut)).toByteBuffer
}
def FileToByteArray(fileName: String, timeOut: FiniteDuration)(
implicit mat: Materializer): Array[Byte] = {
val fut = FileIO.fromPath(Paths.get(fileName)).runFold(ByteString()) { case (hd, bs) =>
hd ++ bs
}
(Await.result(fut, timeOut)).toArray
}
def FileToInputStream(fileName: String, timeOut: FiniteDuration)(
implicit mat: Materializer): InputStream = {
val fut = FileIO.fromPath(Paths.get(fileName)).runFold(ByteString()) { case (hd, bs) =>
hd ++ bs
}
val buf = (Await.result(fut, timeOut)).toArray
new ByteArrayInputStream(buf)
}
def ByteBufferToFile(byteBuf: ByteBuffer, fileName: String)(
implicit mat: Materializer) = {
val ba = new Array[Byte](byteBuf.remaining())
byteBuf.get(ba,,ba.length)
val baInput = new ByteArrayInputStream(ba)
val source = StreamConverters.fromInputStream(() => baInput) //ByteBufferInputStream(bytes))
source.runWith(FileIO.toPath(Paths.get(fileName)))
}
def ByteArrayToFile(bytes: Array[Byte], fileName: String)(
implicit mat: Materializer) = {
val bb = ByteBuffer.wrap(bytes)
val baInput = new ByteArrayInputStream(bytes)
val source = StreamConverters.fromInputStream(() => baInput) //ByteBufferInputStream(bytes))
source.runWith(FileIO.toPath(Paths.get(fileName)))
}
def InputStreamToFile(is: InputStream, fileName: String)(
implicit mat: Materializer) = {
val source = StreamConverters.fromInputStream(() => is)
source.runWith(FileIO.toPath(Paths.get(fileName)))
}
}
MongoScala103.scala
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import java.util.Calendar import org.bson.BsonBinary import scala.util._
import FileStreaming._ import scala.concurrent.duration._
import org.mongodb.scala._
import org.mongodb.scala.bson.{BsonArray, BsonDocument} import scala.collection.JavaConverters._ import org.mongodb.scala.connection.ClusterSettings
import org.mongodb.scala.model.Filters._
object MongoScala103 extends App {
import Helpers._ val clusterSettings = ClusterSettings.builder()
.hosts(List(new ServerAddress("localhost:27017")).asJava).build()
val clientSettings = MongoClientSettings.builder().clusterSettings(clusterSettings).build()
val client = MongoClient(clientSettings) implicit val system = ActorSystem()
implicit val mat = ActorMaterializer()
implicit val ec = system.dispatcher val db: MongoDatabase = client.getDatabase("testdb")
val poOrgCollection: MongoCollection[Document] = db.getCollection("po")
poOrgCollection.drop.headResult()
val poCollection: MongoCollection[Document] = db.getCollection("po") val ca = Calendar.getInstance()
ca.set(,,)
val podate1 = ca.getTime
ca.set(,,)
val podate2 = ca.getTime val pic = FileToByteArray("/users/tiger-macpro/sample.png", seconds) val po1 = Document (
"ponum" -> "po18012301",
"vendor" -> "The smartphone compay",
"podate" -> podate1,
"remarks" -> "urgent, rush order",
"handler" -> pic,
"podtl" -> Seq(
Document("item" -> "sony smartphone", "price" -> 2389.00, "qty" -> , "packing" -> "standard"),
Document("item" -> "ericson smartphone", "price" -> 897.00, "qty" -> , "payterm" -> "30 days")
)
) val po2 = Document (
"ponum" -> "po18022002",
"vendor" -> "The Samsung compay",
"podate" -> podate2,
"podtl" -> Seq(
Document("item" -> "samsung galaxy s8", "price" -> 2300.00, "qty" -> , "packing" -> "standard"),
Document("item" -> "samsung galaxy s7", "price" -> 1897.00, "qty" -> , "payterm" -> "30 days"),
Document("item" -> "apple iphone7", "price" -> 6500.00, "qty" -> , "packing" -> "luxury")
)
) poCollection.insertMany(Seq(po1,po2)).headResult() case class PO (
ponum: String,
podate: java.util.Date,
vendor: String,
remarks: Option[String],
podtl: Option[BsonArray],
handler: Option[BsonBinary]
)
def toPO(doc: Document): PO = {
val ks = doc.keySet
PO(
ponum = doc.getString("ponum"),
podate = doc.getDate("podate"),
vendor = doc.getString("vendor"),
remarks = {
if (ks.contains("remarks"))
Some(doc.getString("remarks"))
else
None
},
podtl = {
if (ks.contains("podtl"))
doc.get("podtl").asInstanceOf[Option[BsonArray]]
else
None
},
handler = {
if (ks.contains("handler"))
doc.get("handler").asInstanceOf[Option[BsonBinary]]
else
None
}
)
} case class PODTL(
item: String,
price: Double,
qty: Int,
packing: Option[String],
payTerm: Option[String]
)
def toPODTL(podtl: Document): PODTL = {
val ks = podtl.keySet
PODTL(
item = podtl.getString("item"),
price = podtl.getDouble("price"),
qty = podtl.getInteger("qty"),
packing = {
if (ks.contains("packing"))
Some(podtl.getString("packing"))
else None
},
payTerm = {
if(ks.contains("payterm"))
Some(podtl.getString("payterm"))
else None
}
)
} def showPO(po: PO) = {
println(s"po number: ${po.ponum}")
println(s"po date: ${po.podate.toString}")
println(s"vendor: ${po.vendor}")
if (po.remarks != None)
println(s"remarks: ${po.remarks.get}")
po.podtl match {
case Some(barr) =>
val docs = barr.getValues.asScala.toList
docs.map { dc =>
toPODTL(dc.asInstanceOf[org.bson.BsonDocument])
}.foreach { doc: PODTL =>
print(s"==>Item: ${doc.item} ")
print(s"price: ${doc.price} ")
print(s"qty: ${doc.qty} ")
doc.packing.foreach(pk => print(s"packing: ${pk} "))
doc.payTerm.foreach(pt => print(s"payTerm: ${pt} "))
println("")
}
case _ =>
} po.handler match {
case Some(bs) =>
val fileName = s"/users/tiger-macpro/${po.ponum}.png"
ByteArrayToFile(bs.getData,fileName)
println(s"picture saved to ${fileName}")
case None => println("no picture provided")
} } poCollection.find().toFuture().onComplete {
case Success(docs) => docs.map(toPO).foreach (showPO)
println("------------------------------")
case Failure(e) => println(e.getMessage)
} poCollection.find(equal("podtl.qty",)).toFuture().onComplete {
case Success(docs) => docs.map(toPO).foreach (showPO)
println("-------------------------------")
case Failure(e) => println(e.getMessage)
} scala.io.StdIn.readLine()
system.terminate() }
SDP(9):MongoDB-Scala - data access and modeling的更多相关文章
- MongoDB学习笔记(1):MongoDB的安装和说明
MongoDB学习笔记(1):MongoDB的安装和说明 快速开始 下载地址 官网下载: https://www.mongodb.com/download-center?jmp=nav#communi ...
- MongoDB和Java(7):MongoDB用户管理
最近花了一些时间学习了下MongoDB数据库,感觉还是比较全面系统的,涉及了软件安装.客户端操作.安全认证.副本集和分布式集群搭建,以及使用Spring Data连接MongoDB进行数据操作,收获很 ...
- SDP(8):文本式数据库-MongoDB-Scala基本操作
MongoDB是一种文本式数据库.与传统的关系式数据库最大不同是MongoDB没有标准的格式要求,即没有schema,合适高效处理当今由互联网+商业产生的多元多态数据.MongoDB也是一种分布式数据 ...
- mongoDB系列之(二):mongoDB 副本集
1. 什么是副本集 副本集就是mongoDB副本所组成的一个集群. 同期原理是,写操作发生在主库,从库同步主库的OpLog日志. 集群中没有特定的主库,主库是选举产生,如果主库down了,会再选举出一 ...
- [转载]MongoDB学习(三):MongoDB Shell的使用
MongoDB shell MongoDB自带简洁但功能强大的JavaScript shell.JavaScript shell键入一个变量会将变量的值转换为字符串打印到控制台上. 下面介绍基本的操作 ...
- SDP(0):Streaming-Data-Processor - Data Processing with Akka-Stream
再有两天就进入2018了,想想还是要准备一下明年的工作方向.回想当初开始学习函数式编程时的主要目的是想设计一套标准API給那些习惯了OOP方式开发商业应用软件的程序员们,使他们能用一种接近传统数据库软 ...
- SDP(13): Scala.Future - far from completion,绝不能用来做甩手掌柜
在前面几篇关于数据库引擎的讨论里很多的运算函数都返回了scala.Future类型的结果,因为我以为这样就可以很方便的实现了non-blocking效果.无论任何复杂的数据处理操作,只要把它们包在一个 ...
- restapi(3)- MongoDBEngine : MongoDB Scala编程工具库
最近刚好有同事在学习MongoDB,我们讨论过MongoDB应该置于服务器端然后通过web-service为客户端提供数据的上传下载服务.我们可以用上节讨论的respapi框架来实现针对MongoDB ...
- SDP(1):ScalikeJDBC-基本操作介绍
简单来说:JDBC是一种开放标准的跨编程语言.跨数据库类型编程API.各类型数据库产品厂商都会按它的标准要求来提供针对自身产品的JDBC驱动程序.最主要的这是一套成熟的工具,在编程人员中使用很普及.既 ...
随机推荐
- webpack 介绍 & 安装 & 常用命令
webpack 介绍 & 安装 & 常用命令 webpack系列目录 webpack 系列 一:模块系统的演进 webpack 系列 二:webpack 介绍&安装 webpa ...
- echo 0000
一个奇怪的问题,正常状态下如果sql插入失败,则输出0000,代码如下: $stmt=$db->prepare("insert into message(user,title,cont ...
- nodejs爬虫笔记(一)---request与cheerio等模块的应用
目标:爬取慕课网里面一个教程的视频信息,并将其存入mysql数据库.以http://www.imooc.com/learn/857为例. 一.工具 1.安装nodejs:(操作系统环境:WiN 7 6 ...
- sqlserver数据库使用技巧(一)--限制数据库的大小
如何限制数据库的大小? 随着数据库的使用,他占用的空间会越来越大,为了便于资源的合理分配和管理,我们可以限制其最大的大小,这个建议只在测试环境使用 具体操作如下: 打开sqlserver数据库管理工具 ...
- Tomcat8远程访问manager,host-manager被拒绝403
Tomcat部署在服务器之后在服务器本地访问manager和host-manager成功(即127.0.0.1:8080或者localhost:8080),但使用测试主机访问tomcat的manage ...
- AppScan 扫描测试策略
使用 AppScan 进行扫描 针对大型网站的扫描,我们按照戴明环 PDCA 的方法论来进行规划和讨论,建议 AppScan 使用步骤:计划(Plan).执行(Do).检查(check).分析(Ana ...
- Android开发——Viewpager的介绍使用
目录: 一.Viewpager的简单介绍 二.简单的Viewpager使用 三.简单显示图片的Viewpager实现 四.广告图的实现及Viewpager指示器(小圆点)的实现 五.APP引导页的实现 ...
- ABP官方文档翻译 3.2 值对象
值对象 介绍 值对象基类 最佳实践 介绍 "展现领域描述性层面且没有概念性身份的对象称之为值对象."(Eric Evans). 和实体相反,实体有身份标示(Id),值对象没有身份标 ...
- java对象表示方式--XStream
对象表示有各种各样的方式,序列化只是其中的一种而已.表示一个对象的目的无非就是为了对象<---->IO之间相互认识,至于怎么认识,那就有很多选择了.除了之前讲过的序列化,还可以选择将数据J ...
- hdu 4609 3-idiots [fft 生成函数 计数]
hdu 4609 3-idiots 题意: 给出\(A_i\),问随机选择一个三元子集,选择的数字构成三角形的三边长的概率. 一开始一直想直接做.... 先生成函数求选两个的方案(注意要减去两次选择同 ...