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的更多相关文章

  1. MongoDB学习笔记(1):MongoDB的安装和说明

    MongoDB学习笔记(1):MongoDB的安装和说明 快速开始 下载地址 官网下载: https://www.mongodb.com/download-center?jmp=nav#communi ...

  2. MongoDB和Java(7):MongoDB用户管理

    最近花了一些时间学习了下MongoDB数据库,感觉还是比较全面系统的,涉及了软件安装.客户端操作.安全认证.副本集和分布式集群搭建,以及使用Spring Data连接MongoDB进行数据操作,收获很 ...

  3. SDP(8):文本式数据库-MongoDB-Scala基本操作

    MongoDB是一种文本式数据库.与传统的关系式数据库最大不同是MongoDB没有标准的格式要求,即没有schema,合适高效处理当今由互联网+商业产生的多元多态数据.MongoDB也是一种分布式数据 ...

  4. mongoDB系列之(二):mongoDB 副本集

    1. 什么是副本集 副本集就是mongoDB副本所组成的一个集群. 同期原理是,写操作发生在主库,从库同步主库的OpLog日志. 集群中没有特定的主库,主库是选举产生,如果主库down了,会再选举出一 ...

  5. [转载]MongoDB学习(三):MongoDB Shell的使用

    MongoDB shell MongoDB自带简洁但功能强大的JavaScript shell.JavaScript shell键入一个变量会将变量的值转换为字符串打印到控制台上. 下面介绍基本的操作 ...

  6. SDP(0):Streaming-Data-Processor - Data Processing with Akka-Stream

    再有两天就进入2018了,想想还是要准备一下明年的工作方向.回想当初开始学习函数式编程时的主要目的是想设计一套标准API給那些习惯了OOP方式开发商业应用软件的程序员们,使他们能用一种接近传统数据库软 ...

  7. SDP(13): Scala.Future - far from completion,绝不能用来做甩手掌柜

    在前面几篇关于数据库引擎的讨论里很多的运算函数都返回了scala.Future类型的结果,因为我以为这样就可以很方便的实现了non-blocking效果.无论任何复杂的数据处理操作,只要把它们包在一个 ...

  8. restapi(3)- MongoDBEngine : MongoDB Scala编程工具库

    最近刚好有同事在学习MongoDB,我们讨论过MongoDB应该置于服务器端然后通过web-service为客户端提供数据的上传下载服务.我们可以用上节讨论的respapi框架来实现针对MongoDB ...

  9. SDP(1):ScalikeJDBC-基本操作介绍

    简单来说:JDBC是一种开放标准的跨编程语言.跨数据库类型编程API.各类型数据库产品厂商都会按它的标准要求来提供针对自身产品的JDBC驱动程序.最主要的这是一套成熟的工具,在编程人员中使用很普及.既 ...

随机推荐

  1. Css的优先级机制

    样式的优先级 多重样式(Multiple Style):如果外部样式.内部样式.内联样式同时应用于同一个元素,就是使用多重样式的情况. 一般情况优先级如下: (外部样式)External style ...

  2. Spring MVC (JDK8+Tomcat8)

    1 Spring MVC概述 Spring MVC是Spring为表现层提供的基于MVC设计理念的优秀的web框架,是目前最主流的MVC框架之一. Spring3.0后全面超越Struts2,成为最优 ...

  3. 在nagios中使用nrpe自定义脚本

    nrpe的安装    tar xvfz nrpe-2.13.tar.gz cd nrpe-2.13 ./configure make all make install-plugin make inst ...

  4. NIO笔记---上

    小弟前端时间由于开发个管理系统导致断更了近20天!!马上就要春招了,学习了一下NIO,将笔记记录下,希望和我一样的18届毕业生都能找到满意的公司!! 本文记录了NIO与IO的区别,缓冲区的数据存取,直 ...

  5. iOS-FMDB事务【批量更新数据】

    打开数据库(sqlite) ///打开数据库 + (BOOL)openDataBase{ _TYDatabase = [[FMDatabase alloc]initWithPath:[self dat ...

  6. ABP官方文档翻译 6.1.2 MVC视图

    ASP.NET MVC 视图 介绍 AbpWebViewPage基类 介绍 ABP通过Abp.Web.Mvc nuget包集成到MVC视图.你可以如往常一样创建正常的MVC视图. AbpWebView ...

  7. xBIM 基本的模型操作

    目录 xBIM 应用与学习 (一) xBIM 应用与学习 (二) xBIM 基本的模型操作 xBIM 日志操作 XBIM 3D 墙壁案例 xBIM 格式之间转换 xBIM 使用Linq 来优化查询 x ...

  8. FindBugs简单应用

    FindBugs是一种java代码的静态分析工具,无需开发人员费劲就能找出代码中可能存在的缺陷.FindBugs 不注重样式或者格式,它试图只寻找缺陷或者潜在的性能问题. 第一步,http://sou ...

  9. 洛谷 [P2483] [模板] k短路

    人生中的第一道黑题... 其实就是k短路模板 #include <iostream> #include <cstdio> #include <cstring> #i ...

  10. BZOJ 1502: [NOI2005]月下柠檬树 [辛普森积分 解析几何 圆]

    1502: [NOI2005]月下柠檬树 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1070  Solved: 596[Submit][Status] ...