MongoDB - 快速指南



MongoDB - 概述

MongoDB 是一个跨平台的、面向文档的数据库,它提供高性能、高可用性和易扩展性。MongoDB 基于集合和文档的概念。

数据库

数据库是集合的物理容器。每个数据库在文件系统上都有自己的一组文件。单个 MongoDB 服务器通常包含多个数据库。

集合

集合是一组 MongoDB 文档。它相当于 RDBMS 表格。集合存在于单个数据库中。集合不强制执行模式。集合中的文档可以具有不同的字段。通常,集合中的所有文档都具有相同或相关的用途。

文档

文档是一组键值对。文档具有动态模式。动态模式意味着同一集合中的文档不需要具有相同的字段集或结构,并且集合中文档的公共字段可能保存不同类型的数据。

下表显示了 RDBMS 术语与 MongoDB 的关系。

RDBMS MongoDB
数据库 数据库
集合
元组/行 文档
字段
表连接 嵌入式文档
主键 主键(MongoDB 本身提供的默认键 _id)
数据库服务器和客户端
Mysqld/Oracle mongod
mysql/sqlplus mongo

示例文档

以下示例显示了博客网站的文档结构,它只是一个逗号分隔的键值对。

{
   _id: ObjectId(7df78ad8902c)
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by: 'tutorials point',
   url: 'https://tutorialspoint.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100, 
   comments: [	
      {
         user:'user1',
         message: 'My first comment',
         dateCreated: new Date(2011,1,20,2,15),
         like: 0 
      },
      {
         user:'user2',
         message: 'My second comments',
         dateCreated: new Date(2011,1,25,7,45),
         like: 5
      }
   ]
}

_id 是一个 12 字节的十六进制数字,它确保每个文档的唯一性。您可以在插入文档时提供 _id。如果您不提供,则 MongoDB 会为每个文档提供一个唯一的 ID。这 12 个字节中,前 4 个字节用于当前时间戳,接下来的 3 个字节用于机器 ID,接下来的 2 个字节用于 MongoDB 服务器的进程 ID,其余 3 个字节是简单的增量值。

MongoDB - 优点

任何关系数据库都具有典型的模式设计,该设计显示了表数以及这些表之间的关系。而在 MongoDB 中,没有关系的概念。

MongoDB 相对于 RDBMS 的优势

  • 无模式 - MongoDB 是一个文档数据库,其中一个集合包含不同的文档。文档的字段数、内容和大小可能彼此不同。

  • 单个对象的结构清晰。

  • 没有复杂的连接。

  • 强大的查询能力。MongoDB 支持使用基于文档的查询语言对文档进行动态查询,该语言几乎与 SQL 一样强大。

  • 调整。

  • 易于扩展 - MongoDB 易于扩展。

  • 无需将应用程序对象转换为数据库对象。

  • 使用内部内存存储(窗口化)工作集,从而能够更快地访问数据。

为什么要使用 MongoDB?

  • 面向文档的存储 - 数据以 JSON 样式文档的形式存储。

  • 对任何属性进行索引

  • 复制和高可用性

  • 自动分片

  • 丰富的查询

  • 快速就地更新

  • MongoDB 提供专业支持

在哪里使用 MongoDB?

  • 大数据
  • 内容管理和交付
  • 移动和社交基础设施
  • 用户数据管理
  • 数据中心

MongoDB - 环境

现在让我们看看如何在 Windows 上安装 MongoDB。

在 Windows 上安装 MongoDB

要在 Windows 上安装 MongoDB,首先从https://www.mongodb.org/downloads下载最新版本的 MongoDB。请确保根据您的 Windows 版本获取正确的 MongoDB 版本。要获取您的 Windows 版本,请打开命令提示符并执行以下命令。

C:\>wmic os get osarchitecture
OSArchitecture
64-bit
C:\>

32 位版本的 MongoDB 仅支持小于 2GB 的数据库,仅适用于测试和评估目的。

现在将下载的文件解压缩到 c:\ 驱动器或任何其他位置。确保解压缩文件夹的名称为 mongodb-win32-i386-[version] 或 mongodb-win32-x86_64-[version]。此处 [version] 是 MongoDB 下载的版本。

接下来,打开命令提示符并运行以下命令。

C:\>move mongodb-win64-* mongodb
   1 dir(s) moved.
C:\>

如果您在不同位置解压缩了 MongoDB,则使用命令 cd FOLDER/DIR 转到该路径,然后运行上述过程。

MongoDB 需要一个数据文件夹来存储其文件。MongoDB 数据目录的默认位置是 c:\data\db。因此,您需要使用命令提示符创建此文件夹。执行以下命令序列。

C:\>md data
C:\md data\db

如果您必须在不同位置安装 MongoDB,则需要通过在 mongod.exe 中设置路径 dbpath 来指定 \data\db 的备用路径。为此,请发出以下命令。

在命令提示符中,导航到 MongoDB 安装文件夹中的 bin 目录。假设我的安装文件夹是 D:\set up\mongodb

C:\Users\XYZ>d:
D:\>cd "set up"
D:\set up>cd mongodb
D:\set up\mongodb>cd bin
D:\set up\mongodb\bin>mongod.exe --dbpath "d:\set up\mongodb\data" 

这将在控制台输出上显示 等待连接 消息,这表示 mongod.exe 进程已成功运行。

现在要运行 MongoDB,您需要打开另一个命令提示符并发出以下命令。

D:\set up\mongodb\bin>mongo.exe
MongoDB shell version: 2.4.6
connecting to: test
>db.test.save( { a: 1 } )
>db.test.find()
{ "_id" : ObjectId(5879b0f65a56a454), "a" : 1 }
>

这将显示 MongoDB 已成功安装和运行。下次运行 MongoDB 时,您只需要发出命令即可。

D:\set up\mongodb\bin>mongod.exe --dbpath "d:\set up\mongodb\data" 
D:\set up\mongodb\bin>mongo.exe

在 Ubuntu 上安装 MongoDB

运行以下命令以导入 MongoDB 公共 GPG 密钥 -

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10

使用以下命令创建 /etc/apt/sources.list.d/mongodb.list 文件。

echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' 
   | sudo tee /etc/apt/sources.list.d/mongodb.list

现在发出以下命令以更新存储库 -

sudo apt-get update

接下来,使用以下命令安装 MongoDB -

apt-get install mongodb-10gen = 2.2.3

在上述安装中,2.2.3 是当前发布的 MongoDB 版本。请务必始终安装最新版本。现在 MongoDB 已成功安装。

启动 MongoDB

sudo service mongodb start

停止 MongoDB

sudo service mongodb stop

重新启动 MongoDB

sudo service mongodb restart

要使用 MongoDB,请运行以下命令。

mongo

这将连接到正在运行的 MongoDB 实例。

MongoDB 帮助

要获取命令列表,请在 MongoDB 客户端中键入 db.help()。这将为您提供如下面的屏幕截图所示的命令列表。

DB Help

MongoDB 统计信息

要获取有关 MongoDB 服务器的统计信息,请在 MongoDB 客户端中键入命令 db.stats()。这将显示数据库名称、数据库中的集合数和文档数。该命令的输出显示在下面的屏幕截图中。

DB Stats

MongoDB - 数据建模

MongoDB 中的数据具有灵活的模式。同一集合中的文档。它们不需要具有相同的字段集或结构,并且集合中文档的公共字段可能保存不同类型的数据。

在 MongoDB 中设计模式时的一些注意事项

  • 根据用户需求设计模式。

  • 如果要将对象一起使用,则将其组合到一个文档中。否则将它们分开(但要确保不需要连接)。

  • 复制数据(但要有限制),因为与计算时间相比,磁盘空间便宜。

  • 在写入时执行连接,而不是在读取时执行连接。

  • 针对最频繁的使用案例优化模式。

  • 在模式中进行复杂的聚合。

示例

假设客户需要一个用于其博客/网站的数据库设计,并查看 RDBMS 和 MongoDB 模式设计之间的差异。网站具有以下要求。

  • 每个帖子都有唯一的标题、描述和 URL。
  • 每个帖子可以有一个或多个标签。
  • 每个帖子都有其发布者的姓名和点赞总数。
  • 每个帖子都有用户提供的评论,以及他们的姓名、消息、日期时间和点赞。
  • 每个帖子可以有零个或多个评论。

在 RDBMS 模式中,上述要求的设计将至少包含三个表。

RDBMS Schema Design

而在 MongoDB 模式中,设计将包含一个集合 post 以及以下结构 -

{
   _id: POST_ID
   title: TITLE_OF_POST, 
   description: POST_DESCRIPTION,
   by: POST_BY,
   url: URL_OF_POST,
   tags: [TAG1, TAG2, TAG3],
   likes: TOTAL_LIKES, 
   comments: [	
      {
         user:'COMMENT_BY',
         message: TEXT,
         dateCreated: DATE_TIME,
         like: LIKES 
      },
      {
         user:'COMMENT_BY',
         message: TEXT,
         dateCreated: DATE_TIME,
         like: LIKES
      }
   ]
}

因此,在显示数据时,在 RDBMS 中,您需要连接三个表,而在 MongoDB 中,数据将仅从一个集合中显示。

MongoDB - 创建数据库

在本章中,我们将了解如何在 MongoDB 中创建数据库。

use 命令

MongoDB 的 use DATABASE_NAME 用于创建数据库。如果数据库不存在,则该命令将创建一个新数据库,否则它将返回现有数据库。

语法

use DATABASE 语句的基本语法如下 -

use DATABASE_NAME

示例

如果您要使用名为 <mydb> 的数据库,则 use DATABASE 语句将如下所示 -

>use mydb
switched to db mydb

要检查您当前选择的数据库,请使用命令 db

>db
mydb

如果您要检查数据库列表,请使用命令 show dbs

>show dbs
local     0.78125GB
test      0.23012GB

您创建的数据库(mydb)不在列表中。要显示数据库,您需要向其中插入至少一个文档。

>db.movie.insert({"name":"tutorials point"})
>show dbs
local      0.78125GB
mydb       0.23012GB
test       0.23012GB

在 MongoDB 中,默认数据库是 test。如果您没有创建任何数据库,则集合将存储在 test 数据库中。

MongoDB - 删除数据库

在本章中,我们将了解如何使用 MongoDB 命令删除数据库。

dropDatabase() 方法

MongoDB 的 db.dropDatabase() 命令用于删除现有数据库。

语法

dropDatabase() 命令的基本语法如下 -

db.dropDatabase()

这将删除选定的数据库。如果您没有选择任何数据库,则它将删除默认的“test”数据库。

示例

首先,使用命令 show dbs 检查可用数据库的列表。

>show dbs
local      0.78125GB
mydb       0.23012GB
test       0.23012GB
>

如果您要删除新数据库 <mydb>,则 dropDatabase() 命令将如下所示 -

>use mydb
switched to db mydb
>db.dropDatabase()
>{ "dropped" : "mydb", "ok" : 1 }
>

现在检查数据库列表。

>show dbs
local      0.78125GB
test       0.23012GB
>

MongoDB - 创建集合

在本章中,我们将了解如何在 MongoDB 中创建集合。

createCollection() 方法

MongoDB 的 db.createCollection(name, options) 用于创建集合。

语法

createCollection() 命令的基本语法如下 -

db.createCollection(name, options)

在命令中,name 是要创建的集合的名称。Options 是一个文档,用于指定集合的配置。

参数 类型 描述
Name String 要创建的集合的名称
Options 文档 (可选) 指定关于内存大小和索引的选项

Options 参数是可选的,因此您只需要指定集合的名称。以下是您可以使用的选项列表 -

字段 类型 描述
capped 布尔型 (可选) 如果为 true,则启用固定大小集合。固定大小集合是一个固定大小的集合,当它达到最大大小时会自动覆盖其最旧的条目。如果您指定 true,则也需要指定 size 参数。
autoIndexId 布尔型 (可选) 如果为 true,则自动在 _id 字段上创建索引。默认值为 false。
size 数字 (可选) 为固定大小集合指定最大大小(以字节为单位)。如果 capped 为 true,则也需要指定此字段。
max 数字 (可选) 指定固定大小集合中允许的最大文档数。

插入文档时,MongoDB 首先检查固定大小集合的 size 字段,然后检查 max 字段。

示例

不带选项的createCollection()方法的基本语法如下所示 -

>use test
switched to db test
>db.createCollection("mycollection")
{ "ok" : 1 }
>

您可以使用show collections命令检查已创建的集合。

>show collections
mycollection
system.indexes

以下示例显示了带有一些重要选项的createCollection()方法的语法 -

>db.createCollection("mycol", { capped : true, autoIndexId : true, size : 
   6142800, max : 10000 } )
{ "ok" : 1 }
>

在 MongoDB 中,您不需要创建集合。当您插入一些文档时,MongoDB 会自动创建集合。

>db.tutorialspoint.insert({"name" : "tutorialspoint"})
>show collections
mycol
mycollection
system.indexes
tutorialspoint
>

MongoDB - 删除集合

在本章中,我们将了解如何使用 MongoDB 删除集合。

drop() 方法

MongoDB 的db.collection.drop()用于从数据库中删除集合。

语法

drop()命令的基本语法如下所示 -

db.COLLECTION_NAME.drop()

示例

首先,检查数据库mydb中可用的集合。

>use mydb
switched to db mydb
>show collections
mycol
mycollection
system.indexes
tutorialspoint
>

现在删除名为mycollection的集合。

>db.mycollection.drop()
true
>

再次检查数据库中的集合列表。

>show collections
mycol
system.indexes
tutorialspoint
>

如果成功删除选定的集合,则drop()方法将返回 true,否则将返回 false。

MongoDB - 数据类型

MongoDB 支持多种数据类型。其中一些是 -

  • 字符串 - 这是用于存储数据的最常用的数据类型。MongoDB 中的字符串必须是 UTF-8 有效的。

  • 整数 - 此类型用于存储数值。整数可以是 32 位或 64 位,具体取决于您的服务器。

  • 布尔型 - 此类型用于存储布尔(true/false)值。

  • 双精度浮点数 - 此类型用于存储浮点值。

  • 最小/最大键 - 此类型用于将值与最低和最高的 BSON 元素进行比较。

  • 数组 - 此类型用于将数组或列表或多个值存储到一个键中。

  • 时间戳 - ctimestamp。这对于记录文档何时被修改或添加非常有用。

  • 对象 - 此数据类型用于嵌入文档。

  • 空值 - 此类型用于存储空值。

  • 符号 - 此数据类型与字符串的使用方式相同;但是,它通常保留给使用特定符号类型的语言。

  • 日期 - 此数据类型用于以 UNIX 时间格式存储当前日期或时间。您可以通过创建 Date 对象并将日期、月份、年份传递给它来指定您自己的日期时间。

  • 对象 ID - 此数据类型用于存储文档的 ID。

  • 二进制数据 - 此数据类型用于存储二进制数据。

  • 代码 - 此数据类型用于将 JavaScript 代码存储到文档中。

  • 正则表达式 - 此数据类型用于存储正则表达式。

MongoDB - 插入文档

在本章中,我们将学习如何在 MongoDB 集合中插入文档。

insert() 方法

要将数据插入 MongoDB 集合,您需要使用 MongoDB 的insert()save()方法。

语法

insert()命令的基本语法如下所示 -

>db.COLLECTION_NAME.insert(document)

示例

>db.mycol.insert({
   _id: ObjectId(7df78ad8902c),
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by: 'tutorials point',
   url: 'https://tutorialspoint.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100
})

这里mycol是我们的集合名称,如上一章所创建。如果数据库中不存在该集合,则 MongoDB 将创建此集合,然后向其中插入一个文档。

在插入的文档中,如果我们不指定 _id 参数,则 MongoDB 会为该文档分配一个唯一的 ObjectId。

_id 是 12 个字节的十六进制数字,对于集合中的每个文档都是唯一的。12 个字节的划分如下所示 -

_id: ObjectId(4 bytes timestamp, 3 bytes machine id, 2 bytes process id, 
   3 bytes incrementer)

要在单个查询中插入多个文档,可以在 insert() 命令中传递一个文档数组。

示例

>db.post.insert([
   {
      title: 'MongoDB Overview', 
      description: 'MongoDB is no sql database',
      by: 'tutorials point',
      url: 'https://tutorialspoint.com',
      tags: ['mongodb', 'database', 'NoSQL'],
      likes: 100
   },
	
   {
      title: 'NoSQL Database', 
      description: "NoSQL database doesn't have tables",
      by: 'tutorials point',
      url: 'https://tutorialspoint.com',
      tags: ['mongodb', 'database', 'NoSQL'],
      likes: 20, 
      comments: [	
         {
            user:'user1',
            message: 'My first comment',
            dateCreated: new Date(2013,11,10,2,35),
            like: 0 
         }
      ]
   }
])

要插入文档,您也可以使用db.post.save(document)。如果您没有在文档中指定_id,则save()方法的工作方式与insert()方法相同。如果您指定了 _id,则它将替换包含在 save() 方法中指定的 _id 的文档的全部数据。

MongoDB - 查询文档

在本章中,我们将学习如何从 MongoDB 集合中查询文档。

find() 方法

要从 MongoDB 集合中查询数据,您需要使用 MongoDB 的find()方法。

语法

find()方法的基本语法如下所示 -

>db.COLLECTION_NAME.find()

find()方法将以非结构化的方式显示所有文档。

pretty() 方法

要以格式化的方式显示结果,可以使用pretty()方法。

语法

>db.mycol.find().pretty()

示例

>db.mycol.find().pretty()
{
   "_id": ObjectId(7df78ad8902c),
   "title": "MongoDB Overview", 
   "description": "MongoDB is no sql database",
   "by": "tutorials point",
   "url": "https://tutorialspoint.com",
   "tags": ["mongodb", "database", "NoSQL"],
   "likes": "100"
}
>

除了 find() 方法之外,还有一个findOne()方法,它只返回一个文档。

RDBMS 中 Where 子句等效项在 MongoDB 中

要根据某些条件查询文档,您可以使用以下操作。

操作 语法 示例 RDBMS 等效项
等于 {<key>:<value>} db.mycol.find({"by":"tutorials point"}).pretty() where by = 'tutorials point'
小于 {<key>:{$lt:<value>}} db.mycol.find({"likes":{$lt:50}}).pretty() where likes < 50
小于等于 {<key>:{$lte:<value>}} db.mycol.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {<key>:{$gt:<value>}} db.mycol.find({"likes":{$gt:50}}).pretty() where likes > 50
大于等于 {<key>:{$gte:<value>}} db.mycol.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {<key>:{$ne:<value>}} db.mycol.find({"likes":{$ne:50}}).pretty() where likes != 50

MongoDB 中的 AND

语法

find()方法中,如果您通过用 ',' 分隔多个键来传递它们,则 MongoDB 会将其视为AND条件。以下是AND的基本语法 -

>db.mycol.find(
   {
      $and: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()

示例

以下示例将显示由“tutorials point”编写且标题为“MongoDB 概述”的所有教程。

>db.mycol.find({$and:[{"by":"tutorials point"},{"title": "MongoDB Overview"}]}).pretty() {
   "_id": ObjectId(7df78ad8902c),
   "title": "MongoDB Overview", 
   "description": "MongoDB is no sql database",
   "by": "tutorials point",
   "url": "https://tutorialspoint.com",
   "tags": ["mongodb", "database", "NoSQL"],
   "likes": "100"
}

对于上面给出的示例,等效的 where 子句将为' where by = 'tutorials point' AND title = 'MongoDB Overview' '。您可以在 find 子句中传递任意数量的键值对。

MongoDB 中的 OR

语法

要根据 OR 条件查询文档,您需要使用$or关键字。以下是OR的基本语法 -

>db.mycol.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()

示例

以下示例将显示由“tutorials point”编写或标题为“MongoDB 概述”的所有教程。

>db.mycol.find({$or:[{"by":"tutorials point"},{"title": "MongoDB Overview"}]}).pretty()
{
   "_id": ObjectId(7df78ad8902c),
   "title": "MongoDB Overview", 
   "description": "MongoDB is no sql database",
   "by": "tutorials point",
   "url": "https://tutorialspoint.com",
   "tags": ["mongodb", "database", "NoSQL"],
   "likes": "100"
}
>

一起使用 AND 和 OR

示例

以下示例将显示点赞数大于 10 且标题为“MongoDB 概述”或作者为“tutorials point”的文档。等效的 SQL where 子句为'where likes>10 AND (by = 'tutorials point' OR title = 'MongoDB Overview')'

>db.mycol.find({"likes": {$gt:10}, $or: [{"by": "tutorials point"},
   {"title": "MongoDB Overview"}]}).pretty()
{
   "_id": ObjectId(7df78ad8902c),
   "title": "MongoDB Overview", 
   "description": "MongoDB is no sql database",
   "by": "tutorials point",
   "url": "https://tutorialspoint.com",
   "tags": ["mongodb", "database", "NoSQL"],
   "likes": "100"
}
>

MongoDB - 更新文档

MongoDB 的update()save()方法用于更新集合中的文档。update() 方法更新现有文档中的值,而 save() 方法用 save() 方法中传递的文档替换现有文档。

MongoDB Update() 方法

update() 方法更新现有文档中的值。

语法

update()方法的基本语法如下所示 -

>db.COLLECTION_NAME.update(SELECTION_CRITERIA, UPDATED_DATA)

示例

假设 mycol 集合具有以下数据。

{ "_id" : ObjectId(5983548781331adf45ec5), "title":"MongoDB Overview"}
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}

以下示例将为标题为“MongoDB 概述”的文档设置新的标题“新的 MongoDB 教程”。

>db.mycol.update({'title':'MongoDB Overview'},{$set:{'title':'New MongoDB Tutorial'}})
>db.mycol.find()
{ "_id" : ObjectId(5983548781331adf45ec5), "title":"New MongoDB Tutorial"}
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}
>

默认情况下,MongoDB 只会更新单个文档。要更新多个文档,您需要将参数“multi”设置为 true。

>db.mycol.update({'title':'MongoDB Overview'},
   {$set:{'title':'New MongoDB Tutorial'}},{multi:true})

MongoDB Save() 方法

save()方法用 save() 方法中传递的新文档替换现有文档。

语法

MongoDB save()方法的基本语法如下所示 -

>db.COLLECTION_NAME.save({_id:ObjectId(),NEW_DATA})

示例

以下示例将替换 _id 为“5983548781331adf45ec5”的文档。

>db.mycol.save(
   {
      "_id" : ObjectId(5983548781331adf45ec5), "title":"Tutorials Point New Topic",
      "by":"Tutorials Point"
   }
)
>db.mycol.find()
{ "_id" : ObjectId(5983548781331adf45ec5), "title":"Tutorials Point New Topic",
   "by":"Tutorials Point"}
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}
>

MongoDB - 删除文档

在本章中,我们将学习如何使用 MongoDB 删除文档。

remove() 方法

MongoDB 的remove()方法用于从集合中删除文档。remove() 方法接受两个参数。一个是删除条件,第二个是 justOne 标志。

  • 删除条件 - (可选)根据文档将删除的删除条件。

  • justOne - (可选)如果设置为 true 或 1,则仅删除一个文档。

语法

remove()方法的基本语法如下所示 -

>db.COLLECTION_NAME.remove(DELLETION_CRITTERIA)

示例

假设 mycol 集合具有以下数据。

{ "_id" : ObjectId(5983548781331adf45ec5), "title":"MongoDB Overview"}
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}

以下示例将删除标题为“MongoDB 概述”的所有文档。

>db.mycol.remove({'title':'MongoDB Overview'})
>db.mycol.find()
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}
>

仅删除一个

如果有多条记录并且您只想删除第一条记录,则在remove()方法中设置justOne参数。

>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)

删除所有文档

如果您不指定删除条件,则 MongoDB 将删除集合中的所有文档。这相当于 SQL 的截断命令。

>db.mycol.remove({})
>db.mycol.find()
>

MongoDB - 投影

在 MongoDB 中,投影意味着选择仅必要的数据,而不是选择文档的全部数据。如果一个文档有 5 个字段,而您只需要显示 3 个,则只选择其中的 3 个字段。

find() 方法

MongoDB 查询文档中解释的 MongoDB 的find()方法接受第二个可选参数,即您要检索的字段列表。在 MongoDB 中,当您执行find()方法时,它会显示文档的所有字段。要限制此操作,您需要设置一个值为 1 或 0 的字段列表。1 用于显示字段,而 0 用于隐藏字段。

语法

带投影的find()方法的基本语法如下所示 -

>db.COLLECTION_NAME.find({},{KEY:1})

示例

假设集合 mycol 具有以下数据 -

{ "_id" : ObjectId(5983548781331adf45ec5), "title":"MongoDB Overview"}
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}

以下示例将在查询文档时显示文档的标题。

>db.mycol.find({},{"title":1,_id:0})
{"title":"MongoDB Overview"}
{"title":"NoSQL Overview"}
{"title":"Tutorials Point Overview"}
>

请注意,在执行find()方法时,始终显示_id字段,如果您不希望此字段,则需要将其设置为 0。

MongoDB - 限制记录

在本章中,我们将学习如何使用 MongoDB 限制记录。

Limit() 方法

要限制 MongoDB 中的记录,您需要使用limit()方法。该方法接受一个数字类型参数,即您希望显示的文档数量。

语法

limit()方法的基本语法如下所示 -

>db.COLLECTION_NAME.find().limit(NUMBER)

示例

假设集合 myycol 具有以下数据。

{ "_id" : ObjectId(5983548781331adf45ec5), "title":"MongoDB Overview"}
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}

以下示例在查询文档时只会显示两个文档。

>db.mycol.find({},{"title":1,_id:0}).limit(2)
{"title":"MongoDB Overview"}
{"title":"NoSQL Overview"}
>

如果您没有在limit()方法中指定数字参数,则它将显示集合中的所有文档。

MongoDB Skip() 方法

除了 `limit()` 方法外,还有一个方法 `skip()` 也接受数字类型的参数,用于跳过指定数量的文档。

语法

`skip()` 方法的基本语法如下所示:

>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

示例

下面的示例将只显示第二个文档。

>db.mycol.find({},{"title":1,_id:0}).limit(1).skip(1)
{"title":"NoSQL Overview"}
>

请注意,`skip()` 方法的默认值为 0。

MongoDB - 排序记录

在本章中,我们将学习如何在 MongoDB 中排序记录。

`sort()` 方法

要对 MongoDB 中的文档进行排序,需要使用 `sort()` 方法。该方法接受一个包含字段列表及其排序顺序的文档。要指定排序顺序,使用 1 和 -1。1 用于升序,-1 用于降序。

语法

`sort()` 方法的基本语法如下所示:

>db.COLLECTION_NAME.find().sort({KEY:1})

示例

假设集合 myycol 具有以下数据。

{ "_id" : ObjectId(5983548781331adf45ec5), "title":"MongoDB Overview"}
{ "_id" : ObjectId(5983548781331adf45ec6), "title":"NoSQL Overview"}
{ "_id" : ObjectId(5983548781331adf45ec7), "title":"Tutorials Point Overview"}

下面的示例将按标题降序显示文档。

>db.mycol.find({},{"title":1,_id:0}).sort({"title":-1})
{"title":"Tutorials Point Overview"}
{"title":"NoSQL Overview"}
{"title":"MongoDB Overview"}
>

请注意,如果不指定排序顺序,则 `sort()` 方法将按升序显示文档。

MongoDB - 索引

索引支持高效地解决查询。如果没有索引,MongoDB 必须扫描集合中的每个文档以选择与查询语句匹配的文档。此扫描效率极低,并且需要 MongoDB 处理大量数据。

索引是特殊的数据结构,以易于遍历的形式存储数据集的一小部分数据。索引存储特定字段或字段集的值,并按索引中指定的字段值排序。

`createIndex()` 方法

要创建索引,需要使用 MongoDB 的 `createIndex()` 方法。

语法

`createIndex()` 方法的基本语法如下所示。

>db.COLLECTION_NAME.createIndex({KEY:1})

这里 `key` 是要创建索引的字段的名称,1 表示升序。要创建降序索引,需要使用 -1。

示例

>db.mycol.createIndex({"title":1})
{
	"createdCollectionAutomatically" : false,
	"numIndexesBefore" : 1,
	"numIndexesAfter" : 2,
	"ok" : 1
}
>

在 `createIndex()` 方法中,可以传递多个字段,以在多个字段上创建索引。

>db.mycol.createIndex({"title":1,"description":-1})
>

此方法还接受选项列表(可选)。以下是列表:

参数 类型 描述
`background` 布尔型 在后台构建索引,以便构建索引不会阻塞其他数据库活动。指定 `true` 以在后台构建。默认值为 `false` 。
`unique` 布尔型 创建唯一索引,以便集合不会接受插入索引键或键与索引中现有值匹配的文档。指定 `true` 以创建唯一索引。默认值为 `false` 。
`name` `string` 索引的名称。如果未指定,MongoDB 会通过连接已索引字段的名称和排序顺序来生成索引名称。
`sparse` 布尔型 如果为 `true` ,则索引仅引用具有指定字段的文档。这些索引使用的空间更少,但在某些情况下(尤其是排序)的行为有所不同。默认值为 `false` 。
`expireAfterSeconds` `integer` 以秒为单位指定一个值作为 TTL,以控制 MongoDB 在此集合中保留文档的时间。
`weights` `document` 权重是 1 到 99,999 之间的数字,表示字段相对于其他已索引字段在分数方面的意义。
`default_language` `string` 对于文本索引,确定停用词列表以及词干分析器和分词器的规则的语言。默认值为 `English` 。
`language_override` `string` 对于文本索引,指定文档中包含用于覆盖默认语言的语言的字段的名称。默认值为 `language` 。

`dropIndex()` 方法

可以使用 MongoDB 的 `dropIndex()` 方法删除特定索引。

语法

`DropIndex()` 方法的基本语法如下所示。

>db.COLLECTION_NAME.dropIndex({KEY:1})

这里,"key" 是要删除现有索引的文件的名称。除了索引规范文档(上述语法)之外,还可以直接指定索引的名称,如下所示

dropIndex("name_of_the_index")

示例

> db.mycol.dropIndex({"title":1})
{
	"ok" : 0,
	"errmsg" : "can't find index with key: { title: 1.0 }",
	"code" : 27,
	"codeName" : "IndexNotFound"
}

`dropIndexes()` 方法

此方法删除集合上的多个(指定的)索引。

语法

`DropIndexes()` 方法的基本语法如下所示:

>db.COLLECTION_NAME.dropIndexes()

示例

假设我们在名为 mycol 的集合中创建了 2 个索引,如下所示:

> db.mycol.createIndex({"title":1,"description":-1})

以下示例删除上面创建的 mycol 的索引:

>db.mycol.dropIndexes({"title":1,"description":-1})
{ "nIndexesWas" : 2, "ok" : 1 }
>

`getIndexes()` 方法

此方法返回集合中所有索引的描述。

语法

以下是 `getIndexes()` 方法的基本语法:

db.COLLECTION_NAME.getIndexes()

示例

假设我们在名为 mycol 的集合中创建了 2 个索引,如下所示:

> db.mycol.createIndex({"title":1,"description":-1})

以下示例检索集合 mycol 中的所有索引:

> db.mycol.getIndexes()
[
	{
		"v" : 2,
		"key" : {
			"_id" : 1
		},
		"name" : "_id_",
		"ns" : "test.mycol"
	},
	{
		"v" : 2,
		"key" : {
			"title" : 1,
			"description" : -1
		},
		"name" : "title_1_description_-1",
		"ns" : "test.mycol"
	}
]
>

MongoDB - 聚合

聚合操作处理数据记录并返回计算结果。聚合操作将来自多个文档的值组合在一起,并且可以在组合数据上执行各种操作以返回单个结果。在 SQL 中,`count(*)` 和 `group by` 等价于 mongodb 聚合。

`aggregate()` 方法

对于 MongoDB 中的聚合,应使用 `aggregate()` 方法。

语法

`aggregate()` 方法的基本语法如下所示:

>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

示例

在集合中,您有以下数据:

{
   _id: ObjectId(7df78ad8902c)
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by_user: 'tutorials point',
   url: 'https://tutorialspoint.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100
},
{
   _id: ObjectId(7df78ad8902d)
   title: 'NoSQL Overview', 
   description: 'No sql database is very fast',
   by_user: 'tutorials point',
   url: 'https://tutorialspoint.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 10
},
{
   _id: ObjectId(7df78ad8902e)
   title: 'Neo4j Overview', 
   description: 'Neo4j is no sql database',
   by_user: 'Neo4j',
   url: 'http://www.neo4j.com',
   tags: ['neo4j', 'database', 'NoSQL'],
   likes: 750
},

现在,从上面的集合中,如果要显示一个列表,说明每个用户编写了多少个教程,则将使用以下 `aggregate()` 方法:

> db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
{
   "result" : [
      {
         "_id" : "tutorials point",
         "num_tutorial" : 2
      },
      {
         "_id" : "Neo4j",
         "num_tutorial" : 1
      }
   ],
   "ok" : 1
}
>

上述用例的等效 SQL 查询将为 `select by_user, count(*) from mycol group by by_user` 。

在上面的示例中,我们已按字段 `by_user` 对文档进行分组,并且在 `by_user` 的每次出现时,都会递增 `sum` 的先前值。以下是可用聚合表达式的列表。

表达式 描述 示例
`$sum` 将集合中所有文档中定义的值加起来。 `db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])`
`$avg` 计算集合中所有文档中所有给定值的平均值。 `db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])`
`$min` 获取集合中所有文档中相应值的最小值。 `db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])`
`$max` 获取集合中所有文档中相应值的最大值。 `db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])`
`$push` 将值插入到结果文档中的数组中。 `db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])`
`$addToSet` 将值插入到结果文档中的数组中,但不创建重复项。 `db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])`
`$first` 根据分组从源文档中获取第一个文档。通常,这只有与之前应用的某些“$sort”阶段一起使用才有意义。 `db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])`
`$last` 根据分组从源文档中获取最后一个文档。通常,这只有与之前应用的某些“$sort”阶段一起使用才有意义。 `db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])`

管道概念

在 UNIX 命令中,shell 管道意味着可以在某些输入上执行操作并将输出用作下一个命令的输入,依此类推。MongoDB 在聚合框架中也支持相同的概念。有一组可能的阶段,每个阶段都被视为一组文档作为输入,并产生一组结果文档(或管道末尾的最终结果 JSON 文档)。然后,这可以依次用于下一个阶段,依此类推。

以下是聚合框架中可能的阶段:

  • `$project` - 用于从集合中选择一些特定字段。

  • `$match` - 这是一个过滤操作,因此这可以减少作为输入传递给下一阶段的文档数量。

  • `$group` - 如上所述,这将执行实际的聚合。

  • `$sort` - 对文档进行排序。

  • `$skip` - 通过此操作,可以向前跳过给定数量的文档列表。

  • `$limit` - 这将限制要查看的文档数量,从当前位置开始的给定数量。

  • `$unwind` - 这用于展开使用数组的文档。使用数组时,数据会某种程度上预先连接,此操作将通过此操作撤消,以便再次获得单个文档。因此,通过此阶段,我们将增加下一阶段的文档数量。

MongoDB - 复制

复制是跨多个服务器同步数据的过程。复制提供冗余并通过在不同数据库服务器上复制数据来提高数据可用性。复制保护数据库免受单个服务器丢失的影响。复制还允许您从硬件故障和服务中断中恢复。使用数据的其他副本,您可以将一个副本专门用于灾难恢复、报告或备份。

为什么要复制?

  • 为了确保您的数据安全
  • 数据的高(24*7)可用性
  • 灾难恢复
  • 维护(如备份、索引重建、压缩)期间无停机时间
  • 读取扩展(额外的副本可供读取)
  • 副本集对应用程序是透明的

MongoDB 中的复制工作原理

MongoDB 通过使用副本集来实现复制。副本集是一组承载相同数据集的 `mongod` 实例。在一个副本中,一个节点是主节点,它接收所有写操作。所有其他实例(例如辅助节点)都应用来自主节点的操作,以便它们具有相同的数据集。副本集只能有一个主节点。

  • 副本集是由两个或多个节点组成的一组(通常至少需要 3 个节点)。

  • 在一个副本集中,一个节点是主节点,其余节点是辅助节点。

  • 所有数据都从主节点复制到辅助节点。

  • 在自动故障转移或维护时,将为主要节点进行选举,并选举新的主节点。

  • 在故障节点恢复后,它将再次加入副本集并充当辅助节点。

MongoDB 复制的典型示意图显示,客户端应用程序始终与主节点交互,然后主节点将数据复制到辅助节点。

MongoDB Replication

副本集功能

  • 一个由 N 个节点组成的集群
  • 任何一个节点都可以是主节点
  • 所有写操作都转到主节点
  • 自动故障转移
  • 自动恢复
  • 主节点的一致性选举

设置副本集

在本教程中,我们将把独立的 MongoDB 实例转换为副本集。要转换为副本集,请执行以下步骤:

  • 关闭已运行的 MongoDB 服务器。

  • 通过指定 `--replSet` 选项启动 MongoDB 服务器。以下是 `--replSet` 的基本语法:

mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"

示例

mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
  • 它将在端口 27017 上启动一个名为 rs0 的 mongod 实例。

  • 现在启动命令提示符并连接到此 mongod 实例。

  • 在 Mongo 客户端中,发出命令rs.initiate()以初始化一个新的副本集。

  • 要检查副本集配置,请发出命令rs.conf()。要检查副本集的状态,请发出命令rs.status()

向副本集添加成员

要向副本集添加成员,请在多台机器上启动 mongod 实例。现在启动一个 mongo 客户端并发出命令rs.add()

语法

rs.add()命令的基本语法如下所示:

>rs.add(HOST_NAME:PORT)

示例

假设您的 mongod 实例名称为mongod1.net,并且它在端口27017上运行。要将此实例添加到副本集,请在 Mongo 客户端中发出命令rs.add()

>rs.add("mongod1.net:27017")
>

只有在连接到主节点时,才能将 mongod 实例添加到副本集。要检查是否已连接到主节点,请在 mongo 客户端中发出命令db.isMaster()

MongoDB - 分片

分片是将数据记录存储在多台机器上的过程,它是 MongoDB 满足数据增长需求的方法。随着数据大小的增加,单台机器可能不足以存储数据,也无法提供可接受的读写吞吐量。分片通过水平扩展解决了这个问题。使用分片,您可以添加更多机器来支持数据增长以及读写操作的需求。

为什么要分片?

  • 在复制中,所有写入都转到主节点
  • 延迟敏感查询仍然转到主节点
  • 单个副本集最多只能有 12 个节点
  • 当活动数据集很大时,内存可能不够大
  • 本地磁盘不够大
  • 垂直扩展成本过高

MongoDB 中的分片

下图显示了使用分片集群在 MongoDB 中进行分片。

MongoDB Sharding

在下图中,有三个主要组件:

  • 分片 - 分片用于存储数据。它们提供高可用性和数据一致性。在生产环境中,每个分片都是一个单独的副本集。

  • 配置服务器 - 配置服务器存储集群的元数据。此数据包含集群数据集到分片的映射。查询路由器使用此元数据将操作定位到特定分片。在生产环境中,分片集群正好有 3 个配置服务器。

  • 查询路由器 - 查询路由器基本上是 mongo 实例,与客户端应用程序交互并将操作引导到相应的分片。查询路由器处理并将操作定位到分片,然后将结果返回给客户端。分片集群可以包含多个查询路由器来划分客户端请求负载。客户端将请求发送到一个查询路由器。通常,分片集群有很多查询路由器。

MongoDB - 创建备份

在本章中,我们将了解如何在 MongoDB 中创建备份。

转储 MongoDB 数据

要创建 MongoDB 中数据库的备份,您应该使用mongodump命令。此命令会将服务器的全部数据转储到转储目录中。有许多选项可用于限制数据量或创建远程服务器的备份。

语法

mongodump命令的基本语法如下所示:

>mongodump

示例

启动您的 mongod 服务器。假设您的 mongod 服务器在 localhost 和端口 27017 上运行,请打开命令提示符并转到 mongodb 实例的 bin 目录,然后键入命令mongodump

假设 mycol 集合具有以下数据。

>mongodump

该命令将连接到在127.0.0.1和端口27017上运行的服务器,并将服务器的所有数据备份到目录/bin/dump/。以下是命令的输出:

DB Stats

以下是可与mongodump命令一起使用的可用选项列表。

语法 描述 示例
mongodump --host HOST_NAME --port PORT_NUMBER 此命令将备份指定 mongod 实例的所有数据库。 mongodump --host tutorialspoint.com --port 27017
mongodump --dbpath DB_PATH --out BACKUP_DIRECTORY 此命令将仅备份指定路径下的指定数据库。 mongodump --dbpath /data/db/ --out /data/backup/
mongodump --collection COLLECTION --db DB_NAME 此命令将仅备份指定数据库的指定集合。 mongodump --collection mycol --db test

恢复数据

要恢复备份数据,可以使用 MongoDB 的mongorestore命令。此命令会从备份目录恢复所有数据。

语法

mongorestore命令的基本语法如下所示:

>mongorestore

以下是命令的输出:

DB Stats

MongoDB - 部署

在准备 MongoDB 部署时,您应该尝试了解您的应用程序如何在生产环境中运行。最好制定一种一致且可重复的方法来管理您的部署环境,以便最大程度地减少在生产环境中出现任何意外情况。

最佳方法包括对您的设置进行原型设计、进行负载测试、监控关键指标,并利用这些信息来扩展您的设置。该方法的关键部分是主动监控您的整个系统 - 这将帮助您了解您的生产系统在部署之前将如何运行,并确定您需要在何处增加容量。例如,深入了解内存使用情况的潜在峰值可能有助于在写入锁故障发生之前将其扑灭。

为了监控您的部署,MongoDB 提供了一些以下命令:

mongostat

此命令检查所有正在运行的 mongod 实例的状态,并返回数据库操作的计数器。这些计数器包括插入、查询、更新、删除和游标。命令还显示何时遇到页面错误,并显示您的锁定百分比。这意味着您的内存不足、写入容量不足或存在某些性能问题。

要运行该命令,请启动您的 mongod 实例。在另一个命令提示符中,转到 mongodb 安装的bin目录,然后键入mongostat

D:\set up\mongodb\bin>mongostat

以下是命令的输出:

mongostat

mongotop

此命令跟踪并报告 MongoDB 实例在集合基础上的读写活动。默认情况下,mongotop每秒返回信息,您可以相应地更改它。您应该检查此读写活动是否与您的应用程序意图相匹配,并且您没有一次向数据库写入太多数据、过于频繁地从磁盘读取数据或超过您的工作集大小。

要运行该命令,请启动您的 mongod 实例。在另一个命令提示符中,转到 mongodb 安装的bin目录,然后键入mongotop

D:\set up\mongodb\bin>mongotop

以下是命令的输出:

mongotop

要更改mongotop命令以较不频繁地返回信息,请在 mongotop 命令后指定一个特定数字。

D:\set up\mongodb\bin>mongotop 30

以上示例将每 30 秒返回一次值。

除了 MongoDB 工具之外,10gen 还提供了一个免费的托管监控服务 MongoDB Management Service (MMS),该服务提供了一个仪表板,并为您提供整个集群的指标视图。

MongoDB - Java

在本章中,我们将学习如何设置 MongoDB JDBC 驱动程序。

安装

在开始在 Java 程序中使用 MongoDB 之前,您需要确保机器上已安装 MongoDB JDBC 驱动程序和 Java。您可以查看 Java 教程以了解如何在您的机器上安装 Java。现在,让我们检查如何设置 MongoDB JDBC 驱动程序。

  • 您需要从路径下载 mongo.jar下载 jar 文件。确保下载其最新版本。

  • 您需要将 mongo.jar 包含到您的类路径中。

连接到数据库

要连接数据库,您需要指定数据库名称,如果数据库不存在,则 MongoDB 会自动创建它。

以下是连接到数据库的代码片段:

import com.mongodb.client.MongoDatabase; 
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class ConnectToDB { 
   
   public static void main( String args[] ) {  
      
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 
   
      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb"); 
      System.out.println("Credentials ::"+ credential);     
   } 
}

现在,让我们编译并运行上述程序以创建我们的数据库 myDb,如下所示。

$javac ConnectToDB.java 
$java ConnectToDB

执行后,上述程序将为您提供以下输出。

Connected to the database successfully 
Credentials ::MongoCredential{
   mechanism = null, 
   userName = 'sampleUser', 
   source = 'myDb', 
   password = <hidden>, 
   mechanismProperties = {}
}

创建集合

要创建集合,可以使用com.mongodb.client.MongoDatabase类的createCollection()方法。

以下是创建集合的代码片段:

import com.mongodb.client.MongoDatabase; 
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class CreatingCollection { 
   
   public static void main( String args[] ) {  
      
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 
     
      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      //Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb");  
      
      //Creating a collection 
      database.createCollection("sampleCollection"); 
      System.out.println("Collection created successfully"); 
   } 
} 

编译后,上述程序将为您提供以下结果:

Connected to the database successfully 
Collection created successfully

获取/选择集合

要从数据库中获取/选择集合,可以使用com.mongodb.client.MongoDatabase类的getCollection()方法。

以下是获取/选择集合的程序:

import com.mongodb.client.MongoCollection; 
import com.mongodb.client.MongoDatabase; 

import org.bson.Document; 
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class selectingCollection { 
   
   public static void main( String args[] ) {  
      
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 
     
      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb");  
      
      // Creating a collection 
      System.out.println("Collection created successfully"); 

      // Retieving a collection
      MongoCollection<Document> collection = database.getCollection("myCollection"); 
      System.out.println("Collection myCollection selected successfully"); 
   }
}

编译后,上述程序将为您提供以下结果:

Connected to the database successfully 
Collection created successfully 
Collection myCollection selected successfully

插入文档

要将文档插入 MongoDB,可以使用com.mongodb.client.MongoCollection类的insert()方法。

以下是插入文档的代码片段:

import com.mongodb.client.MongoCollection; 
import com.mongodb.client.MongoDatabase; 

import org.bson.Document;  
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class InsertingDocument { 
   
   public static void main( String args[] ) {  
      
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 

      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb"); 

      // Retrieving a collection
      MongoCollection<Document> collection = database.getCollection("sampleCollection"); 
      System.out.println("Collection sampleCollection selected successfully");

      Document document = new Document("title", "MongoDB") 
      .append("id", 1)
      .append("description", "database") 
      .append("likes", 100) 
      .append("url", "https://tutorialspoint.com/mongodb/") 
      .append("by", "tutorials point");  
      collection.insertOne(document); 
      System.out.println("Document inserted successfully");     
   } 
}

编译后,上述程序将为您提供以下结果:

Connected to the database successfully 
Collection sampleCollection selected successfully 
Document inserted successfully

检索所有文档

要从集合中选择所有文档,可以使用com.mongodb.client.MongoCollection类的find()方法。此方法返回一个游标,因此您需要迭代此游标。

以下是选择所有文档的程序:

import com.mongodb.client.FindIterable; 
import com.mongodb.client.MongoCollection; 
import com.mongodb.client.MongoDatabase;  

import java.util.Iterator; 
import org.bson.Document; 
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class RetrievingAllDocuments { 
   
   public static void main( String args[] ) {  
      
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 

      // Creating Credentials 
      MongoCredential credential;
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb");  
      
      // Retrieving a collection 
      MongoCollection<Document> collection = database.getCollection("sampleCollection");
      System.out.println("Collection sampleCollection selected successfully"); 

      // Getting the iterable object 
      FindIterable<Document> iterDoc = collection.find(); 
      int i = 1; 

      // Getting the iterator 
      Iterator it = iterDoc.iterator(); 
    
      while (it.hasNext()) {  
         System.out.println(it.next());  
      i++; 
      }
   } 
}

编译后,上述程序将为您提供以下结果:

Document{{
   _id = 5967745223993a32646baab8, 
   title = MongoDB, 
   id = 1, 
   description = database, 
   likes = 100, 
   url = https://tutorialspoint.com/mongodb/, by = tutorials point
}}  
Document{{
   _id = 7452239959673a32646baab8, 
   title = RethinkDB, 
   id = 2, 
   description = database, 
   likes = 200, 
   url = https://tutorialspoint.com/rethinkdb/, by = tutorials point
}}

更新文档

要更新集合中的文档,可以使用com.mongodb.client.MongoCollection类的updateOne()方法。

以下是选择第一个文档的程序:

import com.mongodb.client.FindIterable; 
import com.mongodb.client.MongoCollection; 
import com.mongodb.client.MongoDatabase; 
import com.mongodb.client.model.Filters; 
import com.mongodb.client.model.Updates; 

import java.util.Iterator; 
import org.bson.Document;  
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class UpdatingDocuments { 
   
   public static void main( String args[] ) {  
      
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 
     
      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb"); 

      // Retrieving a collection 
      MongoCollection<Document> collection = database.getCollection("sampleCollection");
      System.out.println("Collection myCollection selected successfully"); 

      collection.updateOne(Filters.eq("id", 1), Updates.set("likes", 150));       
      System.out.println("Document update successfully...");  
      
      // Retrieving the documents after updation 
      // Getting the iterable object
      FindIterable<Document> iterDoc = collection.find(); 
      int i = 1; 

      // Getting the iterator 
      Iterator it = iterDoc.iterator(); 

      while (it.hasNext()) {  
         System.out.println(it.next());  
         i++; 
      }     
   }  
}

编译后,上述程序将为您提供以下结果:

Document update successfully... 
Document {{
   _id = 5967745223993a32646baab8, 
   title = MongoDB, 
   id = 1, 
   description = database, 
   likes = 150, 
   url = https://tutorialspoint.com/mongodb/, by = tutorials point
}}

删除文档

要从集合中删除文档,您需要使用com.mongodb.client.MongoCollection类的deleteOne()方法。

以下是删除文档的程序:

import com.mongodb.client.FindIterable; 
import com.mongodb.client.MongoCollection; 
import com.mongodb.client.MongoDatabase; 
import com.mongodb.client.model.Filters;  

import java.util.Iterator; 
import org.bson.Document; 
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class DeletingDocuments { 
   
   public static void main( String args[] ) {  
   
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 );
      
      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb"); 

      // Retrieving a collection
      MongoCollection<Document> collection = database.getCollection("sampleCollection");
      System.out.println("Collection sampleCollection selected successfully"); 

      // Deleting the documents 
      collection.deleteOne(Filters.eq("id", 1)); 
      System.out.println("Document deleted successfully...");  
      
      // Retrieving the documents after updation 
      // Getting the iterable object 
      FindIterable<Document> iterDoc = collection.find(); 
      int i = 1; 

      // Getting the iterator 
      Iterator it = iterDoc.iterator(); 

      while (it.hasNext()) {  
         System.out.println("Inserted Document: "+i);  
         System.out.println(it.next());  
         i++; 
      }       
   } 
}

编译后,上述程序将为您提供以下结果:

Connected to the database successfully 
Collection sampleCollection selected successfully 
Document deleted successfully...

删除集合

要从数据库中删除集合,您需要使用com.mongodb.client.MongoCollection类的drop()方法。

以下是删除集合的程序:

import com.mongodb.client.MongoCollection; 
import com.mongodb.client.MongoDatabase;  

import org.bson.Document;  
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class DropingCollection { 
   
   public static void main( String args[] ) {  

      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 

      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 
      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb");  
      
      // Creating a collection 
      System.out.println("Collections created successfully"); 

      // Retieving a collection
      MongoCollection<Document> collection = database.getCollection("sampleCollection");

      // Dropping a Collection 
      collection.drop(); 
      System.out.println("Collection dropped successfully");
   } 
}

编译后,上述程序将为您提供以下结果:

Connected to the database successfully 
Collection sampleCollection selected successfully 
Collection dropped successfully

列出所有集合

要列出数据库中的所有集合,您需要使用com.mongodb.client.MongoDatabase类的listCollectionNames()方法。

以下是列出数据库所有集合的程序:

import com.mongodb.client.MongoDatabase; 
import com.mongodb.MongoClient; 
import com.mongodb.MongoCredential;  

public class ListOfCollection { 
   
   public static void main( String args[] ) {  
      
      // Creating a Mongo client 
      MongoClient mongo = new MongoClient( "localhost" , 27017 ); 

      // Creating Credentials 
      MongoCredential credential; 
      credential = MongoCredential.createCredential("sampleUser", "myDb", 
         "password".toCharArray()); 

      System.out.println("Connected to the database successfully");  
      
      // Accessing the database 
      MongoDatabase database = mongo.getDatabase("myDb"); 
      System.out.println("Collection created successfully"); 
      for (String name : database.listCollectionNames()) { 
         System.out.println(name); 
      } 
   }
} 

编译后,上述程序将为您提供以下结果:

Connected to the database successfully 
Collection created successfully 
myCollection 
myCollection1 
myCollection5

其余的 MongoDB 方法save()、limit()、skip()、sort()等的工作方式与后续教程中解释的相同。

MongoDB - PHP

要将 MongoDB 与 PHP 一起使用,您需要使用 MongoDB PHP 驱动程序。从网址下载 PHP 驱动程序下载驱动程序。确保下载其最新版本。现在解压缩存档并将 php_mongo.dll 放入您的 PHP 扩展目录(默认为“ext”),然后将以下行添加到您的 php.ini 文件中:

extension = php_mongo.dll

建立连接并选择数据库

要建立连接,您需要指定数据库名称,如果数据库不存在,则 MongoDB 会自动创建它。

以下是连接到数据库的代码片段:

<?php
   // connect to mongodb
   $m = new MongoClient();
	
   echo "Connection to database successfully";
   // select a database
   $db = $m->mydb;
	
   echo "Database mydb selected";
?>

程序执行后,将产生以下结果:

Connection to database successfully
Database mydb selected

创建集合

以下是创建集合的代码片段:

<?php
   // connect to mongodb
   $m = new MongoClient();
   echo "Connection to database successfully";
	
   // select a database
   $db = $m->mydb;
   echo "Database mydb selected";
   $collection = $db->createCollection("mycol");
   echo "Collection created succsessfully";
?>

程序执行后,将产生以下结果:

Connection to database successfully
Database mydb selected
Collection created succsessfully

插入文档

要将文档插入 MongoDB,可以使用insert()方法。

以下是插入文档的代码片段:

<?php
   // connect to mongodb
   $m = new MongoClient();
   echo "Connection to database successfully";
	
   // select a database
   $db = $m->mydb;
   echo "Database mydb selected";
   $collection = $db->mycol;
   echo "Collection selected succsessfully";
	
   $document = array( 
      "title" => "MongoDB", 
      "description" => "database", 
      "likes" => 100,
      "url" => "https://tutorialspoint.com/mongodb/",
      "by" => "tutorials point"
   );
	
   $collection->insert($document);
   echo "Document inserted successfully";
?>

程序执行后,将产生以下结果:

Connection to database successfully
Database mydb selected
Collection selected succsessfully
Document inserted successfully

查找所有文档

要从集合中选择所有文档,可以使用 find() 方法。

以下是选择所有文档的代码片段:

<?php
   // connect to mongodb
   $m = new MongoClient();
   echo "Connection to database successfully";
	
   // select a database
   $db = $m->mydb;
   echo "Database mydb selected";
   $collection = $db->mycol;
   echo "Collection selected succsessfully";

   $cursor = $collection->find();
   // iterate cursor to display title of documents
	
   foreach ($cursor as $document) {
      echo $document["title"] . "\n";
   }
?>

程序执行后,将产生以下结果:

Connection to database successfully
Database mydb selected
Collection selected succsessfully {
   "title": "MongoDB"
}

更新文档

要更新文档,您需要使用 update() 方法。

在以下示例中,我们将插入文档的标题更新为MongoDB 教程。以下是更新文档的代码片段:

<?php
   // connect to mongodb
   $m = new MongoClient();
   echo "Connection to database successfully";
	
   // select a database
   $db = $m->mydb;
   echo "Database mydb selected";
   $collection = $db->mycol;
   echo "Collection selected succsessfully";

   // now update the document
   $collection->update(array("title"=>"MongoDB"), 
      array('$set'=>array("title"=>"MongoDB Tutorial")));
   echo "Document updated successfully";
	
   // now display the updated document
   $cursor = $collection->find();
	
   // iterate cursor to display title of documents
   echo "Updated document";
	
   foreach ($cursor as $document) {
      echo $document["title"] . "\n";
   }
?>

程序执行后,将产生以下结果:

Connection to database successfully
Database mydb selected
Collection selected succsessfully
Document updated successfully
Updated document {
   "title": "MongoDB Tutorial"
}

删除文档

要删除文档,您需要使用 remove() 方法。

在以下示例中,我们将删除标题为MongoDB 教程的文档。以下是删除文档的代码片段:

<?php
   // connect to mongodb
   $m = new MongoClient();
   echo "Connection to database successfully";
	
   // select a database
   $db = $m->mydb;
   echo "Database mydb selected";
   $collection = $db->mycol;
   echo "Collection selected succsessfully";
   
   // now remove the document
   $collection->remove(array("title"=>"MongoDB Tutorial"),false);
   echo "Documents deleted successfully";
   
   // now display the available documents
   $cursor = $collection->find();
	
   // iterate cursor to display title of documents
   echo "Updated document";
	
   foreach ($cursor as $document) {
      echo $document["title"] . "\n";
   }
?>

程序执行后,将产生以下结果:

Connection to database successfully
Database mydb selected
Collection selected succsessfully
Documents deleted successfully

在以上示例中,第二个参数是布尔类型,用于remove()方法的justOne字段。

其余的 MongoDB 方法findOne()、save()、limit()、skip()、sort()等的工作方式与上述解释相同。

MongoDB - 关系

MongoDB 中的关系表示各种文档在逻辑上如何相互关联。关系可以通过嵌入引用方法建模。此类关系可以是 1:1、1:N、N:1 或 N:N。

让我们考虑为用户存储地址的情况。因此,一个用户可以有多个地址,这使得它成为 1:N 关系。

以下是用户文档的示例文档结构:

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "name": "Tom Hanks",
   "contact": "987654321",
   "dob": "01-01-1991"
}

以下是地址文档的示例文档结构:

{
   "_id":ObjectId("52ffc4a5d85242602e000000"),
   "building": "22 A, Indiana Apt",
   "pincode": 123456,
   "city": "Los Angeles",
   "state": "California"
} 

建模嵌入关系

在嵌入方法中,我们将地址文档嵌入到用户文档中。

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "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.users.findOne({"name":"Tom Benzamin"},{"address":1})

请注意,在上面的查询中,dbusers分别是数据库和集合。

缺点是,如果嵌入文档的大小不断增长,则会影响读写性能。

建模引用关系

这是设计规范化关系的方法。在这种方法中,用户和地址文档将分别维护,但用户文档将包含一个字段,该字段将引用地址文档的id字段。

{
   "_id":ObjectId("52ffc33cd85242f436000001"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin",
   "address_ids": [
      ObjectId("52ffc4a5d85242602e000000"),
      ObjectId("52ffc4a5d85242602e000001")
   ]
}

如上所示,用户文档包含数组字段address_ids,其中包含相应地址的ObjectId。使用这些ObjectId,我们可以查询地址文档并从中获取地址详细信息。通过这种方法,我们需要两个查询:第一个是从user文档中获取address_ids字段,第二个是从address集合中获取这些地址。

>var result = db.users.findOne({"name":"Tom Benzamin"},{"address_ids":1})
>var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})

MongoDB - 数据库引用

如MongoDB关系最后一章所述,要在MongoDB中实现规范化的数据库结构,我们使用引用关系的概念,也称为手动引用,其中我们手动将引用文档的id存储在其他文档中。但是,在文档包含来自不同集合的引用的情况下,我们可以使用MongoDB DBRefs

DBRefs与手动引用

例如,在使用DBRefs而不是手动引用的场景中,考虑一个数据库,我们将在不同的集合(address_home、address_office、address_mailing等)中存储不同类型的地址(家庭、办公室、邮寄等)。现在,当user集合的文档引用地址时,它还需要根据地址类型指定要查找哪个集合。在这种情况下,文档引用来自多个集合的文档,我们应该使用DBRefs。

使用DBRefs

DBRefs中有三个字段:

  • $ref - 此字段指定引用文档的集合

  • $id - 此字段指定引用文档的_id字段

  • $db - 这是一个可选字段,包含引用文档所在的数据库名称

考虑一个示例用户文档,其中包含DBRef字段address,如代码片段所示:

{
   "_id":ObjectId("53402597d852426020000002"),
   "address": {
   "$ref": "address_home",
   "$id": ObjectId("534009e4d852427820000002"),
   "$db": "tutorialspoint"},
   "contact": "987654321",
   "dob": "01-01-1991",
   "name": "Tom Benzamin"
}

此处的address DBRef字段指定引用地址文档位于tutorialspoint数据库的address_home集合中,并且id为534009e4d852427820000002。

以下代码动态地在由$ref参数(在本例中为address_home)指定的集合中查找id与DBRef中的$id参数指定的id相同的文档。

>var user = db.users.findOne({"name":"Tom Benzamin"})
>var dbRef = user.address
>db[dbRef.$ref].findOne({"_id":(dbRef.$id)})

以上代码返回存在于address_home集合中的以下地址文档:

{
   "_id" : ObjectId("534009e4d852427820000002"),
   "building" : "22 A, Indiana Apt",
   "pincode" : 123456,
   "city" : "Los Angeles",
   "state" : "California"
}

MongoDB - 覆盖查询

在本节中,我们将学习覆盖查询。

什么是覆盖查询?

根据MongoDB官方文档,覆盖查询是指:

  • 查询中的所有字段都是索引的一部分。
  • 查询中返回的所有字段都在同一个索引中。

由于查询中存在的所有字段都是索引的一部分,因此MongoDB使用同一个索引匹配查询条件并返回结果,而无需实际查看文档内部。由于索引存在于RAM中,因此与通过扫描文档获取数据相比,从索引中获取数据要快得多。

使用覆盖查询

要测试覆盖查询,请考虑users集合中的以下文档:

{
   "_id": ObjectId("53402597d852426020000002"),
   "contact": "987654321",
   "dob": "01-01-1991",
   "gender": "M",
   "name": "Tom Benzamin",
   "user_name": "tombenzamin"
}

我们将首先使用以下查询为users集合中的genderuser_name字段创建一个复合索引:

>db.users.ensureIndex({gender:1,user_name:1})

现在,此索引将覆盖以下查询:

>db.users.find({gender:"M"},{user_name:1,_id:0})

也就是说,对于上述查询,MongoDB不会去数据库文档中查找。相反,它会从索引数据中获取所需的数据,这非常快。

由于我们的索引不包含_id字段,因此我们已将其显式地从查询的结果集中排除,因为MongoDB默认在每个查询中都返回_id字段。因此,以下查询将不会被上面创建的索引覆盖:

>db.users.find({gender:"M"},{user_name:1})

最后,请记住,如果以下情况,索引无法覆盖查询:

  • 任何索引字段都是数组
  • 任何索引字段都是子文档

MongoDB - 分析查询

分析查询是衡量数据库和索引设计有效性的一个非常重要的方面。我们将学习常用的$explain$hint查询。

使用$explain

$explain运算符提供有关查询、查询中使用的索引和其他统计信息的信息。在分析索引的优化程度时,它非常有用。

在上一节中,我们已经使用以下查询为users集合中的genderuser_name字段创建了一个索引:

>db.users.ensureIndex({gender:1,user_name:1})

我们现在将在以下查询上使用$explain

>db.users.find({gender:"M"},{user_name:1,_id:0}).explain()

上述explain()查询返回以下分析结果:

{
   "cursor" : "BtreeCursor gender_1_user_name_1",
   "isMultiKey" : false,
   "n" : 1,
   "nscannedObjects" : 0,
   "nscanned" : 1,
   "nscannedObjectsAllPlans" : 0,
   "nscannedAllPlans" : 1,
   "scanAndOrder" : false,
   "indexOnly" : true,
   "nYields" : 0,
   "nChunkSkips" : 0,
   "millis" : 0,
   "indexBounds" : {
      "gender" : [
         [
            "M",
            "M"
         ]
      ],
      "user_name" : [
         [
            {
               "$minElement" : 1
            },
            {
               "$maxElement" : 1
            }
         ]
      ]
   }
}

我们现在将查看此结果集中的字段:

  • indexOnly的true值表示此查询已使用索引。

  • cursor字段指定使用的游标类型。BTreeCursor类型表示使用了索引,并且还给出了使用的索引的名称。BasicCursor表示在没有使用任何索引的情况下进行了完整扫描。

  • n表示返回的匹配文档的数量。

  • nscannedObjects表示扫描的文档总数。

  • nscanned表示扫描的文档或索引条目的总数。

使用$hint

$hint运算符强制查询优化器使用指定的索引来运行查询。当您想要测试使用不同索引的查询的性能时,这尤其有用。例如,以下查询指定要用于此查询的genderuser_name字段上的索引:

>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1})

要使用$explain分析上述查询:

>db.users.find({gender:"M"},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()

MongoDB - 原子操作

原子操作的模型数据

维护原子性的推荐方法是使用嵌入文档将所有相关的、经常一起更新的信息保存在单个文档中。这将确保单个文档的所有更新都是原子的。

考虑以下产品文档:

{
   "_id":1,
   "product_name": "Samsung S3",
   "category": "mobiles",
   "product_total": 5,
   "product_available": 3,
   "product_bought_by": [
      {
         "customer": "john",
         "date": "7-Jan-2014"
      },
      {
         "customer": "mark",
         "date": "8-Jan-2014"
      }
   ]
}

在此文档中,我们已将购买产品的客户的信息嵌入到product_bought_by字段中。现在,每当新客户购买产品时,我们首先会使用product_available字段检查产品是否仍然可用。如果可用,我们将减少product_available字段的值,并将新客户的嵌入文档插入product_bought_by字段。我们将使用findAndModify命令来实现此功能,因为它可以同时搜索和更新文档。

>db.products.findAndModify({ 
   query:{_id:2,product_available:{$gt:0}}, 
   update:{ 
      $inc:{product_available:-1}, 
      $push:{product_bought_by:{customer:"rob",date:"9-Jan-2014"}} 
   }    
})

我们使用嵌入文档和findAndModify查询的方法确保仅当产品可用时才会更新产品购买信息。并且整个事务都在同一个查询中,是原子的。

与此相反,考虑我们可能将产品可用性和谁购买了产品的信息分开保存的场景。在这种情况下,我们首先将使用第一个查询检查产品是否可用。然后在第二个查询中,我们将更新购买信息。但是,在执行这两个查询之间,其他用户可能已购买了产品,并且该产品不再可用。在不知道这一点的情况下,我们的第二个查询将根据第一个查询的结果更新购买信息。这将使数据库不一致,因为我们已经销售了不可用的产品。

MongoDB - 高级索引

考虑users集合的以下文档:

{
   "address": {
      "city": "Los Angeles",
      "state": "California",
      "pincode": "123"
   },
   "tags": [
      "music",
      "cricket",
      "blogs"
   ],
   "name": "Tom Benzamin"
}

以上文档包含一个address子文档和一个tags数组

索引数组字段

假设我们想根据用户的标签搜索用户文档。为此,我们将在集合中的tags数组上创建索引。

为数组创建索引会为其每个字段创建单独的索引条目。因此,在我们的例子中,当我们在tags数组上创建索引时,将为其值music、cricket和blogs创建单独的索引。

要在tags数组上创建索引,请使用以下代码:

>db.users.ensureIndex({"tags":1})

创建索引后,我们可以像这样搜索集合的tags字段:

>db.users.find({tags:"cricket"})

要验证是否使用了正确的索引,请使用以下explain命令:

>db.users.find({tags:"cricket"}).explain()

上述命令导致"cursor" : "BtreeCursor tags_1",这确认使用了正确的索引。

索引子文档字段

假设我们想根据city、state和pincode字段搜索文档。由于所有这些字段都是address子文档字段的一部分,因此我们将在子文档的所有字段上创建索引。

要为子文档的所有三个字段创建索引,请使用以下代码:

>db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})

创建索引后,我们可以利用此索引搜索任何子文档字段,如下所示:

>db.users.find({"address.city":"Los Angeles"})   

请记住,查询表达式必须遵循指定的索引顺序。因此,上面创建的索引将支持以下查询:

>db.users.find({"address.city":"Los Angeles","address.state":"California"}) 

它还将支持以下查询:

>db.users.find({"address.city":"LosAngeles","address.state":"California",
   "address.pincode":"123"})

MongoDB - 索引限制

在本节中,我们将学习索引限制及其其他组件。

额外开销

每个索引都会占用一些空间,并且在每次插入、更新和删除时都会造成开销。因此,如果您很少将集合用于读取操作,则没有必要使用索引。

RAM使用情况

由于索引存储在RAM中,因此您应该确保索引的总大小不超过RAM限制。如果总大小超过RAM大小,它将开始删除一些索引,从而导致性能下降。

查询限制

索引不能用于使用以下内容的查询:

  • 正则表达式或否定运算符,如$nin、$not等。
  • 算术运算符,如$mod等。
  • $where子句

因此,始终建议检查查询的索引使用情况。

索引键限制

从2.6版本开始,如果现有索引字段的值超过索引键限制,MongoDB将不会创建索引。

插入超过索引键限制的文档

如果文档的索引字段值超过索引键限制,MongoDB将不会将任何文档插入到索引集合中。mongorestore和mongoimport实用程序也是如此。

最大范围

  • 一个集合最多只能有64个索引。
  • 索引名称的长度不能超过125个字符。
  • 复合索引最多可以有31个索引字段。

MongoDB - ObjectId

在之前的所有章节中,我们一直在使用MongoDB ObjectId。在本节中,我们将了解ObjectId的结构。

ObjectId是一个12字节的BSON类型,具有以下结构:

  • 前4个字节表示自Unix纪元以来的秒数
  • 接下来的3个字节是机器标识符
  • 接下来的2个字节包含进程ID
  • 最后3个字节是随机计数器值

MongoDB 使用 ObjectId 作为每个文档的_id 字段的默认值,该值在创建任何文档时生成。ObjectId 的复杂组合使得所有 _id 字段都唯一。

创建新的 ObjectId

要生成一个新的 ObjectId,使用以下代码:

>newObjectId = ObjectId()

以上语句返回以下唯一生成的 ID:

ObjectId("5349b4ddd2781d08c09890f3")

除了由 MongoDB 生成 ObjectId 之外,您还可以提供一个 12 字节的 ID:

>myObjectId = ObjectId("5349b4ddd2781d08c09890f4")

创建文档的时间戳

由于默认情况下 _id ObjectId 存储 4 字节的时间戳,因此在大多数情况下,您不需要存储任何文档的创建时间。您可以使用 getTimestamp 方法获取文档的创建时间:

>ObjectId("5349b4ddd2781d08c09890f4").getTimestamp()

这将以 ISO 日期格式返回此文档的创建时间:

ISODate("2014-04-12T21:49:17Z")

将 ObjectId 转换为字符串

在某些情况下,您可能需要 ObjectId 的字符串格式的值。要将 ObjectId 转换为字符串,请使用以下代码:

>newObjectId.str

以上代码将返回 Guid 的字符串格式:

5349b4ddd2781d08c09890f3

MongoDB - Map Reduce

根据 MongoDB 文档,Map-Reduce 是一种数据处理范式,用于将大量数据压缩成有用的聚合结果。MongoDB 使用mapReduce 命令进行 Map-Reduce 操作。MapReduce 通常用于处理大型数据集。

MapReduce 命令

以下是基本 mapReduce 命令的语法:

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

map-reduce 函数首先查询集合,然后将结果文档映射到发出键值对,然后根据具有多个值的键进行归约。

在以上语法中:

  • map 是一个 JavaScript 函数,它将值与键映射并发出键值对

  • reduce 是一个 JavaScript 函数,它减少或组合所有具有相同键的文档

  • out 指定 map-reduce 查询结果的位置

  • query 指定用于选择文档的可选选择条件

  • sort 指定可选的排序条件

  • limit 指定要返回的可选最大文档数

使用 MapReduce

考虑以下存储用户帖子的文档结构。该文档存储用户的 user_name 和帖子的状态。

{
   "post_text": "tutorialspoint is an awesome website for tutorials",
   "user_name": "mark",
   "status":"active"
}

现在,我们将对我们的posts 集合使用 mapReduce 函数来选择所有活跃的帖子,根据 user_name 对其进行分组,然后使用以下代码计算每个用户的帖子数量:

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

以上 MapReduce 查询输出以下结果:

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

结果显示共有 4 个文档匹配查询 (status:"active"),map 函数发出了 4 个带有键值对的文档,最后 reduce 函数将具有相同键的映射文档分组为 2 个。

要查看此 mapReduce 查询的结果,请使用 find 运算符:

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

以上查询给出以下结果,表明tommark 这两个用户都各有 2 篇帖子处于活跃状态:

{ "_id" : "tom", "value" : 2 }
{ "_id" : "mark", "value" : 2 }

以类似的方式,MapReduce 查询可用于构建大型复杂的聚合查询。自定义 JavaScript 函数的使用使 MapReduce 的使用非常灵活和强大。

MongoDB - 文本搜索

从 2.4 版开始,MongoDB 开始支持文本索引以在字符串内容内进行搜索。文本搜索 使用词干提取技术通过删除诸如a、an、the 等词干停止词来查找字符串字段中的指定单词。目前,MongoDB 支持大约 15 种语言。

启用文本搜索

最初,文本搜索是一个实验性功能,但从 2.6 版开始,默认情况下启用该配置。但是,如果您使用的是 MongoDB 的早期版本,则必须使用以下代码启用文本搜索:

>db.adminCommand({setParameter:true,textSearchEnabled:true})

创建文本索引

考虑posts 集合下包含帖子文本及其标签的以下文档:

{
   "post_text": "enjoy the mongodb articles on tutorialspoint",
   "tags": [
      "mongodb",
      "tutorialspoint"
   ]
}

我们将对 post_text 字段创建文本索引,以便我们可以在帖子文本中进行搜索:

>db.posts.ensureIndex({post_text:"text"})

使用文本索引

现在我们已在 post_text 字段上创建了文本索引,我们将搜索所有在其文本中包含单词tutorialspoint 的帖子。

>db.posts.find({$text:{$search:"tutorialspoint"}})

以上命令返回以下在帖子文本中包含单词tutorialspoint 的结果文档:

{ 
   "_id" : ObjectId("53493d14d852429c10000002"), 
   "post_text" : "enjoy the mongodb articles on tutorialspoint", 
   "tags" : [ "mongodb", "tutorialspoint" ]
}
{
   "_id" : ObjectId("53493d1fd852429c10000003"), 
   "post_text" : "writing tutorials on mongodb",
   "tags" : [ "mongodb", "tutorial" ] 
}

如果您使用的是旧版本的 MongoDB,则必须使用以下命令:

>db.posts.runCommand("text",{search:" tutorialspoint "})

与普通搜索相比,使用文本搜索极大地提高了搜索效率。

删除文本索引

要删除现有的文本索引,首先使用以下查询查找索引的名称:

>db.posts.getIndexes()

从以上查询获取索引名称后,运行以下命令。此处,post_text_text 是索引的名称。

>db.posts.dropIndex("post_text_text")

MongoDB - 正则表达式

正则表达式在所有语言中都经常用于在任何字符串中搜索模式或单词。MongoDB 还提供正则表达式的功能,使用$regex 运算符进行字符串模式匹配。MongoDB 使用 PCRE(Perl 兼容正则表达式)作为正则表达式语言。

与文本搜索不同,我们不需要进行任何配置或命令来使用正则表达式。

考虑posts 集合下包含帖子文本及其标签的以下文档结构:

{
   "post_text": "enjoy the mongodb articles on tutorialspoint",
   "tags": [
      "mongodb",
      "tutorialspoint"
   ]
}

使用正则表达式

以下正则表达式查询搜索所有包含字符串tutorialspoint 的帖子:

>db.posts.find({post_text:{$regex:"tutorialspoint"}})

相同的查询也可以写成:

>db.posts.find({post_text:/tutorialspoint/})

使用不区分大小写的正则表达式

要使搜索不区分大小写,我们使用$options 参数,其值为$i。以下命令将查找包含单词tutorialspoint 的字符串,无论大小写如何:

>db.posts.find({post_text:{$regex:"tutorialspoint",$options:"$i"}})

此查询返回的结果之一是以下文档,其中包含不同大小写的单词tutorialspoint

{
   "_id" : ObjectId("53493d37d852429c10000004"),
   "post_text" : "hey! this is my post on TutorialsPoint", 
   "tags" : [ "tutorialspoint" ]
} 
 

对数组元素使用正则表达式

我们也可以对数组字段使用正则表达式的概念。当我们实现标签的功能时,这尤其重要。因此,如果您想搜索所有以单词 tutorial 开头的标签(tutorial 或 tutorials 或 tutorialpoint 或 tutorialphp)的帖子,您可以使用以下代码:

>db.posts.find({tags:{$regex:"tutorial"}})

优化正则表达式查询

  • 如果文档字段已索引,则查询将使用索引值来匹配正则表达式。这使得搜索速度比正则表达式扫描整个集合快得多。

  • 如果正则表达式是前缀表达式,则所有匹配项都必须以某些字符串字符开头。例如,如果正则表达式为^tut,则查询必须仅搜索以tut 开头的字符串。

使用 RockMongo

RockMongo 是一种 MongoDB 管理工具,您可以使用它来管理服务器、数据库、集合、文档、索引等等。它提供了一种非常用户友好的方式来读取、写入和创建文档。它类似于 PHP 和 MySQL 的 PHPMyAdmin 工具。

下载 RockMongo

您可以从此处下载最新版本的 RockMongo:https://github.com/iwind/rockmongo

安装 RockMongo

下载完成后,您可以将软件包解压缩到服务器根文件夹中,并将解压缩的文件夹重命名为rockmongo。打开任何 Web 浏览器并访问文件夹 rockmongo 中的index.php 页面。分别输入 admin/admin 作为用户名/密码。

使用 RockMongo

我们现在将了解您可以使用 RockMongo 执行的一些基本操作。

创建新数据库

要创建新数据库,请单击数据库选项卡。单击创建新数据库。在下一个屏幕上,提供新数据库的名称,然后单击创建。您将看到左侧面板中添加了一个新数据库。

创建新集合

要在数据库中创建新集合,请从左侧面板中单击该数据库。单击顶部的新集合链接。提供所需的集合名称。不必担心 Is Capped、Size 和 Max 的其他字段。单击创建。将创建一个新集合,您将能够在左侧面板中看到它。

创建新文档

要创建新文档,请单击要在其下添加文档的集合。当您单击集合时,您将能够看到列出在该集合中的所有文档。要创建新文档,请单击顶部的插入链接。您可以以 JSON 或数组格式输入文档数据,然后单击保存

导出/导入数据

要导入/导出任何集合的数据,请单击该集合,然后单击顶部面板上的导出/导入链接。按照后续说明将数据导出为 zip 格式,然后导入相同的 zip 文件以导入数据。

MongoDB - GridFS

GridFS 是 MongoDB 用于存储和检索大型文件(如图像、音频文件、视频文件等)的规范。它是一种用于存储文件的某种文件系统,但其数据存储在 MongoDB 集合中。GridFS 能够存储文件,即使文件大小超过其 16MB 的文档大小限制。

GridFS 将文件分成块,并将每个数据块存储在单独的文档中,每个文档的最大大小为 255k。

GridFS 默认使用两个集合fs.filesfs.chunks 来存储文件的元数据和块。每个块都由其唯一的 _id ObjectId 字段标识。fs.files 充当父文档。fs.chunks 文档中的files_id 字段将块链接到其父级。

以下是 fs.files 集合的示例文档:

{
   "filename": "test.txt",
   "chunkSize": NumberInt(261120),
   "uploadDate": ISODate("2014-04-13T11:32:33.557Z"),
   "md5": "7b762939321e146569b07f72c62cca4f",
   "length": NumberInt(646)
}

该文档指定文件名、块大小、上传日期和长度。

以下是 fs.chunks 文档的示例文档:

{
   "files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
   "n": NumberInt(0),
   "data": "Mongo Binary Data"
}

将文件添加到 GridFS

现在,我们将使用 GridFS 使用put 命令存储一个 mp3 文件。为此,我们将使用 MongoDB 安装文件夹的 bin 文件夹中提供的mongofiles.exe 实用程序。

打开命令提示符,导航到 MongoDB 安装文件夹的 bin 文件夹中的 mongofiles.exe,然后键入以下代码:

>mongofiles.exe -d gridfs put song.mp3

此处,gridfs 是将存储文件的数据库的名称。如果数据库不存在,MongoDB 将自动动态创建一个新文档。Song.mp3 是上传的文件的名称。要查看数据库中的文件文档,您可以使用 find 查询:

>db.fs.files.find()

以上命令返回以下文档:

{
   _id: ObjectId('534a811bf8b4aa4d33fdf94d'), 
   filename: "song.mp3", 
   chunkSize: 261120, 
   uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",
   length: 10401959 
}

我们还可以使用以下代码查看与存储的文件相关的 fs.chunks 集合中存在的所有块,使用上一个查询中返回的文档 ID:

>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})

在我的情况下,查询返回了 40 个文档,这意味着整个 mp3 文档被分成了 40 个数据块。

MongoDB - 有界集合

有限集合(Capped Collections)是固定大小的循环集合,按照插入顺序存储数据,从而支持高效的创建、读取和删除操作。循环是指,当分配给集合的固定大小用尽时,它将开始删除集合中最旧的文档,而无需任何显式命令。

有限集合限制对文档的更新,如果更新导致文档大小增加。由于有限集合按磁盘存储顺序存储文档,因此可以确保文档大小不会增加磁盘上分配的大小。有限集合最适合存储日志信息、缓存数据或任何其他大量数据。

创建有限集合

要创建有限集合,我们使用正常的 createCollection 命令,但将 capped 选项设置为 true,并以字节为单位指定集合的最大大小。

>db.createCollection("cappedLogCollection",{capped:true,size:10000})

除了集合大小外,我们还可以使用 max 参数限制集合中的文档数量 -

>db.createCollection("cappedLogCollection",{capped:true,size:10000,max:1000})

如果要检查集合是否为有限集合,请使用以下 isCapped 命令 -

>db.cappedLogCollection.isCapped()

如果您有一个现有的集合,计划将其转换为有限集合,您可以使用以下代码 -

>db.runCommand({"convertToCapped":"posts",size:10000})

此代码会将我们现有的集合 posts 转换为有限集合。

查询有限集合

默认情况下,对有限集合的 find 查询将按插入顺序显示结果。但是,如果希望以相反的顺序检索文档,请使用以下代码所示的 sort 命令 -

>db.cappedLogCollection.find().sort({$natural:-1})

关于有限集合,还有一些其他重要的要点值得了解 -

  • 我们无法从有限集合中删除文档。

  • 有限集合中没有默认索引,即使在 _id 字段上也没有。

  • 在插入新文档时,MongoDB 不必实际查找磁盘上容纳新文档的位置。它可以盲目地将新文档插入到集合的尾部。这使得有限集合中的插入操作非常快。

  • 类似地,在读取文档时,MongoDB 以与磁盘上相同的顺序返回文档。这使得读取操作非常快。

MongoDB - 自动递增序列

MongoDB 没有开箱即用的自动递增功能,如 SQL 数据库。默认情况下,它使用 12 字节的 ObjectId 作为 _id 字段的主键,以唯一地标识文档。但是,在某些情况下,我们可能希望 _id 字段具有除 ObjectId 之外的某些自动递增值。

由于这不是 MongoDB 的默认功能,因此我们将通过使用 MongoDB 文档建议的 counters 集合以编程方式实现此功能。

使用计数器集合

考虑以下 products 文档。我们希望 _id 字段为从 1、2、3、4 到 n 开始的 自动递增整数序列

{
  "_id":1,
  "product_name": "Apple iPhone",
  "category": "mobiles"
}

为此,创建一个 counters 集合,它将跟踪所有序列字段的最后一个序列值。

>db.createCollection("counters")

现在,我们将以下文档插入 counters 集合中,其中 productid 为其键 -

{
  "_id":"productid",
  "sequence_value": 0
}

字段 sequence_value 跟踪序列的最后一个值。

使用以下代码将此序列文档插入 counters 集合中 -

>db.counters.insert({_id:"productid",sequence_value:0})

创建 Javascript 函数

现在,我们将创建一个函数 getNextSequenceValue,它将序列名称作为输入,将序列号递增 1 并返回更新的序列号。在我们的例子中,序列名称为 productid

>function getNextSequenceValue(sequenceName){

   var sequenceDocument = db.counters.findAndModify({
      query:{_id: sequenceName },
      update: {$inc:{sequence_value:1}},
      new:true
   });
	
   return sequenceDocument.sequence_value;
}

使用 Javascript 函数

现在,我们将在创建新文档时使用 getNextSequenceValue 函数,并将返回的序列值作为文档的 _id 字段。

使用以下代码插入两个示例文档 -

>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Apple iPhone",
   "category":"mobiles"
})

>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Samsung S3",
   "category":"mobiles"
})

如您所见,我们已使用 getNextSequenceValue 函数为 _id 字段设置值。

为了验证功能,让我们使用 find 命令获取文档 -

>db.products.find()

上述查询返回了以下具有自动递增 _id 字段的文档 -

{ "_id" : 1, "product_name" : "Apple iPhone", "category" : "mobiles"}

{ "_id" : 2, "product_name" : "Samsung S3", "category" : "mobiles" }
广告