【MongoDB入门】

MongoDB相关概念介绍

文档是 MongoDB 中的基本数据单元,类似于数据库中的行;
集合可以被看作具有动态模式的表;
一个 MongoDB 实例可以拥有多个独立的数据库
每个数据库都拥有自己的集合;
每个文档都有一个特殊的键 “_id”,其在所属的集合中是唯一的;
mongo shell 对管理 MongoDB 实例和使用 MongoDB 的查询语言操作数据提供了内置的支持。它也是一个功能齐全的 JavaScript 解释器,用户可以根据需求创建或加载自己的脚本。

文档

1、文档是 MongoDB 的核心概念:它是一组有序键值的集合。
{“key”:“value”,“key1”:“value1”,…}
2、文档中的键是字符串类型。除了少数例外的情况,可以使用任意 UTF-8 字符作为键。
3、键中不能含有 \0(空字符)。这个字符用于表示一个键的结束。. 和 $ 是特殊字符,只能在某些特定情况下使用,保留字符,如果使用不当,那么驱动程序将无法正常工作。
4、MongoDB 会区分类型和大小写。例如,下面文档是不同的:
{“hello”,“world”} 与{“Hello”:“world”}
{“age”:“5”}与{“age”:5}
5、MongoDB 中的文档不能包含重复的键

集合

集合就是一组文档。如果将文档比作关系数据库中的行,那么一个集合就相当于一张表。
集合具有动态模式的特性。这意味着一个集合中的文档可以具有任意数量的不同“形状”。即文档中的键、键的数量以及值的类型都是不同的.例如,以下两个文档可以存储在同一个集合中:
{“hello”:“world”} 与{“age”:5,“name”:“zhangsan”}
既然不同类型的文档不需要区分模式,为什么还要使用多个集合呢?
1、对于开发人员和管理员来说,将不同类型的文档保存在同一个集合中可能是一个噩梦。开发人员需要确保每个查询只返回特定模式的文档,或者确保执行查询的应用程序代码可以处理不同类型的文档。
2、获取集合列表比提取集合中的文档类型列表要快得多。如果在每个文档中都有一个"type" 字段来指明这个文档是“blog”还是“comment”,那么在单个集合中查找这 2个值要比查询 2 个相应的集合慢得多。
3、将相同类型的文档放入同一个集合中可以实现数据的局部性。相对于从既包含博客文章又包含作者数据的集合中进行查询,从一个只包含博客文章的集合中获取几篇文章可能会需要更少的磁盘查找次数。
4、在创建索引(尤其是在创建唯一索引)时,我们会采用一些文档结构。这些索引是按照每个集合来定义的。通过只将单一类型的文档放入集合中,可以更高效地对集合进行索引。
5、创建模式并且将相关类型的文档放在一起是非常合理的。

命名

集合由其名称进行标识。集合名称可以是任意 UTF-8 字符串,但有以下限制。
集合名称不能是空字符串(“”)。
集合名称不能含有 \0(空字符),因为这个字符用于表示一个集合名称的结束。
集合名称不能以 system. 开头,该前缀是为内部集合保留的。例如,system.users集合中保存着数据库的用户,system.namespaces 集合中保存着有关数据库所有集合的信息。
用户创建的集合名称中不应包含保留字符 $。许多驱动程序确实支持在集合名称中使用 $,这是因为某些由系统生成的集合会包含它,但除非你要访问的是这些集合之一,否则不应在名称中使用 $ 字符。
子集合
使用 . 字符分隔不同命名空间的子集合是一种组织集合的惯例。
GridFS 是一种用于存储大型文件的协议,它使用子集合将文件元数据与内容块分开存储

数据库

MongoDB 使用集合对文档进行分组,使用数据库对集合进行分组。推荐的做法是将单个应用程序的所有数据都存储在同一个数据库中。在同一个MongoDB 服务器上存储多个应用程序或用户的数据时,使用单独的数据库会非常有用。

与集合相同,数据库也是按照名称进行标识的。数据库名称可以是任意 UTF-8 字符串,但有以下限制。
数据库名称不能是空字符串(“”)。
数据库名称不能包含 /、\、.、"、*、<、>、:、|、?、$、单一的空格以及 \0(空字符),基本上只能使用 ASCII 字母和数字。
数据库名称区分大小写。数据库名称的长度限制为 64 字节。
此外,还有一些数据库名称是保留的。这些数据库可以被访问,但它们具有特殊的语义。具体如下。
admin: admin 数据库会在身份验证和授权时被使用。此外,某些管理操作需要访问此数据库。
local:特定于单个服务器的数据会存储在此数据库中。在副本集中,local 用于存储复制过程中所使用的数据,而 local 数据库本身不会被复制。
config: MongoDB 的分片集群会使用 config 数据库存储关于每个分片的信息。
通过将数据库名称与该库中的集合名称连接起来,可以获得一个完全限定的集合名称,称为
命名空间
。命名空间的长度限制为 120 字节,而实际使用时应该小于 100 字节。

启动MongoDB

$ mongod
注:如果没有指定参数,则 mongod 会使用默认的数据目录 /data/db/(在 Windows系统中为当前卷的 \data\db\)。如果数据目录不存在或不可写,那么服务器端将无法启动。因此在启动 MongoDB 之前,创建数据目录(如 mkdir -p/data/db/)并确保对该目录有写权限非常重要。
启动时,服务器端会打印版本和系统信息,然后开始等待连接。默认情况下,MongoDB 会监听 27017 端口上的套接字连接。如果端口不可用,那么服务器将无法启动——最常见的原因是有另一个 MongoDB 实例正在运行。

MongoDB shell介绍

MongoDB 自带 JavaScript shell,允许使用命令行与 MongoDB 实例进行交互。shell 在很多场景中非常有用,包括执行管理功能、检查正在运行的实例或仅仅是探索 MongoDB。
运行shell
$ mongo
基本数学运算测试:
> x=100
100
>x/5
20
>Math.sin(Math.PI/2)
1
> new Date(“2022/1/1”)
ISODate(“2022-01-01T05:00:00Z”)
>“hello world”.replace(“world”,“mongodb”)
hello mongodb
函数调用:
>function factorial(n){
if(n<=1) return 1;
return n*factorial(n-1);
}
>factorial(5)
120
注:你可以创建多行命令。当按下回车键时,shell 将检测 JavaScript 语句是否完整。如果语句不完整,那么 shell 将允许你在下一行继续进行编写。连续 3次按下回车键将取消未输入完成的命令并返回到 > 提示符
查看 db 当前指向哪个数据库:
>db
选择要使用的数据库
>use test
通过 db 变量来访问集合
>db.test
基本操作
>user = {“name”:“zhangsan”,age:33}
>db.users.insertOne(user)
>db.users.find().pretty()
>db.users.findOne()
>db.users.updateOne({“name”:“zhangsan”},{$set:{“address”:“shenzhen”}})
>db.users.deleteOne({“name”:“zhangsan”})
>db.users.deleteMany({“name”:“zhangsan”})

数据类型

MongoDB 中的文档可以被认为是“类似于 JSON”的形式,JSON 是一种简单的数据表示方式,易于理解、易于解析且易于记忆,由于JSON只有 null、布尔值、数字、字符串、数组和对象这几种类型。因此 JSON 的表达能力比较有限。所以MongoDB 在保留了 JSON 基本键–值对特性的基础上,增加了对许多额外数据类型的支持。其中常见类型如下:
{“x”:null}
null 类型用于表示空值或不存在的字段。
{“x”:true}
布尔类型的值可以为 true 或者 false。
{“x”:3.14}
{“x”:3}
shell 默认使用 64 位的浮点数来表示数值类型
{“x”:NumberInt(“3”)}
{“x”:NumberLong(“3”)}
整数,可以使用 NumberInt 或 NumberLong 类,它们分别表示 4 字节和 8 字节的有符号整数。
{“x”:“test”}
任何 UTF-8 字符串都可以使用字符串类型来表示。
{“x”:new Date()}
MongoDB 会将日期存储为 64 位整数,表示自 Unix 纪元(1970 年 1 月 1日)以来的毫秒数,不包含时区信息。
{“x”:/test/i}
查询时可以使用正则表达式,语法与 JavaScript 的正则表达式语法相同。
{“x”:[“a”,“x”,“c”]}
集合或者列表可以表示为数组
{“x”:{“key”:“value”}}
文档可以嵌套其他文档,此时被嵌套的文档就成了父文档的值。
二进制数据是任意字节的字符串,不能通过 shell 操作。如果要将非 UTF-8字符串存入数据库,那么使用二进制数据是唯一的方法。
{“x”:function(){//}}
MongoDB 还可以在查询和文档中存储任意的 JavaScript 代码
ObjectId: ObjectId 类采用了轻量化设计,可以很容易地在不同的机器上以全局唯一的方式生成。MongoDB 的分布式特性是它使用 ObjectId而不是其他传统做法(比如自动递增主键)的主要原因:跨多个服务器同步自动递增主键既困难又耗时。因为 MongoDB 的设计初衷就是作为一个分布式数据库,所以能够在分片环境中生成唯一的标识符非常重要。
ObjectId 占用了 12 字节的存储空间,可以用 24 个十六进制数字组成的字符串来表示:每字节存储两个数字。这会让它们看起来比实际大,很多人会因此感到紧张。但需要注意的是,尽管 ObjectId 通常被表示为一个巨大的十六进制字符串,但该字符串实际上是所存储数据的两倍长。
如果快速地连续创建多个新的 ObjectId,则会发现每次只能看到最后几个数字有变化。此外,如果在创建的过程中间隔几秒,那么 ObjectId 中间的几个数字也将发生变化。这是由 ObjectId 的创建方式导致的。ObjectId 的 12 字节是按照如下方式生成的:
ObjectId 的前 4 字节是从 Unix 纪元开始以秒为单位的时间戳。
时间戳与接下来的 5 字节(稍后会介绍)组合在一起,在秒级别的粒度上提供了唯一性。
因为时间戳在前,所以 ObjectId 将大致按照插入的顺序进行排列。这并不是一个很强的保证,但是确实在某些方面很有用,比如可以使 ObjectId 的索引效率更高。
在这 4 字节中也隐含了每个文档的创建时间。大多数驱动程序提供了从 ObjectId中提取此信息的方法。
由于 ObjectId 使用的是当前时间,因此很多人会担心需要对服务器进行时钟同步。尽管出于其他原因进行时钟同步是一个好主意,但在这里实际的时间戳对 ObjectId 并不重要,只要它总是不停增加就可以了(每秒 1次)。
ObjectId 中接下来的 5 字节是一个随机值。最后 3 字节是一个计数器,以一个随机数作为起始值,用来避免在不同机器上生成相互冲突的 ObjectId。
因此,前 9 字节保证了 ObjectId 在 1 秒内跨机器和进程的唯一性。最后 3 字节只是一个递增计数器,负责确保单个进程中 1 秒内的唯一性。这允许在 1 秒内为每个进程生成多达 2563(16 777 216)个唯一的 ObjectId。
注:
1、创建日期对象时,应该调用 newDate(),而不是 Date()
2、数组可以包含不同数据类型的元素
{“things”:[“pie”,3.14]}

使用MongoDB shell

1、连接数据库
$ mongo host:port/dbName
2、不连接任何数据库,运行时才连数据库
$ mongo --nodb
>conn = new Mongo(“host:port”)
>db=conn.getDB(“test”)
3、帮助命令
>help()
4、查看函数实现,直接调用方法名后回车即可看到
>db.test.insertOne
5、shell依次执行脚本
$ mongo script1.js script2.js
如果使用连接到非默认主机/端口上的 mongod 实例运行脚本,则需要先指定地址,之后再指定脚本:
$mongo host:port/test --quiet script1.js script2.js
脚本中使用 print 函数将内容打印到标准输出,–quiet 选项来防止打印“MongoDB shell version v4.2.0”的提示信息。还可以使用 load 函数从交互式 shell 中运行脚本:
>load(“script1.js”)
在脚本中可以访问 db 变量以及任何其他全局变量。不过,shell 的辅助函数(如use db 或 show collections)不能在文件中使用。每一个辅助函数都有对应的JavaScript 等价函数,如下表所示。
shell辅助函数对应的JavaScript函数
示例:
//testConnect.js
var conn = function(post,dbname){
if(!port){
port = 27017;
}
if(!dbname){
dbname=“test”;
}
db = connect(“localhost:”+port+“/”+dbname);
return db;
}
如果将这个脚本加载到 shell 中,connectTo 函数就可以使用了:
> typeof conn
undefined
>load(‘testConnect.js’)
>typeof conn
function
默认情况下,shell 会查找启动 shell 时所在的目录(使用 pwd() 查看)。如果脚本不在当前目录下,则可以给 shell 指定一个相对路径或绝对路径。如果希望将shell 脚本放在~/my-scripts 中,那么可以使用 load(“/home/myUser/my-scripts/defineConnectTo.js”) 命令来加载 defineConnectTo.js。注意,load 函数无法解析 ~ 字符。

可以使用 run 函数在 shell 中运行命令行程序。可以在函数的参数列表中指定程序所需的参数:
>run(“ls”,“-l”,“/home/user/script/”) #查看目录下的文件
这种方式通常局限性较大,因为输出的格式很奇怪,而且不支持管道。
6、.mongorc.js文件
如果你有一些需要频繁被加载的脚本,那么可以将它们添加到 .mongorc.js 文件中。此文件会在启动 shell 时自动运行。
假设你希望 shell 在你成功登录时显示欢迎语。那么可以在用户主目录下创建一个名为 .mongorc.js 的文件,然后在其中添加如下内容:
//.mongorc.js
var tmp = “hello”;
print(tmp);
之后,当启动 shell 时,就会看到这样一些内容:
$ mongo
mongo shell version:4.2.1
connect to :test
hello
在更实际一些的场景中,可以使用此脚本设置任何要使用的全局变量,或者为长名称创建一个简短的别名,也可以重写内置函数。.mongorc.js 最常见的用途之一是移除那些比较“危险”的 shell 辅助函数。可以在这里使用 no 选项重写类似dropDatabase 或 deleteIndexes 这样的函数,或者取消它们的定义:
var no=function(){
print(“no no no”)
}
//禁止删除数据库
db.dropDatabase = DB.prototype.dropDatabase = no;
//禁止删除集合
DBCollection.prototype.drop=no;
//禁止删除单个索引
DBCollection.prototype.dropIndex=no;
//禁止删除多个索引
DBCollection.prototype.dropIndexes = no;
现在,如果尝试调用这些函数,则它会打印出一条错误消息。注意,这种方式并不能保护数据库免受恶意用户的攻击,它只能帮助你防止自己的手误操作。
如果在启动 shell 时指定 --norc 参数,则可以禁用对 .mongorc.js 文件的加载。
.mongorc.js还可以预设变量,方便后续使用
7、不便使用的集合名称
方式一
>db.getCollection(“version”)
方式二
var collections = [“version”,“posts”]
for(var i in collections){
print(db.test[collections[i]]);
}
方式三
>var name = “@#$”
>db[name].find();
直接使用 db.@#&! 进行查询是非法的,但是可以使用 db[name]。


版权声明:本文为ntzzzsj原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。