MongoDB

292 阅读5分钟

安装及配置(mac)

命令行安装

  • cd /usr/local 进入目录

  • sudo curl -O fastdl.mongodb.org/osx/mongodb… 该地址替换为最新版本地址

    获取方式:进入 www.mongodb.com/try/downloa… 点击copy link

  • sudo tar -zxvf mongodb-macos-x86_64-5.0.6.tgz 解压

  • sudo mv mongodb-macos-x86_64-5.0.6.tgz/ mongodb 解压后改名,不要忘了改文件名

配置全局变量

  • cd ~

  • vim .bash_profile

    在文件中添加 export PATH=${PATH}:/usr/local/mongodb/bin/

    :wq 保存退出

  • source .bash_profile 刷新终端

  • mongod --version 验证版本号是否成功。

Tip: 开启新的终端验证是否依然成功,若发现无效了,大概是 .zshrc 文件中没有自动执行我们配置的全局变量.

resolve:

  • vim .zshrc
  • 在文件中添加 source .bash_profile, :wq 保存退出
  • source .zshrc 刷新终端

启动和停止 mongodb 服务

  • 在 /usr/local/mongdb 目录下创建文件夹: data/db/
  • 启动 mongodb 服务: mongod --dbpath /usr/local/mongodb/data/db.
  • control + C 或 直接 'x' 掉终端 便可关闭服务。

mongoShell - 连接 mongodb 服务

开启新的终端执行 mongo 或点击mongo shell 直接执行

mongo Shell 执行环境

  • 提供了 javascript 执行环境
  • 内置了一些数据库操作命令
    • show dbs
    • db
    • use database
    • show collections
    • ...
  • 提供了一大推的内置API用来操作数据库
    • db.users.insert({ name: 'jack', age: 18 })

退出连接 三种方式:

  • exit
  • quit()
  • control + C 或直接关闭

基本概念

数据存储结构

由于 mongodb 是文档型数据库,其中存储到数据就是熟悉的 JSON 格式数据。

  • 可以把 mongodb 数据库想象成一个超级大的对象
  • 对象里面有不同的集合
  • 集合中有不同的文档
{
    // 数据库 Database
    "jingdong": {
        // 集合 collection,对应关系型数据库中的 Table
        "user": [
            // 文档 Document,对应关系型数据库中的 Row
            {
                // 数据字段 Field,对应关系型数据库总的 Column
                "id": 1,
                "username": "zhangsan",
                "password": "123"
            },
            {
                "id": 2,
                "username": "lisi",
                "password": "123"
            }
        ],
        "products": [
            {
                "id": 1,
                "username": "wangwu",
                "password": "123"
            },
            {
                "id": 2,
                "username": "lisi",
                "password": "123"
            }
        ]
    },
    // 数据库 Database
    "tianmao": {}
}

数据库

在 mongodb 中,数据库包含一个或多个文档集合。

查看数据库列表

show dbs

查看当前数据库

db

mongodb 中默认的数据库为 test, 如果没有创建新的数据库,集合将存放在 test 数据库中。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

  • admin。 从权限的角度看,这是“root”数据库。如果将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭数据库。
  • local。这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
  • config。当 mongo 用于分片设置时,config 数据库在内部使用,用于保存分片的相关信息。

切换数据库

use <DATABASE_NAME>

创建数据库

use <DATABASE_NAME>

在 mongodb 中数据库只有真正的有了数据才会被创建出来。所以我们可以切换到不存在的数据库,首次将数据存储到数据库中时(例如通过创建集合),mongodb 会创建数据库。

删除数据库

db.dropDatabase()

集合

集合类似于关系型数据库中的表,mongodb 将文档存储在集合中。

创建集合 如果不存在集合,则在第一次为该集合存储数据时,mongodb 会创建该集合。

db.user.insert({name:'jack'})

mongodb 提供 db.createCollection() 方法来显式创建具有各种选项的集合,例如设置最大大小或文档验证规则。如果未指定这些选项,则无需显式创建集合,因为在首次存储集合数据时,mongodb 会创建新集合。

查看所有集合

show collections

查看集合中所有选项

db.user.find()

user 为集合名称

删除集合

db.user.drop()

user 为集合名称

文档

  • mongodb 将数据记录存储为 BSON 文档
  • BSON(Binary JSON)是 JSON 文档的二进制表示形式,它比 JSON 包含更多的数据类型

image.png

文档结构

mongodb 文档由字段和值对组成,并具有以下结构:

image.png

数据类型

字段的值可以是任何 BSON 数据类型,包括其他文档,数组和文档水。例如,一下文档包含各种类型的值:

image.png

  • _id 保存一个 ObjectId 类型
  • name 包含一个嵌入式文档,该文档包含 first 和 last 字段
  • birth 和 death 持有 Date 类型的值
  • contribs 保存一个字符串数组
  • views 拥有 NumberLong 类型的值

mongoShell 基础操作

数据的增删改查,简称 CRUD。

  • Create 创建
  • Read 读取
  • Update 更新
  • Delete 删除

mongodb 图形管理软件

为了更好的学习 CRUD 及之后的语法,建议选择一款图形软件更方便操作。

  • Robo 3T
  • Studio 3T
  • Navicat
  • MongoDB Compass
  • ...

这些工具各有优劣,根据个人喜好选择即可,我推荐 Navicat 这个工具。

创建文档

创建或插入操作将新文档添加到集合中。如果集合当前不存在,则插入操作将创建集合。mongodb 提供以下方法,用于将文档插入集合中:

  • db.collection.insertOne(). 插入单个文档到集合中,传入一个对象
  • db.collection.insertMany(). 插入多个文档到集合中,传入一个数组
  • db.collection.insert(). 插入一个或多个文档到集合中

插入行为

1、 集合创建 如果该集合当前不存在,则插入操作将创建该集合

2、在 mongodb 中,存储在集合中的每个文档都需要一个唯一的 _id 字段作为主键。如果插入的文档省略 _id 字段,则 mongodb 驱动程序会自动为 _id 字段生成 ObjectId.

查询文档

基本查询

  • db.collection.find(query, projection)
    • query: 可选,使用查询操作符指定查询条件
    • projection: 可选,使用投影操作符制定返回的键。查询时返回文档中所有键值,只需省略该参数即可(默认省略)。
  • db.collection.findOne()

例如,如果我们先插入数据:

db.inventory.insertMany([
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 50, size: { h: 10, w: 15.25, uom: "cm" }, status: "B" }
]);

查询所有文档

db.inventory.find()

条件查询

db.inventory.find({status:'A'})

代表查询所有 status 为 A 的。

指定返回的字段

db.inventory.find({},{
	item: 1,
	status:1
})

item 和 status 为文档中的两个字段,1 代表返回这个字段。

指定 AND 条件

db.inventory.find({status:'A', qty: 50})

指定 OR 条件

db.inventory.find({
	$or: [
		{status: 'A'},
		{qty: 50}
	]
})

指定 AND 和 OR 条件

db.inventory.find({
	status: "A",
  $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
})

查询操作符

参考: docs.mongodb.com/manual/refe…

比较运算符:

名称描述
$eq匹配等于指定值的值。
$gt匹配大于指定值的值。
$gte匹配大于或等于指定值的值。
$in匹配数组中指定的任何值。
$lt匹配小于指定值的值。
$lte匹配小于或等于指定值的值。
$ne匹配所有不等于指定值的值。
$nin不匹配数组中指定的任何值。

逻辑运算符:

名称描述
$and将查询子句与逻辑连接,并返回与这两个子句条件匹配的所有文档。
$not反转查询表达式的效果,并返回与查询表达式不匹配的文档。
$nor用逻辑NOR连接查询子句,返回所有不能匹配这两个子句的文档。
$or用逻辑连接查询子句,或返回与任一子句条件匹配的所有文档。

例如:查询 qty 大于等于50的

db.inventory.find({
	qty:{
		$gte: 50
	}
})

例如:匹配 status 为 A 或 D 的

db.inventory.find({
	status: {$in:["A","D"]}
})

查询嵌套文档

直接匹配整个嵌套文档

注意:匹配嵌套文档时,传入的文档必须和真实文档完全匹配,包括字段的顺序。例如以下改变字段顺序将无法匹配到文档:

查询嵌套字段

相等匹配:

运算符匹配:

指定 AND 条件:

查询数组

练习之前我们先把之前的数据删除,重新插入测试数据:

db.inventory.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
   { item: "notebook", qty: 50, tags: ["red", "blue"], dim_cm: [ 14, 21 ] },
   { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
   { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
   { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);

匹配一个数组

db.inventory.find({
	tags: ["red", "blank"]
})

匹配数组也会完全匹配,包括元素的顺序。

如果想找到一个同时包含元素“red” 和“blank” 的数组,但不考虑顺序和是否有其他元素,可以使用 $all 运算符:

db.inventory.find({
	tags: {$all:["red","blank"]}
})

查询数组中的元素

查询包含 tags 中包含 blue 元素的项:

db.inventory.find({
	tags: 'blue'
})

查询 dim_cm 中包含大于25的项

db.inventory.find({
	dim_cm: {$gt: 25}
})

为数组指定多个条件

  • 使用数组元素上的复合过滤条件查询数组。以下例子,表示查询 dim_cm 数组包含可以满足各自条件的元素。即, dim_cm 包含大于20的元素,也包含小于15的元素。
db.inventory.find({
	dim_cm: {$gt: 20, $lt: 15}
})
  • 查询满足多个条件的数组元素。使用 $elemMatch 运算符可以在数组的元素上指定多个条件。以下例子,表示查询 dim_cm 数组中包含同时满足大于22且小于30的元素。
db.inventory.find({
	dim_cm: { $elemMatch: {$gt: 22, $lt: 30}}
})
  • 通过数组索引位置查询元素 查询 dim_cm 数组中索引位置为1时大于25的元素
db.inventory.find({
	"dim_cm.1": {$gt: 25}
})
  • 通过数组长度查询数组。使用 $size 运算符可按元素数量查询数组。以下例子,表示查询 tags 数组长度为3的。
db.inventory.find({
	"tags": {$size: 3}
})

查询嵌套文档中的数组

测试数据:

db.inventory.insertMany( [
   { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
   { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
   { item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
   { item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);

查询嵌套在数组中的文档

db.inventory.find({
	instock: { warehouse: "A", qty: 5 }
})

匹配文档需完全匹配。

在文档数组中的字段上指定查询条件

db.inventory.find({
	"instock.qty": {$gt: 20}
})

如下图,查询到的是包含 qty 大于 20 的项。

使用数组索引在嵌套文档中查询字段

db.inventory.find({
	"instock.0.qty": {$gt: 20}
})

如下图,查询到的是 instock 中第0项 qty 大于20的。

多条件

查询同时满足 qty 为5, warehouse 为 A 的项:

db.inventory.find({
	"instock": {$elemMatch: {qty: 5, warehouse:"A"}}
})

查询 instock 中 qty 同时满足大于20 小于等于60 的项。

db.inventory.find({
	"instock": {$elemMatch: {qty: {$gt: 20, $lte: 60}}}
})

包含 qty 大于10的,也包含 qty 小于等于20 的:

db.inventory.find({
	"instock.qty": {$gt: 10, $lte: 20}
})

包含 qty 为5的,也包含 warehouse 为 A的:

db.inventory.find({
	"instock.qty": 5,
	"instock.warehouse":"A"
})

指定返回字段

测试数据:

db.inventory.insertMany( [
  { item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
  { item: "notebook", status: "A",  size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
  { item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
  { item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
  { item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);

仅返回指定字段和 _id 字段

db.inventory.find( { status: "A" }, { item: 1, status: 1 } )

禁止 _id 字段

db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )

排除指定字段

db.inventory.find( { status: "A" }, { status: 0, instock: 0 } )

返回/禁止嵌入式文档中的特定字段

db.inventory.find(
   { status: "A" },
   { item: 1, status: 1, "size.uom": 1 }
)
db.inventory.find(
   { status: "A" },
   { item: 1, status: 1, "size": { "uom": 1 } }
)
db.inventory.find(
   { status: "A" },
   { "size.uom": 0 }
)

指定数组的返回

// 数组指定字段
db.inventory.find( { status: "A" }, { item: 1, status: 1, "instock.qty": 1 } )
// 指定返回 instock 的最后一项
// $slice 为正数时,返回前 n 项
// $slice 为负数时,返回后 n 项
db.inventory.find( { status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } } )

查询空字段或缺少字段

测试数据:

db.inventory.insertMany([
   { _id: 1, item: null },
   { _id: 2 }
])

相等过滤器

查询 item 为 null 或不包含 item 的文档。

db.inventory.find( { item: null } )

类型检查

查询 item 仅为 null 的文档。即 item 字段的值为 BSON 类型为 Null(类型编号10):

db.inventory.find( { item : { $type: 10 } } )

存在检查

db.inventory.find( { item : { $exists: false } } )

更新文档

  • db.collection.updateOne(<filter>,<update>,<optioins>)
  • db.collection.updateMany(<filter>,<update>,<optioins>)
  • db.collection.replaceOne(<filter>,<update>,<optioins>)

测试数据:

db.inventory.insertMany( [
   { item: "canvas", qty: 100, size: { h: 28, w: 35.5, uom: "cm" }, status: "A" },
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "mat", qty: 85, size: { h: 27.9, w: 35.5, uom: "cm" }, status: "A" },
   { item: "mousepad", qty: 25, size: { h: 19, w: 22.85, uom: "cm" }, status: "P" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "P" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" },
   { item: "sketchbook", qty: 80, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "sketch pad", qty: 95, size: { h: 22.85, w: 30.5, uom: "cm" }, status: "A" }
] );

mongodb 提供了更新操作符(例如 $set)来修改字段值。如果字段不存在,则 $set 将创建该字段。

更新单个文档

db.inventory.updateOne(
	{item:'paper'},
	{
		$set: {"size.uom": "cm", status:"P"},
		$currentDate: { lastModified: true }
	}
)

更新操作:

  • 使用 $set 运算符将 size.uom 值改为 cm, 将 status 值改为 P
  • 使用 $currentDate 运算符将 lastModified 字段的值更新为当前日期,如果字段不存在则创建。

更新多个文档

db.inventory.updateMany(
   { "qty": { $lt: 50 } },
   {
     $set: { "size.uom": "in", status: "P" },
     $currentDate: { lastModified: true }
   }
)

修改 qty 小于50的所有项。

替换文档

将一个全新的文档作为第二个参数传入,可以修改除 _id 以外的所有文档内容。

db.inventory.replaceOne(
   { item: "paper" },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)

删除文档

  • db.collection.deleteMany()
  • db.collection.deleteOne()

删除所有

db.inventory.deleteMany({})

删除所有符合条件的文档

db.inventory.deleteMany({status:"A"})

仅删除一个符合条件的文档

db.inventory.deleteOne({status:"A"})

在 Node 中操作 MongoDB

mkdir mongodb-demo
cd mongodb-demo
npm init -y
npm i mongodb

启动 mongodb,并插入数据

use myProject
db.users.insertMany([    {name:'jack', age:15},    {name:'zhangsan', age:20},    {name:'lisi', age:18},])

连接 mongodb

在 mongodb-demo 中新建文件 index.js

const { MongoClient } = require('mongodb')

const url = 'mongodb://localhost:27017'
const client = new MongoClient(url);

// database name
const dbName = 'myProject'

async function run() {
    try {
        await client.connect();
        console.log('connect successful')
        
        // 连接数据库
        const db = client.db(dbName);
        // 连接集合
        const collection = db.collection('users');
        const users = await collection.find()
        console.log(await users.toArray())
    } catch (err) {
        console.log('connect fail')
    } finally {
        await client.close()
    }
}

run()

CRUD

mongodb 在 nodejs 中的增删改查和 mongdoShell 操作语法是一样的。

  • collection.insertMany()
  • collection.insertOne()
  • collection.find()
  • collection.findOne()
  • collection.updateMany()
  • collection.updateOne()
  • collection.replaceOne()
  • collection.deleteMany()
  • collection.deleteOne()

mongoose 的简单使用

安装:

npm install mongoose

连接数据库:

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  // we're connected!
});

定义一个 schema(作为 collection 的 type 定义):

var kittySchema = mongoose.Schema({
  name: String
});

定义一个 Kitten 模块(模块相当于 collection):

var Kitten = mongoose.model('Kitten', kittySchema);

创建一个模块的实例就相当于定义了一个文档:

var felyne = new Kitten({ name: 'Felyne' });
console.log(felyne.name); // 'Felyne'

模块可以定义方法,此时的方法在模块的原型链上,每一个实例都可以调用:

kittySchema.methods.speak = function () {
  var greeting = this.name
    ? "Meow name is " + this.name
    : "I don't have a name";
  console.log(greeting);
}

var Kitten = mongoose.model('Kitten', kittySchema);

var fluffy = new Kitten({ name: 'fluffy' });
fluffy.speak(); // "Meow name is fluffy"

save 方法便可以保存这条文档数据:

fluffy.save(function (err, fluffy) {
    if (err) return console.error(err);
    fluffy.speak();
});
fluffy.save().then();

通过模块(collection)的 find 方法便可以查询所有文档:

Kitten.find(function (err, kittens) {
  if (err) return console.error(err);
  console.log(kittens);
})
Kitten.find.then()

更新:

Kitten.findOneAndUpdate(
    {},  // 查询条件
    {}   // 更新数据
).then()

删除

Kitten.findOneAndDelete(
    {}  // 条件
).then()