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. intent详解(一)

    摘录自:http://blog.csdn.net/harvic880925/article/details/38399723 前言:通过重新翻看Android入门书籍,才发现原来自己露掉了那么多基础知 ...

  2. 解决myeclipse10.1导出War包出错:Security Alert:Integrity check error

    解决myeclipse10.1导出War包出错:Security Alert:Integrity check error 解决myeclipse10.1不能导出war包报 ============== ...

  3. python源码书籍

    <Python源码剖析>一书现在很难买到,目前大部分都是电子书. 为了更好地利用Python语言,无论是使用Python语言本身,还是将Python与C/C++交互使用,深刻理解Pytho ...

  4. LIUNX-Centos 7 编译GDAL

    一.准备工作 安装编译环境 sudo yum install gcc gcc-c++ gcc-g77 flex bison autoconf automake bzip2-devel zlib-dev ...

  5. JDK8 HashMap 源码解析

    HashMap中数据结构 在jdk1.7中,HashMap采用数组+链表(拉链法).因为数组是一组连续的内存空间,易查询,不易增删,而链表是不连续的内存空间,通过节点相互连接,易删除,不易查询.Has ...

  6. 高仿bootstrap样式的分页插件

    链接:https://pan.baidu.com/s/1jKgn2hK 密码:whwl 不知道是自己的第几个分页插件了,以前写一个丢一个,桌面,U盘,移动硬盘.想用的时候找不到,这次传网上来.大家帮忙 ...

  7. JAVA设计模式---模板方法

    1.定义: 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤. (为了防止子类改变模板方法中的算法,可以将模板方法声明为 ...

  8. Go语言入门——dep入门

    本文出现了大量maven的内容,更适合java程序员阅读,如果你的语言做依赖管理的方案与maven差异很大,可能在有些地方会不理解 从很久之前go语言在依赖解决和管理方面方案的匮乏就被不少人诟病.光指 ...

  9. CentOS下内存使用率查看

    freetotal        used        free      shared     buffers      cachedMem:        1815340     1628680 ...

  10. vue2.0开发时导入组件时出错

    导入自定义组件时出现了如下错误 ERROR Failed to compile with 1 errors 12:35:41 This dependency was not found: * comp ...