chapter2 MongoDB基础知识

  • 文档是MongoDB中数据的基本单元,非常类似于关系型数据管理系统的行,但更具表现力
  • 类似地,集合(collection)可以看做是一个拥有动态模式(dynamic schema)的表
  • MongoDB的一个实例可以拥有多个相互独立的数据库(database),每一个数据库都拥有自己的集合。
  • 每一个文档都有一个特殊的键,"_id",这个键在文档所属的集合中是唯一的。
  • MongoDB自带了一个简单但功能强大的JavaScript shell,可用于管理MongoDB的实例或数据操作

2.1 文档

    文档是MongoDB的核心概念。文档就是键值对的一个有序集。比如映射(map)、散列(hash)或字典(dictionary)。例如,在JavaScript里面,文档被表示为对象:

    {"greeting":"Hello,world!"}

    {"greeting":"Hello,world!","foo":3}

    文档中的数据类型可以是不同的数据类型。

    文档的键是字符创。除了少数例外情况,键可以使用任意UTF-8字符。

  • 键不能含有\0(空字符)。这个字符用于表示键的结尾
  • .和$具有特殊意义,只能在特定环境下使用。通常,这两个字符是保留的;如果使用不当的话,驱动程序有提示

    MongoDB区分大小写:

{"foo":3}和{"foo":"3"}不同

{"Foo":3}和{"foo":3}不同

    MongoDB文档不能有重复的键。

    文档中的键/值对是有序的:{"x":1,"y":2}与{"y":2,"x":1}是不同的。

2.2 集合

    集合就是一组文档。如果将MongoDB中的一个文档比喻为关系型数据库中的一行,那么一个集合就相当于一张表。

2.2.1 动态模式

    集合是动态模式的。这意味着一个集合里面的文档可以是各式各样的。

{"greeting":"Hello,world!"}和{"foo":5}可以存储在同一个集合中。

    既然没有必要区分不同类型文档的模式,为什么还要使用多个集合呢?这里有几个重要的原因:

  • 如果把各种各样的文档不加区分地放在同一个集合里,无论对开发者还是对管理员来说都将是噩梦。
  • 在一个集合里查询特定类型的文档在速度上也很不划算,分开查询多个集合要快得多
  • 把同种类型的文档放在一个集合里,数据会更加集中。
  • 创建索引时,需要使用文档的附加结构(特别是创建唯一索引时)

2.2.2 命名

    集合使用名称进行标识。集合名可以是满足下列条件的任意UTF-8字符串

  • 集合名不能是空字符串("")。
  • 集合名不能包含\0字符(空字符),这个字符表示集合名的结束
  • 集合名不能以"system."开头,这是为系统集合保留的前缀。例如,system.users这个集合保存着数据库的用户信息,而system.namespaces集合保存着所有数据库集合的信息。
  • 用户创建的集合不能在集合名中包含保留字符"$"。

    子集合:组织集合的一种惯例是使用"."分隔不同命名空间的子集和。如blog.posts和blog.authors,这里的blog集合(这个集合甚至不需要存在)跟它的子集合没有任何关系。

2.3 数据库

    在MongoDB中,多个文档组成集合,而多个集合可以组成数据库。一个MongoDB实例可以承载多个数据库,每个数据库拥有0个或者多个集合。每个数据库都有独立的权限,即便是在磁盘上,不同的数据库也放置在不同的文件中。

    数据库名可以是满足以下条件的任意UTF-8字符串:

  • 不能是空字符串("")。
  • 不得含有/,\,.,",*,<,>,:,|,?,$(一个空格),\0(空字符)。基本上,只能使用ASCII中的字母和数字。
  • 数据库名区分大小写,
  • 数据库名最多为64字节

    另外,有一些数据库名是保留的,可以直接访问这些有特殊语义的数据库。这些数据库如下所示。

  • admin

从身份验证的角度来讲,这是"root"数据库。

  • local

这个数据库永远都不可能复制,且一台服务器上的所有本地集合都可以存储在这个数据库中。

  • config

MongoDB用于分片设置是时,分片信息会存储在config数据库中。

2.4 启动MongoDB

wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.4.0.tgz
/**  目录/opt  **/
cd /opt
tar zxvf mongodb-linux-x86_64-3.4.0.tgz

/** 重命名 **/
mv mongodb-linux-x86_64-3.4.0.tgz mongodb

/** 进入mongodb目录 **/
cd mongodb

/** 创建db和日志目录 **/
mkdir /opt/mongodb/data 
mkdir /opt/mongodb/data/db
mkdir /opt/mongodb/data/logs
/** logs目录下创建mongodb.log文件 **/
touch mongodb.log

/** 在mongodb/data目中创建mongodb.conf **/
cd data
vi mongodb.conf

/** 加入相关配置 **/
#端口号
port = 27017 
#数据目录
dbpath = /opt/mongodb/data/db
#日志目录
logpath = /opt/mongodb/data/logs/mongodb.log
#设置后台运行
fork = true
#日志输出方式
logappend = true
#开启认证
#auth = true
cd ..   /* 返回上一层目录:mongodb文件夹*/

./bin/mongod --config /opt/mongodb/data/mongodb.conf

vim /etc/profile
# mongo
export PATH=$PATH:/opt/mongodb/bin
export PATH=$PATH:/opt/mongodb/bin

    mongodb启动监听27017端口,同时还会启动一个非常基本的HTTP服务器,监听数字比主端口号高1000的端口,也就是28017.

2.5 MongoDB shell简介

> x=200
200
> x/5;
40
> Math.sin(Math.PI/2);
1
> new Date("2010/1/1");
ISODate("2009-12-31T16: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
查看db当前指向那个数据库,可以使用db命令:
> db
test
选择数据库
> use foobar
switched to db foobar
> db
foobar

2.5.3 shell中的基本操作

1.创建

    insert函数可将一个文档添加到集合中。举一个存储博客文章的例子。首先,创建一个名为post的局部变量,这是一个JavaScript对象,用于表示我们的文档。他会有几个键:"title"、"content"、"date"(发布日期)

> post={"title":"My Blog Post",
... "content":"Here's my blog post.",
... "date":new Date()}
{
        "title" : "My Blog Post",
        "content" : "Here's my blog post.",
        "date" : ISODate("2018-03-18T23:10:33.590Z")
}
> db.blog.insert(post)
WriteResult({ "nInserted" : 1 })
> db.blog.find()
{ "_id" : ObjectId("5aaef21b031a94c0f439fbf5"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2018-03-18T23:10:33.590Z") }
2.读取

    find和findOne方法可以用于查询集合里的文档。若只想查看一个文档,可用findOne:

> db.blog.findOne()
{
        "_id" : ObjectId("5aaef21b031a94c0f439fbf5"),
        "title" : "My Blog Post",
        "content" : "Here's my blog post.",
        "date" : ISODate("2018-03-18T23:10:33.590Z")
}

    find和findOne可以接受一个查询文档作为限定条件。

3.更新
    使用update修改博客文章。update接收(至少)两个参数:第一个时限定条件(用于匹配待更新的文档),第二个是新的文档。
> post.comments=[]
[ ]
> db.blog.update({title:"My Blog Post"},post)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("5aaef21b031a94c0f439fbf5"), "title" : "My Blog Post", "content" : "Here's my blog post.", "date" : ISODate("2018-03-18T23:10:33.590Z"), "comments" : [ ] }
4.删除

    使用remove方法可将文档从数据库中永久删除。如果没有指定任何参数,它会将集合内的文档全部删除。它可以接收一个作为限定条件的文档作为参数。

> db.blog.remove({title:"My Blog Post"})
WriteResult({ "nRemoved" : 1 })

现在,集合又是空的了。

2.6数据类型

2.6.1 基本数据类型

  • null

null用于表示空值或者不存在的字段:{"x":null}

  • 布尔型

布尔类型有两个值true和false:{"x":false}

  • 数值

shell默认使用64为浮点型数值。因此,以下数值在shell中是很"正常"的 {"x":3.14}或{"x":3}

对于整型值,可使用NumberInt类(4字节)或NumberLong类(8字符){"x":NumberInt("3)} {"x":NumberLong("3")}

  • 字符串

UTF-8字符串都可表示为字符串类型的数据:{"x":"foobar"}

  • 日期

日期被存储为自新纪元一来的毫秒数,不存储时区:{"x":new Date()}

  • 正则表达式

查询时,使用正则表达式作为限定条件,语法也与JavaScript的正则表达式语法相同:{"x":/foobar/i}

  • 数组

数据列表或数据集可以表示为数组:{"x":["a","b","c"]}

  • 内嵌文档

文档可嵌套其他文档,被嵌套的文档作为父文档的值:{"x":{"foo":"bar"}}

  • 对象id

对象id是一个12字节的ID,是文档的唯一标识。{"x":ObjectId()}

  • 二进制数据
  • 代码

查询和文档中可以包括任意JavaScript代码:{"x":function(){/*...*/}}

2.6.2 日期

关于JavaScript日期类的完整解释,以及构造函数的参数格式,参见ECMAScript规范http://www.ecmascript.org

2.6.3 数组

    数组是一组值,它既能作为有序对象(如列表、栈或队列),也能作为无序对象(如数据集)来操作。

{"things":["pie",3.14]} "things"这个键的值是一个数组

2.6.4 内嵌文档

{
  "name":"John Doe",
  "address":{
    "street":"123 Park Street",
    "city":"Anythown",
    "state":"NY"
  }
}

2.6.5 _id和ObjectId

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

    1.ObjectId

    ObjectId是"_id"的默认类型。他设计成轻量型的,不同机器都能使用全局唯一的同种方法方便地生成它,ObjectId使用12字节的存储空间,是一个有24个十六进制数字组成的字符串。如果快速地创建多个ObjectId,会发现每次最后几位数字的变化。另外中间的几位数字也会变化。这是ObjectId的创建方式导致的。ObjectId的12字节按照如下方式生成:


  • 时间戳,与随后的5字节组合起来,提供了秒级别的唯一性
  • 由于时间戳在前,这意味着ObjectId大致会按照插入的顺序排列。
  • 这4个字节也隐含了文档创建的时间。绝大多数驱动程序都会提供一个方法,用于从ObjectId获取这些信息。

    接下来的3字节是所在主机的唯一标识符。通常是机器主机名的散列值(hash)。这样就可以确保不同主机生成不同的ObjectId,不产生冲突。

    为了确保在同一机器上并发的多个进程产生的ObjectId是唯一的,接下来的两字节来自产生ObjectId的进程的进程标识符(PID)。

    2.自动生成_id

如果插入文档没有"_id",系统会自动帮你创建一个。MongoDB的哲学:能交给客户端程序来做的事情就不要交给服务器来做。

2.7 使用MongoDB shell


也可以:



2.7.1 shell小贴士


例如,查看update函数的工作机制,


2.7.2 使用shell脚本执行


[root@localhost code]# cat defineConnectTo.js
/**
 * 连接到指定的数据库,并且将db指向这个连接
 */
var connectTo = function(port, dbname) {
  if (!port) {
    port = 27017;
  }
  if(!dbname) {
    dbname = "test";
  }
  db = connect("localhost:"+port+"/"+dbname);
  return db;
};

    如果在shell中加载这个脚本,connectTo函数就可以使用了。

> typeof connectTo
undefined
> load('defineConnectTo.js')
true
> typeof connectTo
function

    默认情况下,shell会在运行shell时所处的目录中查找脚本(可以使用run("pwd")命令查看)。如果脚本不再当前目录中,可以为shell指定一个相对路径或者绝对路径。例如,如果脚本放置在~/my-scripts目录中,可以使用load("/home/myUser/my-scripts/defineConnectTo.js")命令来加载defineConnectTo.js。注意,load函数无法解析~符号。


2.7.3 创建.mongorc.js文件

    如果某些脚本会被频繁加载,可以将它们添加到mongorc.js文件中。这个文件会在启动shell时自动执行。

    例如,我们希望启动成功时让shell显示一句欢迎语。为此,我们在用户主目录下创建一个名为.mongorc.js的文件,向其中添加如下内容:

// mongorc.js
var compliment = ["attractive", "intelligent", "like Batman"];
var index = Math.floor(Math.random()*3);

print("Hello,you're looking particularly"+compliment[index]+"today!");
[root@localhost code]# mongo
MongoDB shell version v3.4.0
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.0
Hello,you're looking particularlyattractivetoday!

    为了实用,可以使用这个脚本创建一些自己需要的全局变量,或者是为太长的名字创建一个简短的别名,也可以重写内置的函数。.mongorc.js最常见的用途之一是移除那些比较"危险"的shell辅助函数。

var no = function() {
  print("Not on my watch.");
};

// 禁止删除数据库
db.dropDatabase = DB.prototype.dropDatabase = no;

// 禁止删除集合
DBCollection.prototype.drop = no;

// 禁止删除索引
DBCollection.prototype.dropIndex = no;

    如果在启动shell时指定--norc参数,就可以禁止加载.mongorc.js。

2.7.4 定制shell提示

    将prompt变量设为一个字符串或者函数,就可以重写默认的shell提示。

    另一个方便的提示是显示当前使用的数据库:


2.7.5 编辑复合变量

shell的多行支持是非常有限的:不可以编辑之前的行。为了方便地调用编辑器,可以在shell中设置EDITOR变量(也可以在环境变量中设置):

> EDITOR="/usr/bin/emacs"

现在,如果想要编辑一个变量,可以使用"edit 变量名"这个命令,比如:

> var wap = db.books.findOne({title:"War and Peace"})
edit wap

在.mongorc.js文件中添加一行内容,EDITOR="编辑器路径";,以后不必单独设置EDITOR变量了。

2.7.6 集合命令注意事项

    可以使用db.collectionName获取一个集合的内容,但是,如果集合名称中包含保留字或者无效的JavaScript属性名称,db.collectionName就不能正常工作了。

    假设要访问version集合,不能直接使用db.version,因为db.version是db的一个方法

> db.version
function () {
        return this.serverBuildInfo().version;
    }

    为了访问version集合,必须使用getCollection函数:

> db.getCollection("version");
test.version

    如果集合名称中包含无效的JavaScript属性名称(比如foo-bar-baz和123abc),也可以使用这个函数来访问相应的集合。

    还有一种方法可以访问以无效属性名称命名的集合,那就是使用数组访问语法;在JavaScript中,x.y等同于x['y']。

如果需要对blog的每一个子集合进行操作,可以使用如下方式进行迭代:


而不必这样:


    注意,不能使用db.blog.i,这样会被解释为test.blog.i,而不是test.blog.posts。必须使用db.blog[i]语法才能将i解释为相应的变量。

    可以使用这种方式来访问那些名字怪异的集合:

> var name = "@#&!"
> db[name].find()

    直接使用db.@#&!进行查询时非法的,但是可以使用db[name]。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值