MongoDB是一种文本式数据库。与传统的关系式数据库最大不同是MongoDB没有标准的格式要求,即没有schema,合适高效处理当今由互联网+商业产生的多元多态数据。MongoDB也是一种分布式数据库,充分具备大数据处理能力和高可用性。MongoDB提供了scala终端驱动mongo-scala-driver,我们就介绍一下MongoDB数据库和通过scala来进行数据操作编程。

与关系数据库相似,MongoDB结构为Database->Collection->Document。Collection对应Table,Document对应Row。因为MongoDB没有schema,所以Collection中的Document可以是不同形状格式的。在用scala使用MongoDB之前必须先建立连接,scala-driver提供了多种连接方式:

  val client1 = MongoClient()
val client2 = MongoClient("mongodb://localhost:27017") val clusterSettings = ClusterSettings.builder()
.hosts(List(new ServerAddress("localhost:27017")).asJava).build()
val clientSettings = MongoClientSettings.builder().clusterSettings(clusterSettings).build()
val client = MongoClient(clientSettings)

下面是一些对应的MongoClient构建函数:

  /**
* Create a default MongoClient at localhost:27017
*
* @return MongoClient
*/
def apply(): MongoClient = apply("mongodb://localhost:27017") /**
* Create a MongoClient instance from a connection string uri
*
* @param uri the connection string
* @return MongoClient
*/
def apply(uri: String): MongoClient = MongoClient(uri, None) /**
* Create a MongoClient instance from a connection string uri
*
* @param uri the connection string
* @param mongoDriverInformation any driver information to associate with the MongoClient
* @return MongoClient
* @note the `mongoDriverInformation` is intended for driver and library authors to associate extra driver metadata with the connections.
*/
def apply(uri: String, mongoDriverInformation: Option[MongoDriverInformation]): MongoClient = {...}
/**
* Create a MongoClient instance from the MongoClientSettings
*
* @param clientSettings MongoClientSettings to use for the MongoClient
* @return MongoClient
*/
def apply(clientSettings: MongoClientSettings): MongoClient = MongoClient(clientSettings, None) /**
* Create a MongoClient instance from the MongoClientSettings
*
* @param clientSettings MongoClientSettings to use for the MongoClient
* @param mongoDriverInformation any driver information to associate with the MongoClient
* @return MongoClient
* @note the `mongoDriverInformation` is intended for driver and library authors to associate extra driver metadata with the connections.
*/
def apply(clientSettings: MongoClientSettings, mongoDriverInformation: Option[MongoDriverInformation]): MongoClient = {

与MongoDB建立连接后可用选定Database:

 val db = client.getDatabase("testdb")

由于没有格式限制,所以testdb不需要预先构建,像文件系统的directory一样,不存在时可以自动创建。同样,db内的collection也是可以自动创建的,因为不需要预先设定字段格式(no-schema):

val db: MongoDatabase = client.getDatabase("testdb")
val userCollection: MongoCollection[Document] = db.getCollection("users")

collection中Document类的构建函数如下:

 /**
* Create a new document from the elems
* @param elems the key/value pairs that make up the Document. This can be any valid `(String, BsonValue)` pair that can be
* transformed into a [[BsonElement]] via [[BsonMagnets.CanBeBsonElement]] implicits and any [[BsonTransformer]]s that
* are in scope.
* @return a new Document consisting key/value pairs given by `elems`.
*/
def apply(elems: CanBeBsonElement*): Document = {
val underlying = new BsonDocument()
elems.foreach(elem => underlying.put(elem.key, elem.value))
new Document(underlying)
}

Document可以通过CanbeBsonElement构建。CanbeBsonElement是一种key/value结构:

 /**
* Represents a single [[BsonElement]]
*
* This is essentially a `(String, BsonValue)` key value pair. Any pair of `(String, T)` where type `T` has a [[BsonTransformer]] in
* scope into a [[BsonValue]] is also a valid pair.
*/
sealed trait CanBeBsonElement {
val bsonElement: BsonElement /**
* The key of the [[BsonElement]]
* @return the key
*/
def key: String = bsonElement.getName /**
* The value of the [[BsonElement]]
* @return the BsonValue
*/
def value: BsonValue = bsonElement.getValue
} /**
* Implicitly converts key/value tuple of type (String, T) into a `CanBeBsonElement`
*
* @param kv the key value pair
* @param transformer the implicit [[BsonTransformer]] for the value
* @tparam T the type of the value
* @return a CanBeBsonElement representing the key/value pair
*/
implicit def tupleToCanBeBsonElement[T](kv: (String, T))(implicit transformer: BsonTransformer[T]): CanBeBsonElement = {
new CanBeBsonElement {
override val bsonElement: BsonElement = BsonElement(kv._1, transformer(kv._2))
}
}

有了上面这个tupleToCanBeBsonElement隐式转换函数就可以用下面的方式构建Document了:

  val doc: Document = Document("_id" -> , "name" -> "MongoDB", "type" -> "database",
"count" -> , "info" -> Document("x" -> , "y" -> ))

这种key/value关系对应了一般数据库表中的字段名称/字段值。下面我们尝试建两个不同格式的Document并把它们加入到同一个collection里:

  val alice = Document("_id" -> , "name" -> "alice wong", "age" -> )
val tiger = Document("first" -> "tiger", "last" -> "chan", "name" -> "tiger chan", "age" -> "unavailable") val addAlice: Observable[Completed] = userCollection.insertOne(alice)
val addTiger: Observable[Completed] = userCollection.insertOne(tiger)

上面这个例子证明了MongoDB的no-schema特性。用insert方法加入数据返回结果是个Obervable类型。这个类型与Future很像:只是一种运算的描述,必须通过subscribe方法来实际运算获取结果:

   addAlice.subscribe(new Observer[Completed] {
override def onComplete(): Unit = println("insert alice completed.")
override def onNext(result: Completed): Unit = println("insert alice sucessful.")
override def onError(e: Throwable): Unit = println(s"insert error: ${e.getMessage}")
})

又或者转成Future后用Future方法如Await来运算:

  def headResult(observable: Observable[Completed]) = Await.result(observable.head(),  seconds)
val r1 = headResult(addTiger)

Mongo-Scala提供了Observable到Future的转换函数:

   /**
* Collects the [[Observable]] results and converts to a [[scala.concurrent.Future]].
*
* Automatically subscribes to the `Observable` and uses the [[collect]] method to aggregate the results.
*
* @note If the Observable is large then this will consume lots of memory!
* If the underlying Observable is infinite this Observable will never complete.
* @return a future representation of the whole Observable
*/
def toFuture(): Future[Seq[T]] = {
val promise = Promise[Seq[T]]()
collect().subscribe((l: Seq[T]) => promise.success(l), (t: Throwable) => promise.failure(t))
promise.future
} /**
* Returns the head of the [[Observable]] in a [[scala.concurrent.Future]].
*
* @return the head result of the [[Observable]].
*/
def head(): Future[T] = {
import scala.concurrent.ExecutionContext.Implicits.global
headOption().map {
case Some(result) => result
case None => null.asInstanceOf[T] // scalastyle:ignore null
}
}

也可以用insertMany来成批加入:

  val peter = Document("_id" -> , "first" -> "peter", "age" -> "old")
val chan = Document("last" -> "chan", "family" -> "chan's")
val addMany = userCollection.insertMany(List(peter,chan))
val r2 = headResult(addMany)

现在我们可以用count得出usersCollection中Document数量和用find把所有Document都印出来:

  userCollection.count.head.onComplete {
case Success(c) => println(s"$c documents in users collection")
case Failure(e) => println(s"count() error: ${e.getMessage}")
}
userCollection.find().toFuture().onComplete {
case Success(users) => users.foreach(println)
case Failure(e) => println(s"find error: ${e.getMessage}")
}
scala.io.StdIn.readLine()

显示结果:

insert alice sucessful.
insert alice completed.
documents in users collection
Document((_id,BsonInt32{value=}), (name,BsonString{value='alice wong'}), (age,BsonInt32{value=}))
Document((_id,BsonObjectId{value=5a96641aa83f2923ab437602}), (first,BsonString{value='tiger'}), (last,BsonString{value='chan'}), (name,BsonString{value='tiger chan'}), (age,BsonString{value='unavailable'}))
Document((_id,BsonInt32{value=}), (first,BsonString{value='peter'}), (age,BsonString{value='old'}))
Document((_id,BsonObjectId{value=5a96641aa83f2923ab437603}), (last,BsonString{value='chan'}), (family,BsonString{value='chan's'}))

这个BsonString很碍眼,用隐式转换来把它转成String:

object Helpers {

  implicit class DocumentObservable[C](val observable: Observable[Document]) extends ImplicitObservable[Document] {
override val converter: (Document) => String = (doc) => doc.toJson
} implicit class GenericObservable[C](val observable: Observable[C]) extends ImplicitObservable[C] {
override val converter: (C) => String = (doc) => doc.toString
} trait ImplicitObservable[C] {
val observable: Observable[C]
val converter: (C) => String def results(): Seq[C] = Await.result(observable.toFuture(), seconds)
def headResult() = Await.result(observable.head(), seconds)
def printResults(initial: String = ""): Unit = {
if (initial.length > ) print(initial)
results().foreach(res => println(converter(res)))
}
def printHeadResult(initial: String = ""): Unit = println(s"${initial}${converter(headResult())}")
} }

现在再列印:

  userCollection.find().printResults("all documents:")

all documents:{ "_id" : , "name" : "alice wong", "age" :  }
{ "_id" : { "$oid" : "5a9665cea83f29243ccacbd2" }, "first" : "tiger", "last" : "chan", "name" : "tiger chan", "age" : "unavailable" }
{ "_id" : , "first" : "peter", "age" : "old" }
{ "_id" : { "$oid" : "5a9665cea83f29243ccacbd3" }, "last" : "chan", "family" : "chan's" }

现在可读性强多了。find()无条件选出所有Document。MongoDB-Scala通过Filters对象提供了完整的查询条件构建函数如equal:

 /**
* Creates a filter that matches all documents where the value of the field name equals the specified value. Note that this does
* actually generate a `\$eq` operator, as the query language doesn't require it.
*
* A friendly alias for the `eq` method.
*
* @param fieldName the field name
* @param value the value
* @tparam TItem the value type
* @return the filter
* @see [[http://docs.mongodb.org/manual/reference/operator/query/eq \$eq]]
*/
def equal[TItem](fieldName: String, value: TItem): Bson = eq(fieldName, value)

equal返回Bson,我们也可以把多个Bson组合起来形成一个更复杂的查询条件:

userCollection.find(and(gte("age",),exists("name",true)))

好了,现在我们可以测试各种查询条件了:

  userCollection.find(notEqual("_id",)).printResults("id != 3:")
userCollection.find(equal("last", "chan")).printResults("last = chan:")
userCollection.find(and(gte("age",),exists("name",true))).printResults("age >= 24")
userCollection.find(or(gte("age",),equal("first","tiger"))).printResults("first = tiger")

显示结果:

id != :{ "_id" : , "name" : "alice wong", "age" :  }
{ "_id" : { "$oid" : "5a9665cea83f29243ccacbd2" }, "first" : "tiger", "last" : "chan", "name" : "tiger chan", "age" : "unavailable" }
{ "_id" : { "$oid" : "5a9665cea83f29243ccacbd3" }, "last" : "chan", "family" : "chan's" }
last = chan:{ "_id" : { "$oid" : "5a9665cea83f29243ccacbd2" }, "first" : "tiger", "last" : "chan", "name" : "tiger chan", "age" : "unavailable" }
{ "_id" : { "$oid" : "5a9665cea83f29243ccacbd3" }, "last" : "chan", "family" : "chan's" }
age >= { "_id" : , "name" : "alice wong", "age" : }
first = tiger{ "_id" : , "name" : "alice wong", "age" : }
{ "_id" : { "$oid" : "5a9665cea83f29243ccacbd2" }, "first" : "tiger", "last" : "chan", "name" : "tiger chan", "age" : "unavailable" }

下面是本次示范的源代码:

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"
)

MongoScala101.scala

import org.mongodb.scala._
import scala.collection.JavaConverters._
import org.mongodb.scala.connection.ClusterSettings
import scala.concurrent._
import scala.concurrent.duration._
import scala.util._
import org.mongodb.scala.model.Filters._
object MongoScala101 extends App {
import scala.concurrent.ExecutionContext.Implicits.global
// val client1 = MongoClient()
// val client2 = MongoClient("mongodb://localhost:27017") val clusterSettings = ClusterSettings.builder()
.hosts(List(new ServerAddress("localhost:27017")).asJava).build()
val clientSettings = MongoClientSettings.builder().clusterSettings(clusterSettings).build()
val client = MongoClient(clientSettings) val db: MongoDatabase = client.getDatabase("testdb")
val userCollection: MongoCollection[Document] = db.getCollection("users")
val deleteAll = userCollection.deleteMany(notEqual("_id", ))
deleteAll.head.onComplete {
case Success(c) => println(s"delete sucessful $c")
case Failure(e) => println(s"delete error: ${e.getMessage}")
} scala.io.StdIn.readLine()
val delete3 = userCollection.deleteMany(equal("_id", ))
delete3.head.onComplete {
case Success(c) => println(s"delete sucessful $c")
case Failure(e) => println(s"delete error: ${e.getMessage}")
}
scala.io.StdIn.readLine() val doc: Document = Document("_id" -> , "name" -> "MongoDB", "type" -> "database",
"count" -> , "info" -> Document("x" -> , "y" -> )) val alice = Document("_id" -> , "name" -> "alice wong", "age" -> )
val tiger = Document("first" -> "tiger", "last" -> "chan", "name" -> "tiger chan", "age" -> "unavailable") val addAlice: Observable[Completed] = userCollection.insertOne(alice)
val addTiger: Observable[Completed] = userCollection.insertOne(tiger) addAlice.subscribe(new Observer[Completed] {
override def onComplete(): Unit = println("insert alice completed.")
override def onNext(result: Completed): Unit = println("insert alice sucessful.")
override def onError(e: Throwable): Unit = println(s"insert error: ${e.getMessage}")
}) def headResult(observable: Observable[Completed]) = Await.result(observable.head(), seconds)
val r1 = headResult(addTiger) val peter = Document("_id" -> , "first" -> "peter", "age" -> "old")
val chan = Document("last" -> "chan", "family" -> "chan's")
val addMany = userCollection.insertMany(List(peter,chan))
val r2 = headResult(addMany) import Helpers._
userCollection.count.head.onComplete {
case Success(c) => println(s"$c documents in users collection")
case Failure(e) => println(s"count() error: ${e.getMessage}")
}
userCollection.find().toFuture().onComplete {
case Success(users) => users.foreach(println)
case Failure(e) => println(s"find error: ${e.getMessage}")
}
scala.io.StdIn.readLine() userCollection.find().printResults("all documents:")
userCollection.find(notEqual("_id",)).printResults("id != 3:")
userCollection.find(equal("last", "chan")).printResults("last = chan:")
userCollection.find(and(gte("age",),exists("name",true))).printResults("age >= 24")
userCollection.find(or(gte("age",),equal("first","tiger"))).printResults("first = tiger") client.close() println("end!!!") } object Helpers { implicit class DocumentObservable[C](val observable: Observable[Document]) extends ImplicitObservable[Document] {
override val converter: (Document) => String = (doc) => doc.toJson
} implicit class GenericObservable[C](val observable: Observable[C]) extends ImplicitObservable[C] {
override val converter: (C) => String = (doc) => doc.toString
} trait ImplicitObservable[C] {
val observable: Observable[C]
val converter: (C) => String def results(): Seq[C] = Await.result(observable.toFuture(), seconds)
def headResult() = Await.result(observable.head(), seconds)
def printResults(initial: String = ""): Unit = {
if (initial.length > ) print(initial)
results().foreach(res => println(converter(res)))
}
def printHeadResult(initial: String = ""): Unit = println(s"${initial}${converter(headResult())}")
} }

SDP(8):文本式数据库-MongoDB-Scala基本操作的更多相关文章

  1. 【网络爬虫入门05】分布式文件存储数据库MongoDB的基本操作与爬虫应用

    [网络爬虫入门05]分布式文件存储数据库MongoDB的基本操作与爬虫应用 广东职业技术学院  欧浩源 1.引言 网络爬虫往往需要将大量的数据存储到数据库中,常用的有MySQL.MongoDB和Red ...

  2. SDP(10):文本式大数据运算环境-MongoDB-Engine功能设计

    为了让前面规划的互联网+数据平台能有效对电子商务数据进行管理及实现大数据统计功能,必须在平台上再增加一个MongDB-Engine:数据平台用户通过传入一种Context来指示MongoDB-Engi ...

  3. mariadb_1 数据库介绍及基本操作

    数据库介绍 1.什么是数据库? 简单的说,数据库就是一个存放数据的仓库,这个仓库是按照一定的数据结构(数据结构是指数据的组织形式或数据之间的联系)来组织,存储的,我们可以通过数据库提供的多种方法来管理 ...

  4. 数据库MongoDB

    一.MongoDB简介 MongoDB是由c++语言编写的,是一个基于分布式文件存储的开源数据库系统,在高负载的情况下,添加更多的节点,可以保证服务器性能.MongoDB旨在为web应用提供扩展的高性 ...

  5. 孤荷凌寒自学python第六十六天学习mongoDB的基本操作并进行简单封装5

    孤荷凌寒自学python第六十六天学习mongoDB的基本操作并进行简单封装5并学习权限设置 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第十二天. 今天继续学习mongo ...

  6. 孤荷凌寒自学python第六十五天学习mongoDB的基本操作并进行简单封装4

    孤荷凌寒自学python第六十五天学习mongoDB的基本操作并进行简单封装4 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第十一天. 今天继续学习mongoDB的简单操作 ...

  7. 孤荷凌寒自学python第六十四天学习mongoDB的基本操作并进行简单封装3

    孤荷凌寒自学python第六十四天学习mongoDB的基本操作并进行简单封装3 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第十天. 今天继续学习mongoDB的简单操作, ...

  8. 孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2

    孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第九天. 今天继续学习mongoDB的简单操作, ...

  9. 孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1

    孤荷凌寒自学python第六十二天学习mongoDB的基本操作并进行简单封装1 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第八天. 今天开始学习mongoDB的简单操作, ...

随机推荐

  1. Ubuntu问题:E45: 'readonly' option is set (add ! to override)错误解决

    问题描述:E45: 'readonly' option is set (add ! to override) 问题分析:该错误为当前用户没有权限对文件作修改 问题解决: 输入 :w !sudo tee ...

  2. [知了堂学习笔记]_牵线Eclipse和Tomcat第一篇 —— 配置Java环境变量&&安装eclipse

    一.先给他们提供一个"浪漫的"环境,比如传说中的"鹊桥"--java环境变量.哈哈! 配置java环境变量. 下载jdk,根据自己电脑的版本和操作位数选择不同的 ...

  3. python_如何定义带参数的装饰器?

    案例: 实现一个装饰器,用它来检查被装饰函数的参数类型. 需求: 装饰器可以通过函数,指明函数参数类型,进行函数调用的时候,传入参数,检测到不匹配时,抛出异常 如何解决这个问题? 先要获取函数的签名, ...

  4. jquery取前、后、父、子元素

    前.prev(); 后.next(); 父.parent(); 子.children(); 注意:前的前是.prev().prev(),例如前元素无i,但前的前的i元素有i,不能写成.prev('i' ...

  5. CopyOnWriteArrayList集合排序异常问题

    1.集合自定义排序实现 对List集合的自定义排序想必大家都知道要使用如下的方式,通过实现Comparator接口并实现compare方法来实现. /** * * @方法名 changeChain * ...

  6. 基于 HTML5 WebGL 的 3D SCADA 主站系统

    这个例子的初衷是模拟服务器与客户端的通信,我把整个需求简化变成了今天的这个例子.3D 的模拟一般需要鹰眼来辅助的,这样找产品以及整个空间的概括会比较明确,在这个例子中我也加了,这篇文章就算是我对这次项 ...

  7. python学习day3------列表、元组、字符串操作

    一.列表 变量名后加中括号[],接下来介绍对列表进行查操作 #!/usr/bin/env python #-*- Coding:utf-8 -*- # Author:Eric.Shen test = ...

  8. Ubuntu14.04server + LNMP + Zabbix3.4安装教程

    此教程使用的编辑器是vim LNMP安装很简单,跟着步骤走没有问题,就不粘图片了. 安装MYSQL (1)开始安装:apt-get install mysql-server libmysqld-dev ...

  9. null undefiend NaN

    console.log(typeof NaN) console.log(typeof undefined) console.log(typeof null)

  10. AndroidStudio3更改包名失败

    使用Android Studio 3.0 Beta6更改包名refactor---rename一直提示:Refactoring cannot be performedFile xxx\build\xx ...