Mongodb 简明教程

MongoDB - Quick Guide

MongoDB - Overview

MongoDB 是一个跨平台文档导向数据库,它提供高性能、高可用性和简单可扩展性。MongoDB 在集合和文档的概念上工作。

Database

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

Collection

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

Document

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

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

RDBMS

MongoDB

Database

Database

Table

Collection

Tuple/Row

Document

column

Field

Table Join

Embedded Documents

Primary Key

主键(默认键 _id 由 mongodb 自身提供)

Database Server and Client

Mysqld/Oracle

mongod

mysql/sqlplus

Sample Document

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

{
   _id: ObjectId(7df78ad8902c)
   title: 'MongoDB Overview',
   description: 'MongoDB is no sql database',
   by: 'tutorials point',
   url: 'http://www.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 个字节是简单的增量 VALUE。

MongoDB - Advantages

任何关系数据库中都有一个典型的架构设计,用于显示表格数量以及这些表格之间的关系。而在 MongoDB 中,关系的概念不存在。

Advantages of MongoDB over RDBMS

  1. Schema less − MongoDB 是一个文档数据库,其中一个集合包含不同的文档。不同文档的字段数、内容和大小可能不同。

  2. 单个对象结构是明确的。

  3. No complex joins.

  4. 出色的可查询性。MongoDB 支持在文档上进行动态查询,所用的文档式查询语言与 SQL 几乎一样强大。

  5. Tuning.

  6. Ease of scale-out − MongoDB 易于扩展。

  7. 无需将应用程序对象转换为数据库对象或将其映射到数据库对象。

  8. 使用内部内存存储(窗口化的)工作集,以实现更快的访问数据。

Why Use MongoDB?

  1. Document Oriented Storage − 数据存储为 JSON 样式文档的形式。

  2. Index on any attribute

  3. Replication and high availability

  4. Auto-sharding

  5. Rich queries

  6. Fast in-place updates

  7. Professional support by MongoDB

Where to Use MongoDB?

  1. Big Data

  2. Content Management and Delivery

  3. Mobile and Social Infrastructure

  4. User Data Management

  5. Data Hub

MongoDB - Environment

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

Install MongoDB On Windows

要在 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-[版本] 或 mongodb-win32-x86_64-[版本]。这里 [版本] 是 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"

这将在控制台输出上显示 waiting for connections 消息,表明 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

Install MongoDB on Ubuntu

运行以下命令以导入 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 已成功安装。

Start MongoDB

sudo service mongodb start

Stop MongoDB

sudo service mongodb stop

Restart MongoDB

sudo service mongodb restart

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

mongo

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

MongoDB Help

要获取命令列表,请在 MongoDB 客户端中输入 db.help() 。这会给你一个命令列表,如下面的屏幕截图所示。

db help

MongoDB Statistics

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

db stats

MongoDB - Data Modelling

MongoDB 中的数据具有灵活的模式。同一集合中的文档。它们不必具有相同的字段或结构集,并且集合中的普通字段可能包含不同类型的数据。

Some considerations while designing Schema in MongoDB

  1. 根据用户需求设计你的架构。

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

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

  4. 在写入时执行连接,不要在读取时执行。

  5. 针对最频繁的使用案例优化你的架构。

  6. 在架构中进行复杂聚合。

Example

假设一个客户需要一个数据库设计,用于他的博客/网站,并查看 RDBMS 和 MongoDB 架构设计之间的区别。该网站有以下要求。

  1. 每篇文章都有一个唯一的标题、说明和 url。

  2. 每篇文章可以有一个或多个标签。

  3. 每篇文章都有其发布者的姓名和总共的喜欢数。

  4. 每篇文章都有用户给出的评论以及他们的姓名、留言、日期时间和喜欢数。

  5. 在每篇文章上,可以有零个或多个评论。

在 RDBMS 架构中,上述要求的设计将至少有三个表。

rdbms

而在 MongoDB 架构中,设计将有一个集合类和以下结构 −

{
   _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 - Create Database

在本章中,我们将看到如何在 MongoDB 中创建一个数据库。

The use Command

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

Syntax

@[s1] 语句的基本语法如下:

use DATABASE_NAME

Example

如果您想使用名为 <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 - Drop Database

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

The dropDatabase() Method

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

Syntax

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

db.dropDatabase()

这会删除所选的数据库。如果你没有选定任何数据库,那么它会删除默认的「test」数据库。

Example

首先,通过使用命令检查可用数据库的列表, 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 - Create Collection

在本章中,我们将介绍如何使用 MongoDB 创建集合。

The createCollection() Method

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

Syntax

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

db.createCollection(name, options)

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

Parameter

Type

Description

Name

String

要创建的集合的名称

Options

Document

(可选)指定关于内存大小和索引的信息

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

Field

Type

Description

capped

Boolean

(可选)如果为 true,则启用带上限的集合。带上限的集合是一个固定大小的集合,当其达到最大大小时会自动覆盖其最旧的条目。 If you specify true, you need to specify size parameter also.

autoIndexId

Boolean

(可选)如果为 true,则会在 _id 域上自动创建索引。s 默认值为 false。

size

number

(可选)为带上限的集合指定最大大小(以字节为单位)。 If capped is true, then you need to specify this field also.

max

number

(可选)指定带上限的集合中允许的最大文档数量。

在插入文档时,MongoDB 首先会检查带上限的集合的 size 域,然后检查 max 域。

Examples

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 - Drop Collection

在本章中,我们将看到如何通过 MongoDB 删除集合。

The drop() Method

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

Syntax

drop() 命令的基本语法如下 −

db.COLLECTION_NAME.drop()

Example

首先,检查你数据库中可用的集合 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 - Datatypes

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

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

  2. Integer − 这种类型用于存储数值。整数可以是 32 位或 64 位,具体取决于您的服务器。

  3. Boolean − 此类型用于存储布尔值(真/假)。

  4. Double − 此类型用于存储浮点值。

  5. Min/ Max keys − 此类型用于对比值,查看其是否低于最低 BSON 元素或高于最高 BSON 元素。

  6. Arrays − 此类型用于将数组或列表或多个值存储在一个键中。

  7. Timestamp − ctimestamp。用于记录修改或添加文档的时间,非常方便。

  8. Object − 此数据类型用于嵌入式文档。

  9. Null − 此类型用于存储 Null 值。

  10. Symbol − 此数据类型与字符串相同,但通常保留用于使用特定符号类型的语言。

  11. *Date * − 此数据类型用于以 UNIX 时间格式存储当前日期或时间。可以通过创建 Date 对象并向其中传递日期、月份、年份来自定义日期时间。

  12. Object ID − 此数据类型用于存储文档的 ID。

  13. Binary data − 此数据类型用于存储二进制数据。

  14. Code − 此数据类型用于将 JavaScript 代码存储到文档中。

  15. Regular expression − 此数据类型用于存储正则表达式。

MongoDB - Insert Document

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

The insert() Method

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

Syntax

insert() 命令的基本语法如下:

>db.COLLECTION_NAME.insert(document)

Example

>db.mycol.insert({
   _id: ObjectId(7df78ad8902c),
   title: 'MongoDB Overview',
   description: 'MongoDB is no sql database',
   by: 'tutorials point',
   url: 'http://www.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() 命令中传递文档的数组。

Example

>db.post.insert([
   {
      title: 'MongoDB Overview',
      description: 'MongoDB is no sql database',
      by: 'tutorials point',
      url: 'http://www.tutorialspoint.com',
      tags: ['mongodb', 'database', 'NoSQL'],
      likes: 100
   },

   {
      title: 'NoSQL Database',
      description: "NoSQL database doesn't have tables",
      by: 'tutorials point',
      url: 'http://www.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,它将替换包含 _id 的文档的所有数据,如 save() 方法中所指定的那样。

MongoDB - Query Document

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

The find() Method

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

Syntax

find() 方法的基本语法如下 −

>db.COLLECTION_NAME.find()

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

The pretty() Method

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

Syntax

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

Example

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

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

RDBMS Where Clause Equivalents in MongoDB

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

Operation

Syntax

Example

RDBMS Equivalent

Equality

{<key>:<value>}

db.mycol.find({"by":"tutorials point"}).pretty()

其中 by = 'tutorials point'

Less Than

{<key>:{$lt:<value>}}

db.mycol.find({"likes":{$lt:50}}).pretty()

where likes < 50

Less Than Equals

{<key>:{$lte:<value>}}

db.mycol.find({"likes":{$lte:50}}).pretty()

where likes ⇐ 50

Greater Than

{<key>:{$gt:<value>}}

db.mycol.find({"likes":{$gt:50}}).pretty()

where likes > 50

Greater Than Equals

{<key>:{$gte:<value>}}

db.mycol.find({"likes":{$gte:50}}).pretty()

where likes >= 50

Not Equals

{<key>:{$ne:<value>}}

db.mycol.find({"likes":{$ne:50}}).pretty()

where likes != 50

AND in MongoDB

Syntax

find() 方法中,如果你通过用逗号分隔的方式传递多个键,那么 MongoDB 将其视为 AND 条件。以下是 AND 的基本语法 −

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

Example

以下示例将显示所有由“教程点”编写的教程,其标题为“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": "http://www.tutorialspoint.com",
   "tags": ["mongodb", "database", "NoSQL"],
   "likes": "100"
}

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

OR in MongoDB

Syntax

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

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

Example

以下示例将显示所有由“教程点”编写的教程,或其标题为“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": "http://www.tutorialspoint.com",
   "tags": ["mongodb", "database", "NoSQL"],
   "likes": "100"
}
>

Using AND and OR Together

Example

以下示例将显示喜欢次数大于 10,且其标题为“MongoDB 概述”或作者为“教程点”的文档。等效的 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": "http://www.tutorialspoint.com",
   "tags": ["mongodb", "database", "NoSQL"],
   "likes": "100"
}
>

MongoDB - Update Document

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

MongoDB Update() Method

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

Syntax

update() 方法的基本语法如下:

>db.COLLECTION_NAME.update(SELECTION_CRITERIA, UPDATED_DATA)

Example

考虑 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() Method

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

Syntax

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

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

Example

以下示例将用 _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 - Delete Document

在本章中,我们将了解如何通过 MongoDB 删除文档。

The remove() Method

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

  1. deletion criteria − (可选)根据该文档的删除条件将被移除。

  2. justOne − (可选)如果被设置为 true 或 1,那么只移除一个文档。

Syntax

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

>db.COLLECTION_NAME.remove(DELLETION_CRITTERIA)

Example

考虑 mycol 集合具有以下数据。

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

以下示例将移除所有标题为“MongoDB Overview” 的文档。

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

Remove Only One

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

>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)

Remove All Documents

如果您没有指定删除条件,则 MongoDB 将从集合中删除整个文档。 This is equivalent of SQL’s truncate command.

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

MongoDB - Projection

在 MongoDB 中,投影是指仅选择必要的数据,而不选择文档的全部数据。如果某个文档有 5 个字段,并且你只需要显示 3 个,那么只从中选择 3 个字段即可。

The find() Method

MongoDB 的 find() 方法(在 MongoDB Query Document 中进行了说明)接受第二个可选参数,即希望检索的字段列表。在 MongoDB 中,执行 find() 方法时,它会显示文档的所有字段。要限制此行为,需要设置一个带有值 1 或 0 的字段列表。1 用于显示字段,0 用于隐藏字段。

Syntax

find() 方法与投影结合起来的基本语法如下:

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

Example

考虑集合 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 - Limit Records

在本节中,我们将了解如何使用 MongoDB 限制记录。

The Limit() Method

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

Syntax

limit() 方法的基本语法如下 −

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

Example

考虑集合 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() Method

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

Syntax

skip() 方法的基本语法如下 −

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

Example

以下示例将仅显示第二个文档。

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

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

MongoDB - Sort Records

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

The sort() Method

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

Syntax

sort() 方法的基本语法如下 -

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

Example

考虑集合 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 - Indexing

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

索引是一种特殊数据结构,使用易于遍历的形式存储一小部分数据集。索引按索引中指定的字段值对特定字段的值或多组字段的值进行排序并存储这些值。

The createIndex() Method

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

Syntax

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

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

此处 key 是您希望创建索引的字段的名称,1 表示升序。要以降序创建索引,您需要使用 -1。

Example

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

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

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

此方法还接受选项列表(这些选项是可选的)。以下是列表 −

Parameter

Type

Description

background

Boolean

在后台生成索引,以便生成索引不会阻止其他数据库活动。指定 true 以在后台生成。默认值是 false

unique

Boolean

创建唯一索引,以便集合不会接受插入包含索引键或在索引中匹配现有值的文档。指定 true 以创建唯一索引。默认值是 false

name

string

索引的名称。如果未指定,MongoDB 会通过连接已编制索引的字段的名称和排序顺序来生成索引名称。

sparse

Boolean

如果为 true,索引仅引用具有指定字段的文档。这些索引使用的空间更少,但在某些情况下(尤其是排序)的行为不同。默认值是 false

expireAfterSeconds

integer

指定一个值(以秒为单位)作为 TTL,以控制 MongoDB 在此集合中保留文档的时长。

weights

document

权重是一个介于 1 至 99,999 之间的数字,表示相对于其他已编制索引的字段而言,该字段在评分方面的显著性。

default_language

string

对于文本索引,确定禁用词列表和词干分析器和标记化器规则的语言。默认值是 English

language_override

string

对于文本索引,指定文档中包含的字段的名称,该名称包含用于覆盖默认语言的语言。默认值是 language。

The dropIndex() method

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

Syntax

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

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

此处,“key”是希望删除现有索引的文件名称。除了索引规范文档(以上语法)之外,还可以直接指定索引名称,如下所示:

dropIndex("name_of_the_index")

Example

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

The dropIndexes() method

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

Syntax

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

>db.COLLECTION_NAME.dropIndexes()

Example

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

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

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

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

The getIndexes() method

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

Syntax

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

db.COLLECTION_NAME.getIndexes()

Example

假设我们在命名的 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 - Aggregation

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

The aggregate() Method

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

Syntax

aggregate() 方法的基本语法如下 -

>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

Example

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

{
   _id: ObjectId(7df78ad8902c)
   title: 'MongoDB Overview',
   description: 'MongoDB is no sql database',
   by_user: 'tutorials point',
   url: 'http://www.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: 'http://www.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 的前一个值都会递增。以下是可用聚合表达式的列表。

Expression

Description

Example

$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"}}}])

Pipeline Concept

在 UNIX 命令中,shell 管道表示在一些输入上执行操作并使用输出作为下一个命令的输入,依此类推的可能性。MongoDB 在聚合框架中也支持相同概念。有一组可能的阶段,每个阶段都将一组文档作为输入并生成一组结果文档(或管道末尾的最终结果 JSON 文档)。它随后依次可用于下一个阶段,依此类推。

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

  1. $project − 用于从集合中选择一些特定字段。

  2. $match − 这是一个筛选操作,因此这会减少作为下一个阶段输入提供的文档量。

  3. $group − 执行如上所述的实际聚合。

  4. $sort − 对文档进行排序。

  5. $skip - 通过此操作,可以跳过给定数量的文档中的文档列表。

  6. $limit - 此操作会从当前位置开始,通过给定数量限制要查看的文档数量。

  7. $unwind - 此操作用于解开使用数组的文档。当使用数组时,数据会被预先连接,而此操作将取消此连接,以便让数据重新变为独立的文档。因此,通过此阶段,我们可以增加下一阶段的文档数量。

MongoDB - Replication

复制是指在多个服务器之间同步数据的过程。复制提供冗余并且使用多个数据库服务器中的多份数据副本提高数据可用性。复制可以防止因单个服务器丢失而导致数据库丢失数据。复制还允许从硬件故障和服务中断中恢复。通过额外的数据副本,您可以分配一份副本到灾难恢复、报告或备份中。

Why Replication?

  1. 保证数据安全

  2. 高可用性(24*7)的数据

  3. Disaster recovery

  4. 无需停机进行维护(如备份、索引重建、压缩)

  5. 读取扩展(可用于读取的额外副本)

  6. 副本集对应用程序是透明的

How Replication Works in MongoDB

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

  1. 副本集是一组由两个或更多个节点(通常最低需要 3 个节点)组成。

  2. 在副本集中,一个节点为主节点,其余节点为辅助节点。

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

  4. 在自动故障转移或维护时,选举会建立用于主节点,并且会选出一个新的主节点。

  5. 故障节点恢复后,它会重新加入副本集,并作为辅助节点运行。

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

replication

Replica Set Features

  1. 由 N 个节点组成的一个集群

  2. 任何一个节点都可以为主节点

  3. 所有写入操作都转到主节点

  4. Automatic failover

  5. Automatic recovery

  6. Consensus election of primary

Set Up a Replica Set

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

  1. 关闭已运行的 MongoDB 服务器。

.

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

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

Example

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

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

  3. 在 Mongo 客户端中,发出命令 rs.initiate() 以启动新的副本集。

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

Add Members to Replica Set

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

Syntax

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

>rs.add(HOST_NAME:PORT)

Example

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

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

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

MongoDB - Sharding

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

Why Sharding?

  1. 在复制中,所有写入都进入主节点

  2. 对延迟敏感的查询仍然会进入主节点

  3. 单个副本集有 12 个节点的限制

  4. 当活动数据集较大时,内存可能不够大

  5. 本地磁盘不够大

  6. 纵向扩展成本过高

Sharding in MongoDB

下图显示了使用分片集群在 MongoDB 中分片的情况。

sharding

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

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

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

  3. Query Routers - 查询路由器基本上是 mongo 实例,与客户端应用程序和直接操作到相应分片进行接口。查询路由器处理操作并将其定向到分片,然后将结果返回给客户端。分片集群可以包含多个查询路由器以划分客户端请求负载。客户端向一个查询路由器发送请求。通常,分片集群有很多查询路由器。

MongoDB - Create Backup

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

Dump MongoDB Data

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

Syntax

mongodump 命令的基本语法如下:

>mongodump

Example

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

考虑 mycol 集合具有以下数据。

>mongodump

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

mongodump

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

Syntax

Description

Example

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

Restore data

要还原备份数据,请使用 MongoDB 的 mongorestore 指令。此指令将从备份目录还原所有数据。

Syntax

mongorestore 指令的基本语法为:

>mongorestore

以下为指令输出:

mongorestore

MongoDB - Deployment

在准备 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 管理服务 (MMS),它提供了一个仪表盘,让你可以查看整个群集的指标。

MongoDB - Java

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

Installation

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

  1. 你需要从路径 Download mongo.jar 下载 jar。确保下载其最新发行版。

  2. 你需要将 mongo.jar 包含到你的类路径中。

Connect to Database

要连接数据库,你需要指定数据库名称;如果数据库不存在,则 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 = {}
}

Create a Collection

为了创建一个集合,使用 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

Getting/Selecting a Collection

为了从数据库中获取/选择一个集合,使用 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

Insert a Document

为了向 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", "http://www.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

Retrieve All Documents

为了从集合中选择所有文档,使用 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 = http://www.tutorialspoint.com/mongodb/, by = tutorials point
}}
Document{{
   _id = 7452239959673a32646baab8,
   title = RethinkDB,
   id = 2,
   description = database,
   likes = 200,
   url = http://www.tutorialspoint.com/rethinkdb/, by = tutorials point
}}

Update Document

为了从集合中更新一个文档,使用 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 = http://www.tutorialspoint.com/mongodb/, by = tutorials point
}}

Delete a Document

为了从集合中删除一个文档,你需要使用 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...

Dropping a Collection

为了从数据库中删除一个集合,你需要使用 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

Listing All the Collections

若要列出数据库中的所有集合,则需要使用 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 驱动程序。从 url Download PHP Driver 下载该驱动程序。务必下载其最新版本。现在解压存档并将 php_mongo.dll 放在你的 PHP 扩展目录(默认情况下为 “ext”)中,并在你的 php.ini 文件中添加以下行:

extension = php_mongo.dll

Make a Connection and Select a Database

为了连接,您需要指定数据库名称,如果数据库不存在,则 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

Create a Collection

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

<?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

Insert a Document

要将文档插入到 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" => "http://www.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 All Documents

要从集合中选择所有文档,使用 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 a Document

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

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

<?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"
}

Delete a Document

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

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

<?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 - Relationships

MongoDB 中的关系表示了各种文档如何在逻辑上相互关联。关系可以通过 EmbeddedReferenced 方法建模。这种关系可以是 1:1、1:N、N:1 或 N:N。

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

以下是 user 文档的示例文档结构 −

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

以下是 address 文档的示例文档结构 −

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

Modeling Embedded Relationships

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

{
   "_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 分别是数据库和集合。

缺点是如果嵌入式文档持续增长且变得过大,则可能会影响读/写性能。

Modeling Referenced Relationships

这是设计规范化关系的方法。在此方法中,用户和地址文档将被分别维护,但用户文档将包含一个引用地址文档 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 - Database References

如 MongoDB 关系的最后一章中所述,为了在 MongoDB 中实现规范化的数据库结构,我们使用了称为 Referenced Relationships (在中也称为 Manual References )的概念,其中我们手动存储其他文档 ID 被引用的文档。但是,如果一份文档包含来自不同集合的引用,则我们可以使用 MongoDB DBRefs

DBRefs vs Manual References

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

Using DBRefs

DBRefs 中有三个字段 −

  1. $ref − 此字段指定引用的文档的集合

  2. $id − 此字段指定引用的文档的 _id 字段

  3. $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 字段指定引用的地址文档位于 address_home 数据库下的 tutorialspoint 集合中,并且 ID 为 534009e4d852427820000002。

以下代码根据 $ref 参数(在我们的示例中为 address_home )指定的集合中动态查找文档,其中 ID 为 DBRef 中的 $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 - Covered Queries

在本章中,我们将了解覆盖查询。

What is a Covered Query?

根据官方 MongoDB 文档,覆盖查询是一个查询,其中 −

  1. 查询中的所有字段都是索引的一部分。

  2. 查询中返回的所有字段都在同一索引中。

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

Using Covered Queries

要测试覆盖查询,请考虑 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})

最后,请记住,如果 −

  1. 任何一个索引字段是数组

  2. 任何一个索引字段是子文档

MongoDB - Analyzing Queries

分析查询是衡量数据库和索引设计有效性的一个非常重要的方面。我们来了解一下经常使用的 $explain$hint 查询。

Using $explain

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

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

>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
            }
         ]
      ]
   }
}

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

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

  2. cursor 字段用于指定游标的类型。BTreeCursor 类型表示使用了索引,并给出了所用索引的名称。BasicCursor 表示在不使用任何索引的情况下进行了全扫描。

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

  4. nscannedObjects 表示扫描的文档总数。

  5. nscanned 表示扫描的文档或索引项总数。

Using $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 - Atomic Operations

Model Data for Atomic Operations

推荐的原子性维护方法是将所有相关信息保存在使用 embedded documents 的单文档中,该文档经常一起更新。这将确保针对单文档的所有更新都是原子的。

考虑以下产品文档——

{
   "_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 - Advanced Indexing

考虑以下 users 集合的文档——

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

上述文档包含一个 address sub-document 和一个 tags array

Indexing Array Fields

假设我们希望根据用户的标签搜索用户文档。为此,我们将在集合中的 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" 证实使用了正确的索引。

Indexing Sub-Document Fields

假设我们希望根据 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 - Indexing Limitations

在本章中,我们将了解索引限制及其其他组件。

Extra Overhead

每个索引都会占用一些空间,并导致每个插入、更新和删除操作产生开销。因此,如果你很少将你的集合用于读取操作,那么不使用索引是有道理的。

RAM Usage

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

Query Limitations

索引无法用于使用以下内容的查询中 −

  1. 诸如 $nin、$not 等正则表达式或否定操作符

  2. 诸如 $mod 等算术操作符

  3. $where clause

因此,建议始终检查查询的索引用法。

Index Key Limits

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

Inserting Documents Exceeding Index Key Limit

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

Maximum Ranges

  1. 一个集合不能有多于 64 个索引。

  2. 索引名称的长度不能超过 125 个字符。

  3. 复合索引最多可以索引 31 个字段。

MongoDB - ObjectId

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

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

  1. 前面 4 字节表示自 Unix 纪元以来的秒数

  2. 接下来的 3 个字节是机器标识符

  3. 接下来的 2 个字节包含 process id

  4. 最后 3 个字节是一个随机计数器值

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

Creating New ObjectId

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

>newObjectId = ObjectId()

上面的语句返回了以下唯一生成的 id: −

ObjectId("5349b4ddd2781d08c09890f3")

除了让 MongoDB 生成 ObjectId 外,您还可以提供一个 12 字节的 id: −

>myObjectId = ObjectId("5349b4ddd2781d08c09890f4")

Creating Timestamp of a Document

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

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

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

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

Converting ObjectId to String

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

>newObjectId.str

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

5349b4ddd2781d08c09890f3

MongoDB - Map Reduce

根据 MongoDB 文档, Map-reduce 是一个用于将大量数据浓缩为有用的聚合结果的数据处理范例。MongoDB 使用 mapReduce 命令用于 map-reduce 操作。MapReduce 通常用于处理大型数据集。

MapReduce Command

以下是基本 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 函数首先查询集合,然后映射结果文档以发出键值对,这些键值对接着基于具有多个值的键进行化简。

在以上语法中 −

  1. map 是使用键映射一个值并发出键值对的 Javascript 函数

  2. reduce 是减少或分组所有具有相同键的文档的 Javascript 函数

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

  4. query 指定用于选择文档的可选选择标准

  5. sort 指定可选的排序标准

  6. limit 指定要返回的文档的最大可选数量

Using 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"),映射函数发出了 4 份文档,带有键值对,最后化简函数将具有相同键的已映射文档分组为 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 都在活动状态下有两篇帖子 −

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

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

从 2.4 版本开始,MongoDB 开始支持文本索引以搜索字符串内容。 Text Search 使用词干提取技巧在字符串字段中查找指定词语,方法是省略诸如 a, an, the, 这样的词干停用词。目前,MongoDB 支持约 15 种语言。

最初,文本搜索是一项实验性功能,但从 2.6 版本开始,该配置默认启用。但是,如果你使用的是以前版本的 MongoDB,则必须通过以下代码启用文本搜索——

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

Creating Text Index

考虑以下 posts 集合中的文档,它包含文章文本及其标签——

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

我们将在 post_text 字段上创建一个文本索引以便我们可以在文章的文本中进行搜索——

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

Using Text Index

现在我们在 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 "})

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

Deleting Text Index

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

>db.posts.getIndexes()

在上面的查询中获取索引的名称后,运行以下命令。在这里, post_text_text 是索引的名称。

>db.posts.dropIndex("post_text_text")

MongoDB - Regular Expression

正则表达式经常用于各种语言中,以在任意字符串中搜索某个模式或单词。MongoDB 还提供正则表达式功能,用于使用 $regex 运算符进行字符串模式匹配。MongoDB 将 PCRE(Perl 兼容正则表达式)用作正则表达式语言。

不同于文本搜索,我们无需执行任何配置或命令即可使用正则表达式。

考虑 posts 集合中以下文档结构,它包含文章文本及其标签——

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

Using regex Expression

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

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

同样的查询还可以这样编写:

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

Using regex Expression with Case Insensitive

要使搜索不区分大小写,我们可以使用值 $i$options 参数。以下命令无论大小写都会查找包含单词 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" ]
}

Using regex for Array Elements

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

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

Optimizing Regular Expression Queries

  1. 如果文档字段是 indexed ,则查询将使用已编制索引的值与正则表达式匹配。这使得搜索速度非常快,与正则表达式扫描整个集合相比。

  2. 如果正则表达式是 prefix expression ,则所有匹配项都应以某个字符串字符开头。例如,如果正则表达式是 ^tut ,则查询必须仅搜索以 tut 开头的字符串。

Working with RockMongo

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

Downloading RockMongo

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

Installing RockMongo

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

Working with RockMongo

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

Creating New Database

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

Creating New Collection

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

Creating New Document

要创建新文档,请单击想要在其中添加文档的集合。当您单击某个集合时,您将可以看到该集合内列出的所有文档。要创建新文档,请单击顶部的 Insert 链接。您可以采用 JSON 或数组格式输入文档数据,然后单击 Save

Export/Import Data

要导入/导出任意集合的数据,请单击该集合,然后单击顶部面板中的 Export/Import 链接。按照后续说明,以 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"
}

Adding Files to GridFS

现在,我们将使用 put 命令通过GridFS存储一个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
}

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

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

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

MongoDB - Capped Collections

Capped collections 是固定大小的循环集合,它遵循插入顺序来支持高性能创建、读取和删除操作。循环是指当分配给集合的固定大小用尽时,它将开始删除集合中最旧的文档,而无需提供任何显式命令。

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

Creating Capped Collection

要创建固定大小集合,我们使用常规 createCollection 命令,但使用 capped 选项为 true ,并指定集合的最大大小(以字节为单位)。

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

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

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

如果你想检查一个集合是否固定大小, hãy使用以下 isCapped 命令:

>db.cappedLogCollection.isCapped()

如果有一个你正在计划转换为固定大小的现有集合,你可以使用以下代码执行:

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

此代码会将我们现有的集合 posts 转换为固定大小集合。

Querying Capped Collection

默认情况下,对固定大小集合执行的 find 查询将按插入顺序显示结果。但是,如果你希望反向检索文档,请使用 sort 命令,如下代码所示:

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

关于固定大小集合还有一些其他值得了解的重要事项:

  1. 我们无法从固定大小集合中删除文档。

  2. 固定大小集合中不存在默认索引,即使在 _id 字段上也不存在。

  3. 在插入新文档时,MongoDB 实际上不必在磁盘上寻找放置新文档的位置。它可以盲目地将新文档插入到集合的尾部。这让固定大小集合中的插入操作变得非常快。

  4. 类似地,当读取文档时,MongoDB 会按照磁盘上的顺序返回文档。这使得读取操作非常快速。

MongoDB - Auto-Increment Sequence

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

由于这不是 MongoDB 中的默认功能,我们将按照 MongoDB 文档中的建议使用 counters 集合编程来实现此功能。

Using Counter Collection

考虑一下以下 products 文档。我们希望 _id 字段从 1、2、3、4 到 n 都自增。

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

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

>db.createCollection("counters")

现在,我们将使用 productid 作为其键将以下文档插入到计数器集合中 −

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

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

使用以下代码将此序列文档插入计数器集合中 −

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

Creating Javascript Function

现在,我们将创建一个函数 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;
}

Using the Javascript Function

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

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

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