MongoDB数据库学习与实践

46 阅读16分钟

MongoDB 数据库 title: mongoDB

MongoDB 数据库学习与实践

[TOC]

一、MongoDB 介绍 :airplane:

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

1.1 主要特点

  • MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
  • 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName="Sameer",Address="8 Gandhi Road")来实现更快的排序。
  • 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
  • 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
  • Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
  • MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
  • Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
  • Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
  • Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
  • GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
  • MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
  • MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
  • MongoDB安装简单。

二、 MongoDB 相关概念:flipper:

2.1 相关概念

SQL术语/概念MongoDB术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
columnfield数据字段/域
indexindex索引
table joins表连接,MongoDB不支持
primary keyprimary key主键,MongoDB自动将_id字段设置为主键

Mon共DB的数据类型:

数据类型描述
String字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean布尔值。用于存储布尔值(真/假)。
Double双精度浮点值。用于存储浮点值。
Min/Max keys将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array用于将数组或列表或多个值存储为一个键。
Timestamp时间戳。记录文档修改或添加的具体时间。
Object用于内嵌文档。
Null用于创建空值。
Symbol符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID对象 ID。用于创建文档的 ID。
Binary Data二进制数据。用于存储二进制数据。
Code代码类型。用于在文档中存储 JavaScript 代码。
Regular expression正则表达式类型。用于存储正则表达式。

2.2 ObjectId

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

  • 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
  • 接下来的 3 个字节是机器标识码
  • 紧接的两个字节由进程 id 组成 PID
  • 最后三个字节是随机数

MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象

由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:

> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate("2017-11-25T07:21:10Z")

bjectId 转为字符串

> newObject.str
5a1919e63df83ce79df8b38f

2.3 mongodb 的连接

标准 URI 连接语法:

mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
  • mongodb:// 这是固定的格式,必须要指定。
  • username:password@ 可选项,如果设置,在连接数据库服务器之后,驱动都会尝试登录这个数据库
  • host1 必须的指定至少一个host, host1 是这个URI唯一要填写的。它指定了要连接服务器的地址。如果要连接复制集,请指定多个主机地址。
  • portX 可选的指定端口,如果不填,默认为27017
  • /database 如果指定username:password@,连接并验证登录指定数据库。若不指定,默认打开 test 数据库。
  • ?options 是连接选项。如果不使用/database,则前面需要加上/。所有连接选项都是键值对name=value,键值对之间通过&或;(分号)隔开

标准的连接格式包含了多个选项(options),如下所示:

选项描述
replicaSet=name验证replica set的名称。 Impliesconnect=replicaSet.
slaveOk=true|falsetrue:在connect=direct模式下,驱动会连接第一台机器,即使这台服务器不是主。在connect=replicaSet模式下,驱动会发送所有的写请求到主并且把读取操作分布在其他从服务器。false: 在 connect=direct模式下,驱动会自动找寻主服务器. 在connect=replicaSet 模式下,驱动仅仅连接主服务器,并且所有的读写命令都连接到主服务器。
safe=true|falsetrue: 在执行更新操作之后,驱动都会发送getLastError命令来确保更新成功。(还要参考 wtimeoutMS).false: 在每次更新之后,驱动不会发送getLastError来确保更新成功。
w=n驱动添加 { w : n } 到getLastError命令. 应用于safe=true。
wtimeoutMS=ms驱动添加 { wtimeout : ms } 到 getlasterror 命令. 应用于 safe=true.
fsync=true|falsetrue: 驱动添加 { fsync : true } 到 getlasterror 命令.应用于 safe=true.false: 驱动不会添加到getLastError命令中。
journal=true|false如果设置为 true, 同步到 journal (在提交到数据库前写入到实体中). 应用于 safe=true
connectTimeoutMS=ms可以打开连接的时间。
socketTimeoutMS=ms发送和接受sockets的时间。

使用用户名和密码连接登录到指定数据库,格式如下:

mongodb://admin:123456@localhost/test

三、MongoDB相关集合指令 :taco:

3.1 相关数据库指令指令

// -- 创建数据库
use info_db
//-- 删除数据库
db.dropDatabase()
//-- 创建集合
db.createCollection("item_wg")
// -- 删除集合
db.item_wg.drop()
// 
// -- 插入文档
db.item_wg.insert({title: 'MongoDB 教程', 
    description: 'MongoDB 是一个 Nosql 数据库',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
})
// -- 查询文档
db.item_wg.find()
// -- 更新文档
db.item_wg.update({'name':'zhangsan'},{$set:{'title':'python'}})

// -- 修改多条相同文档
db.item_wg.update({'title':'MongoDB'},{$set:{'title':'MongoDB教程'}},{multi:true})

// -- save 方法替换文档
db.item_wg.save({
    "_id" : ObjectId("6406994f79694e763e732f92"),
    "title" : "MongoDB",
    "description" : "MongoDB 是一个 Nosql 数据库",
    "by" : "Runoob",
    "url" : "http://www.runoob.com",
    "tags" : [
            "mongodb",
            "NoSQL"
    ],
    "likes" : 110
})

// -- 删除文档 删除查找到1条
db.item_wg.insert({title: 'MongoDB 教程', 
    description: 'MongoDB 是一个 Nosql 数据库',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['mongodb', 'database', 'NoSQL'],
    likes: 100
})
// -- 删除1条

db.item_wg.remove({'title':'MongoDB 教程'},{'justOne':true})
db.item_wg.deleteOne('title':'MongoDB 教程'})
// -- 删除全部
db.item_wg.remove({'title':'MongoDB 教程'})
db.item_wg.deleteMany({'title':'MongoDB 教程'})
db.item_wg.remove({})
db.item_wg.deleteMany({})
// -- mongodb 查询文档 db.collection.find(query, projection)
db.item_wg.find().pretty()
// -- 查用对比条件  
//  -- 等于      db.item_wg.find({"by":"菜鸟教程"}).pretty()
//  -- 小于      db.item_wg.find({"likes":{$lt:50}}).pretty()
//  -- 小于等于   db.item_wg.find({"likes":{$lte:50}}).pretty()
//  -- 大于      db.item_wg.find({"likes":{$gt:50}}).pretty()
//  -- 大于等于   db.item_wg.find({"likes":{$gte:50}}).pretty()
//  -- 不等于    db.item_wg.find({"likes":{$ne:50}}).pretty()


// -- 查询and条件
db.item_wg.find({"by":"菜鸟教程", "title":"MongoDB 教程"}).pretty()
// --  查询or 条件
db.item_wg.find({$or:[{"by":"菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()

// -- 查询 and 和or 联合查询 'where likes>50 AND (by = '菜鸟教程' OR title = 'MongoDB 教程')'
db.item_wg.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()

// -- 条件操作符的使用
db.item_wg.find({likes: {$gt:50 ,$lte:200}})

// -- mongodb  $type 操作符
db.item_wg.insert({
    title: 'PHP 教程', 
    description: 'PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['php'],
    likes: 200
})

db.item_wg.insert({title: 'Java 教程', 
    description: 'Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['java'],
    likes: 150
})

db.item_wg.insert({title: 'MongoDB 教程', 
    description: 'MongoDB 是一个 Nosql 数据库',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['mongodb'],
    likes: 100
})
// -- 想获取 "item_wg" 集合中 title 为 String 的数据,你可以使用以下命令
db.item_wg.find({"title" : {$type : 'string'}})

//-- mongodb  limit 与skip 用法,查询定量的数据
db.item_wg.find({},{"title":1,_id:0}).limit(2)

// -- mongodb skip db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
db.item_wg.find({},{"title":1,_id:0}).limit(1).skip(1)


// -- mongodb 排序sort db.COLLECTION_NAME.find().sort({KEY:1})  1: 升序 -1:降序
db.item_wg.find({},{"title":1,_id:0,"likes":2}).sort({"likes":-1})
// -- mongodb 索引  创建索引 db.collection.createIndex(keys, options) 语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可
db.item_wg.createIndex({"title":1})
// -- mongodb  复合索引
db.item_wg.createIndex({"title":1,"description":-1})

// -- mongodb   后台创建索引
db.item_wg.createIndex({"name":1},{"background":true})

// -- 查看索引集合
db.item_wg.getIndexes()
// -- 查看集合索引大小
db.item_wg.totalIndexSize()
// -- 删除集合所有索引
db.item_wg.dropIndexes()
// -- 删除集合指定索引
db.item_wg.dropIndex("索引名称")

// -- 利用 TTL 集合对存储的数据进行失效时间设置 ,设置在创建记录后,180 秒左右删除
db.col.createIndex({"createDate": 1},{expireAfterSeconds: 180})

// -- mongodb 聚合函数使用 aggregate 类似与count(*) db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
b.item_wg.insert(
{
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by_user: 'runoob.com',
   url: 'http://www.runoob.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100
})
db.item_wg.insert(
{
   title: 'NoSQL Overview', 
   description: 'No sql database is very fast',
   by_user: 'runoob.com',
   url: 'http://www.runoob.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 10
})
db.item_wg.insert(
{
   title: 'Neo4j Overview', 
   description: 'Neo4j is no sql database',
   by_user: 'Neo4j',
   url: 'http://www.neo4j.com',
   tags: ['neo4j', 'database', 'NoSQL'],
   likes: 750
})
// -- 通过以上集合计算每个作者所写的文章数,使用aggregate() 
// --  类似 select by_user, count(*) from mycol group by by_user
db.item_wg.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])


// -- 插入用户信息
db.user_info.insert(
{

   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin",
   "address": [
      {
         "building": "22 A, Indiana Apt",
         "pincode": 123456,
         "city": "Los Angeles",
         "state": "California"
      },
      {
         "building": "170 A, Acropolis Apt",
         "pincode": 456789,
         "city": "Chicago",
         "state": "Illinois"
      }]
} 
)
// -- 查询用户
db.user_info.findOne({"name":"Tom Benzamin"},{"address":1});
// -- 引用关系查询
// >var result = db.users.findOne({"name":"Tom Benzamin"},{"address_ids":1})
// >var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})


// -- mongodb 覆盖索引查询
db.user_info.insert(
{
   "contact": "987654321",
   "dob": "01-01-1991",
   "gender": "M",
   "name": "Tom Benzamin",
   "user_name": "tombenzamin"
})
db.user_info.find();
// -- 创建索引
db.user_info.createIndex({gender:1,user_name:1})
// -- 根据索引内存快速查询
db.user_info.find({gender:"M"},{user_name:1,_id:0})

// -- mongodb 查询分析 MongoDB 查询分析常用函数有:explain() 和 hint()
db.user_info.find({gender:"M"},{user_name:1,_id:0}).explain()
// -- 使用hint 查询分析
db.user_info.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()

// -- mongodb 原子操作
db.book_info.insert({
          title: "MongoDB: The Definitive Guide",
          author: [ "Kristina Chodorow", "Mike Dirolf" ],
          published_date: ISODate("2010-09-24"),
          pages: 216,
          language: "English",
          publisher_id: "oreilly",
          available: 3,
          checkout: [ { by: "joe", date: ISODate("2012-10-15") } ]
        })
  db.book_info.find();
 // -- 使用 db.collection.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息
db.book_info.findAndModify ( {
   query: {
            _id: ObjectId("6406e27979694e763e732f9d"),
            available: { $gt: 0 }
          },
   update: {
             $inc: { available: -1 },
             $push: { checkout: { by: "abc", date: new Date() } }
           }
} )

// -- mongodb 高级索引

db.users.insert(
{
   "address": {
      "city": "Los Angeles",
      "state": "California",
      "pincode": "123"
   },
   "tags": [
      "music",
      "cricket",
      "blogs"
   ],
   "name": "Tom Benzamin"
})
db.users.find({}).pretty()
// -- 基于标签来检索用户,为此我们需要对集合中的数组 tags 建立索引
db.users.createIndex({"tags":1})
// -- 按照索引列检索
db.users.find({tags:"Tom cricket"}).explain()
// -- 索引子文档  需要通过city、state、pincode字段来检索文档,由于这些字段是子文档的字段,所以我们需要对子文档建立索引
db.users.createIndex({"address.city":1,"address.state":1,"address.pincode":1})
// -- 子文档检索
db.users.find({"address.city":"Los Angeles"}).explain();  
db.users.find({"address.state":"California","address.city":"Los Angeles"}) 


// -- Mongodb Map Reduce 
db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})

db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})

db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})

db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"active"
})

db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "mark",
   "status":"disabled"
})

db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"disabled"
})

db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"disabled"
})

db.posts.insert({
   "post_text": "菜鸟教程,最全的技术文档。",
   "user_name": "runoob",
   "status":"active"
})
db.posts.find()

db.posts.mapReduce( 
   function() { emit(this.user_name,1); }, 
   function(key, values) {return Array.sum(values)}, 
      {  
         query:{status:"active"},  
         out:"post_total" 
      }
)
// -- 查询map Reduce 结果
db.post_total.find()
 
// -- mongodb 全文检索-- 不支持中文检索
db.posts.insert(
{
   "post_text": "enjoy the mongodb articles on Runoob",
   "tags": [
      "mongodb",
      "runoob"
   ]
})
db.posts.insert(
{
   "post_text": "enjoy the mongodb articles on  pyhton linux firedway",
   "tags": [
      "python",
      "linux"
   ]
})
//-- 建立全文索引
db.posts.createIndex({post_text:"text"})
// -- 全文检索
db.posts.find({$text:{$search:"pyhton"}})

// -- mongodb 使用正则表达式
db.posts.find();

db.posts.find({post_text:{$regex:"Runoob"}})
db.posts.find({post_text:/Runoob/})
// -- 不区分大小写的正则表达式
db.posts.find({post_text:{$regex:"runoob",$options:"$i"}})

// -- 数组元素使用正则表达式
db.posts.find({tags:{$regex:"run"}})

// -- mongodb GridFS  添加文件
// -- mongofiles.exe -d gridfs put song.mp3  -d gridfs 指定存储文件的数据库名称,如果不存在该数据库,MongoDB会自动创建。如果不存在该数据库,MongoDB会自动创建。Song.mp3 是音频文件名。
// -- 查看数据库文档 db.fs.files.find()

3.2 相关聚合表达式

表达式描述实例
$sum计算总和db.mycol.aggregate([{group : {_id : "by_user", num_tutorial : {sum:"sum : "likes"}}}])
$avg计算平均值db.mycol.aggregate([{group : {_id : "by_user", num_tutorial : {avg:"avg : "likes"}}}])
$min获取集合中所有文档对应值得最小值db.mycol.aggregate([{group : {_id : "by_user", num_tutorial : {min:"min : "likes"}}}])
$max获取集合中所有文档对应值得最大值db.mycol.aggregate([{group : {_id : "by_user", num_tutorial : {max:"max : "likes"}}}])
$push将值加入一个数组中,不会判断是否有重复的值db.mycol.aggregate([{group : {_id : "by_user", url : {push:"push: "url"}}}])
$addToSet将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在了,则不加入db.mycol.aggregate([{group : {_id : "by_user", url : {addToSet:"addToSet : "url"}}}])
$first根据资源文档的排序获取第一个文档数据db.mycol.aggregate([{group : {_id : "by_user", first_url : {first:"first : "url"}}}])
$last根据资源文档的排序获取最后一个文档数据db.mycol.aggregate([{group : {_id : "by_user", last_url : {last:"last : "url"}}}])

3.3 MongoDB 管道

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • match:用于过滤数据,只输出符合条件的文档。match:用于过滤数据,只输出符合条件的文档。match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。
db.article.aggregate(
    { $project : {
        _id : 0 ,
        title : 1 ,
        author : 1
    }});

3.4 Mongodb 原子操作

原子操作常用命令

  • $set

用来指定一个键并更新键值,若键不存在并创建。

{ $set : { field : value } }
  • $unset

用来删除一个键。

{ $unset : { field : 1} }
  • $inc

$inc可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。

{ $inc : { field : value } }
  • $push

用法:

{ $push : { field : value } }

把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。

  • pushAll

同$push,只是一次可以追加多个值到一个数组字段内。

{ $pushAll : { field : value_array } }
  • $pull

从数组field内删除一个等于value值。

{ $pull : { field : _value } }
  • $addToSet

增加一个值到数组内,而且只有当这个值不在数组内才增加。

  • $pop

删除数组的第一个或最后一个元素

{ $pop : { field : 1 } }
  • $rename

修改字段名称

{ $rename : { old_field_name : new_field_name } }
  • $bit

位操作,integer类型

{$bit : { field : {and : 5}}}

四、MongoDB 复制(副本集) :rescue_worker_helmet:

mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。

mongodb各个节点常见的搭配方式为:一主一从、一主多从。

主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

ppZcbqK.png

副本集特征:

  • N 个节点的集群
  • 任何节点可作为主节点
  • 所有写入操作都在主节点上
  • 自动故障转移
  • 自动恢复

五、 MongoDB Map Reduce 操作:cake:

Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。

MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。

以下是MapReduce的基本语法:

>db.collection.mapReduce(
   function() {emit(key,value);},  //map 函数
   function(key,values) {return reduceFunction},   //reduce 函数
   {
      out: collection,
      query: document,
      sort: document,
      limit: number
   }
)

使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。

Map 函数必须调用 emit(key, value) 返回键值对。

参数说明:

  • map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
  • reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
  • out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
  • query 一个筛选条件,只有满足条件的文档才会调用map函数。(query。limit,sort可以随意组合)
  • sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
  • limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)

现在,我们将在 posts 集合中使用 mapReduce 函数来选取已发布的文章(status:"active"),并通过user_name分组,计算每个用户的文章数:

>db.posts.mapReduce( 
   function() { emit(this.user_name,1); }, 
   function(key, values) {return Array.sum(values)}, 
      {  
         query:{status:"active"},  
         out:"post_total" 
      }
)

以上 mapReduce 输出结果为:

{
        "result" : "post_total",
        "timeMillis" : 23,
        "counts" : {
                "input" : 5,
                "emit" : 5,
                "reduce" : 1,
                "output" : 2
        },
        "ok" : 1
}

结果表明,共有 5 个符合查询条件(status:"active")的文档, 在map函数中生成了 5 个键值对文档,最后使用reduce函数将相同的键值分为 2 组。

具体参数说明:

  • result:储存结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了。
  • timeMillis:执行花费的时间,毫秒为单位
  • input:满足条件被发送到map函数的文档个数
  • emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量
  • output:结果集合中的文档个数**(count对调试非常有帮助)**
  • ok:是否成功,成功为1
  • err:如果失败,这里可以有失败原因,不过从经验上来看,原因比较模糊,作用不大

使用 find 操作符来查看 mapReduce 的查询结果:

> var map=function() { emit(this.user_name,1); }
> var reduce=function(key, values) {return Array.sum(values)}
> var options={query:{status:"active"},out:"post_total"}
> db.posts.mapReduce(map,reduce,options)
{ "result" : "post_total", "ok" : 1 }
> db.post_total.find();

六、SpringBoot 集成MongoDB:school:

6.1 引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>

6.2 添加配置

spring:
  application:
    name: boot-mongodb
  data:
    mongodb:
      host: localhost
      port: 27017
      username: test
      password: test
      database: info_db

6.3 基于MongoTemplate 开发CRUD

  • 创建实体类
@Data
@Document("User")
public class User {

    @Id
    private String id;
    private String name;
    private Integer age;
    private String email;
    private String createDate;
}

  • 编写测试类
package com.yongliang.mongodb;

import cn.hutool.json.JSONUtil;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.yongliang.mongodb.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.List;
import java.util.regex.Pattern;

@SpringBootTest
class BootMongodbApplicationTests {
    @Autowired
    private MongoTemplate mongoTemplate;

    // 插入集合
    @Test
    void contextLoads() {
        User user = new User();
        user.setAge(20);
        user.setName("test");
        user.setEmail("123@qq.com");
        User user1 = mongoTemplate.insert(user);
        System.out.println(user1);

    }

    //查询所有记录
    @Test
    public void findAll() {
        List<User> all = mongoTemplate.findAll(User.class);
        System.out.println(JSONUtil.toJsonStr(all));
    }

    //根据id查询
    @Test
    public void findId() {
        User user = mongoTemplate.findById("6406f5de89d50c30e52f6c8b", User.class);
        System.out.println(user.toString());
    }

    //条件查询
    @Test
    public void findUserList() {
        Query query = new Query(Criteria.where("name").is("test").and("age").is(20));
        List<User> users = mongoTemplate.find(query, User.class);
        System.out.println(JSONUtil.toJsonStr(users));
    }

    //模糊条件查询
    @Test
    public void findLikeUserList() {
//        name like test
        String name = "est";
        String regex = String.format("%s%s%s", "^.*", name, ".*$");
        /*1、在使用Pattern.compile函数时,可以加入控制正则表达式的匹配行为的参数:
        Pattern Pattern.compile(String regex, int flag)
        2、regex设置匹配规则
        3、Pattern.CASE_INSENSITIVE,这个标志能让表达式忽略大小写进行匹配。*/
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        //创建一个query对象(用来封装所有条件对象),再创建一个criteria对象(用来构建条件)
        //构建查询条件
        Query query = new Query(Criteria.where("name").regex(pattern));
        List<User> users = mongoTemplate.find(query, User.class);
        System.out.println(users);
    }

    //分页查询(带条件)
    @Test
    public void pageLikeUserList() {
        int pageNo = 1;//设置当前页
        int pageSize = 3;//设置每页显示的记录数

        //条件构建
        String name = "est";
        String regex = String.format("%s%s%s", "^.*", name, ".*$");
     /*1、在使用Pattern.compile函数时,可以加入控制正则表达式的匹配行为的参数:
     Pattern Pattern.compile(String regex, int flag)
     2、regex设置匹配规则
     3、Pattern.CASE_INSENSITIVE,这个标志能让表达式忽略大小写进行匹配。*/
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        //创建一个query对象(用来封装所有条件对象),再创建一个criteria对象(用来构建条件)
        //构建查询条件
        Query query = new Query(Criteria.where("name").regex(pattern));
        //分页构建
        //查询数来集合(表)中的总记录数
        long count = mongoTemplate.count(query, User.class);
        List<User> users = mongoTemplate.find(query.skip((pageNo - 1) * pageSize).limit(pageSize), User.class);
        System.out.println(count);
        System.out.println(users);
    }
    //修改操作
    @Test
    public void  updateUser(){
        //根据id查询
        User user = mongoTemplate.findById("6406f5de89d50c30e52f6c8b", User.class);
        //修改值
        user.setName("test_02");
        user.setAge(2);
        user.setEmail("test_02@qq.com");
        //调用方法实现修改
        Query query = new Query(Criteria.where("_id").is(user.getId()));
        Update update = new Update();
        update.set("name",user.getName());
        update.set("age",user.getAge());
        update.set("email",user.getEmail());
        //调用mongoTemplate的修改方法实现修改
        UpdateResult upsert = mongoTemplate.upsert(query, update, User.class);
        long modifiedCount = upsert.getModifiedCount();//获取到修改受影响的行数
        System.out.println("受影响的条数:"+modifiedCount);
    }

    //删除条件
    @Test
    public void deleteUser(){
        Query query = new Query(Criteria.where("_id").is("6406f5de89d50c30e52f6c8b"));
        DeleteResult remove = mongoTemplate.remove(query, User.class);
        long deletedCount = remove.getDeletedCount();
        System.out.println("删除的条数:"+deletedCount);
    }


}

6.4 基于MongoRepository开发CRUD

  • 添加Repository接口
package com.yongliang.mongodb.repository;

import com.yongliang.mongodb.entity.User;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * @ClassName UserRepository
 * @Description: JPA 查询方式
 * @Author: zhangyongliang
 * @Date: 2023/3/7 16:47
 * @Version: V2.3
 */
public interface UserRepository extends MongoRepository<User,String> {

}

  • 编写测试类
package com.yongliang.mongodb;

import com.yongliang.mongodb.entity.User;
import com.yongliang.mongodb.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.*;

import java.util.List;

/**
 * @ClassName: JpaMongoTest
 * @Description:
 * @Author: zhangyongliang
 * @Date: 2023/3/7 16:48
 * @Version: V2.3
 */
@SpringBootTest
public class JpaMongoTest {
    @Autowired
    private UserRepository userRepository;
    //添加操作
    @Test
    public void save() {
        User user = new User();
        user.setName("ertong");
        user.setAge(20);
        user.setEmail("ertong@qq.com");
        User user1 = userRepository.save(user);
        System.out.println(user1);
    }
    //查询所有
    @Test
    public void findAll(){
        List<User> lists = userRepository.findAll();
        for(User user:lists){
            System.out.println(user);
        }
    }
    //根据id查询
    @Test
    public void findById(){
        User user = userRepository.findById("6406fa9b0fe59c4b6c09aa4a").get();
        System.out.println(user);
    }
    //条件查询
    @Test
    public void findUserList(){
        User user = new User();
        user.setName("ertong");
        user.setAge(20);
        Example<User> example = Example.of(user);
        List<User> all = userRepository.findAll(example);
        System.out.println(all);
    }
    //模糊条件查询
    @Test
    public void findLikeUserList(){
        ///创建匹配器,即如何使用查询条件
        ExampleMatcher matcher = ExampleMatcher.matching()//构建对象
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)//改变默认字符串匹配方式:模糊查询
                .withIgnoreCase(true);//改变默认大小写忽略方式:忽略大小写

        User user = new User();
        user.setName("e");
        user.setAge(20);
        Example<User> example = Example.of(user,matcher);
        List<User> all = userRepository.findAll(example);
        System.out.println(all);
    }
    //分页查询
    @Test
    public void findPageUserAll(){
        //设置分页参数
        //0代表第一页
        Pageable pageable = PageRequest.of(0, 3);
        //查询条件
        User user = new User();
        user.setName("test");
        Example<User> userExample = Example.of(user);//查询条件
        Page<User> page = userRepository.findAll(userExample, pageable);
        System.out.println(page.getContent());
    }
    //修改操作
    @Test
    public void updateUser(){
        //先根据id出要修改的用户
        User user = userRepository.findById("6406f9b94493ab599c5b0db9").get();
        //设置修改的值
        user.setName("haha");
        user.setAge(23);
        user.setEmail("hah@qq.com");
        User user1 = userRepository.save(user);
        System.out.println(user1);
    }
    //删除
    @Test
    public void delete(){
        userRepository.deleteById("6406f9b94493ab599c5b0db9");
    }

}