MongoDB

MongoDB

简介

什么是MongoDB?

一种数据库,而且是非关系型数据库。

MongoDB的优点

  1. 可以快速开发web型应用,因为灵活,不用像关系型数据库一样需要建表
  2. MongoDB存储的是文档(document),文档内存储的是类似json的结构,所谓json就是字符串数组

MongoDB常见命令

MongoDB的安装

我这里使用的docker的方法,注意,这里使用4版本,使用其他版本会出问题,暂时未解决。

# 运行并下载最新的mongodb镜像
docker run -itd --name mongo -p 27017:27017 mongo:4 --auth
# 使用admin账户进行登录
docker exec -it mongo mongo admin
# 创建一个admin账号,密码为 admin
db.createUser({ user:'admin',pwd:'admin',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},"readWriteAnyDatabase"]});
# 尝试使用新创建的用户进行连接
db.auth('admin', 'admin')

# 开启服务器: 
net start MongoDB
# 关闭服务器: 
net stop MongoDB
# 打开客户端: 在cmd窗口输入便可以连接上数据库
mongo

# 查看数据库信息
> show dbs;
admin   0.000GB
config  0.000GB
local   0.000GB
# 查看当前数据库有什么集合
> show collections;
# 查看所在数据库
> db;
# 进入数据库
use ljq;

MongoDB的数据库分类

  1. 数据库(database):用来存储集合的,而且数据库也有分大小。
  2. 集合(collection):集合类似于数组,用于存放文档的。
  3. 文档(document): 文档是MongoDB数据库中最小的单位,我们要操作的目标就是文档。

MongoDB关系: 数据库(database) > 集合(collection)> 文档(document

值得注意的是: 在MongoDB中不需要自己创建数据库和集合,便可以直接创建文档,其实就是在创建文档的同时,会将数据库和集合创建了。

mongodb数据库的CRUD操作

核心: 增: insert() 删: remove() 查: find() 改: update()

增加

表示向 集合名为ljq插入了文档, 文档内容是对象。 集合名是自己创建的,想用什么名称就用什么。

# 表示增加一个或多个文档
db.集合名.insert(document)	

db.ljq.insert({name: "孙悟空", age: 28, address:"花果山"});

db.ljq.insert([
	{name:"猪八戒", age: 38, address:"高老庄"}, 
	{name: "沙和尚", age: 45, address:"流沙河"}
]);
# 表示插入一个文档,回调函数callback是可选的
db.集合名.insertOne(document,[callback]) 
# 表示向集合名为piyou的 插入了一个文档。
db.ljq.insertOne({name:"唐僧", age: 18, address: "女儿国"});

# 表示插入多个文档
db.集合名.insertMany(doucment, [callback]);
# 可以看到,用法是和insert是相差不多的。数组里面有对象,对象即是文档。
db.ljq.insertMany([
	{name:"白骨精", age:20,address:"白骨洞"}, 
	{name:"蜘蛛精", age: 24, address:"蜘蛛洞"}
]);

查找

find()返回的是数组,数组内存放着文档,findOne()返回的就是一个文档, findMany()返回的也是数组内存放着文档的形式。find()的返回值还可以调用count(), 用来获取符合条件的数量

# 查询一个或多个文档 condition是查询条件
db.集合名.find(condition);		
# 查询一个文档
db.集合名.findOne(condition);	 
# 查询多个文档
db.集合名.findMany(condition);
# 这条语句表示的是查询age是18的文档。 值得注意的是,条件也是写在了对象里面,
# 也是因为传入的值需要是json语句
db.ljq.find({age:18});

# 使用count()来计数
db.集合名.find(condition).count();
db.ljq.find().count();

修改

# 修改一个或多个文档 condition: 查询的条件 newObject: 需要修改的语句
db.集合名.update(condition,newObject);	
# 修改一个文档
db.集合名.uodateOne(condition,newObject);
# 修改多个文档 
db.集合名.updateMany(condition, newObject);  

首先需要注意的是,在使用update()时,需要一个修改操作符

$set		# 表示需要设置指定的属性
$unset		# 表示需要删除指定的属性
$push		# 表示给数组添加一个新元素,因为文档内也会有数组,数组便会有数组元素
$addToset 	# 表示给数组添加一个新元素,和push的区别是,如果出现同名的数组元素,则不会再添加
$gt			# 大于
$gte		# 大于等于
$lt			# 小于
$lte		# 小于等于
$or [{条件一,条件二}]		# 表示或的意思,符合条件一或者条件二	
$inc		# 表示自增,用在在原来数据的基础上对数据加减,可用于加薪减薪的操作

所以不能像下面这条语句这样使用,这样使用的话,会使用{age:18}覆盖掉{name:“猪八戒”…}这整条语句。

db.ljq.update({name:"猪八戒"}, {age:39});

> db.ljq.update({name:"猪八戒"}, {age:39});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.ljq.find({});
{ "_id" : ObjectId("633adb3cc2973e9852d04dca"), "name" : "孙悟空", "age" : 28, "address" : "花果山" }
{ "_id" : ObjectId("633adb72c2973e9852d04dcb"), "age" : 39 }
{ "_id" : ObjectId("633adb72c2973e9852d04dcc"), "name" : "沙和尚", "age" : 45, "address" : "流沙河" }
{ "_id" : ObjectId("633adbc5c2973e9852d04dcd"), "name" : "唐僧", "age" : 18, "address" : "女儿国" }
{ "_id" : ObjectId("633adbf1c2973e9852d04dce"), "name" : "白骨精", "age" : 20, "address" : "白骨洞" }
{ "_id" : ObjectId("633adbf1c2973e9852d04dcf"), "name" : "蜘蛛精", "age" : 24, "address" : "蜘蛛洞" }

正确的修改

# 表示根据条件{name:"孙悟空"}, 找到了孙悟空的这个文档, 使用了$set(修改指定属性)
db.ljq.update({name:"孙悟空"}, {$set:{age:29}});

> db.ljq.update({name:"孙悟空"}, {$set:{age:29}});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.ljq.find({});
{ "_id" : ObjectId("633adb3cc2973e9852d04dca"), "name" : "孙悟空", "age" : 29, "address" : "花果山" }
{ "_id" : ObjectId("633adb72c2973e9852d04dcb"), "age" : 39 }
{ "_id" : ObjectId("633adb72c2973e9852d04dcc"), "name" : "沙和尚", "age" : 45, "address" : "流沙河" }
{ "_id" : ObjectId("633adbc5c2973e9852d04dcd"), "name" : "唐僧", "age" : 18, "address" : "女儿国" }
{ "_id" : ObjectId("633adbf1c2973e9852d04dce"), "name" : "白骨精", "age" : 20, "address" : "白骨洞" }
{ "_id" : ObjectId("633adbf1c2973e9852d04dcf"), "name" : "蜘蛛精", "age" : 24, "address" : "蜘蛛洞" }

其他用法

# 表示使用 $unset(删除指定属性), 将唐僧的address的属性值给删掉了
db.ljq.update({name:"唐僧"}, {$unset:{address: 1}});

> db.ljq.update({name:"唐僧"}, {$unset:{address: 1}});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.ljq.find({});
{ "_id" : ObjectId("633adb3cc2973e9852d04dca"), "name" : "孙悟空", "age" : 29, "address" : "花果山" }
{ "_id" : ObjectId("633adb72c2973e9852d04dcb"), "age" : 39 }
{ "_id" : ObjectId("633adb72c2973e9852d04dcc"), "name" : "沙和尚", "age" : 45, "address" : "流沙河" }
{ "_id" : ObjectId("633adbc5c2973e9852d04dcd"), "name" : "唐僧", "age" : 18 }
{ "_id" : ObjectId("633adbf1c2973e9852d04dce"), "name" : "白骨精", "age" : 20, "address" : "白骨洞" }
{ "_id" : ObjectId("633adbf1c2973e9852d04dcf"), "name" : "蜘蛛精", "age" : 24, "address" : "蜘蛛洞" }

删除

# 删除符合条件的一个或多个文档
db.集合名.remove(condition)
# 删除符合条件的一个文档
db.集合名.deleteOne(condition)	
# 删除符合条件的多个文档
db.集合名.deleteMany(condition)	

# 表示删除了符合 age为39 这个条件的一个或多个文档。 也就是删掉了刚刚那个猪八戒的那条语句
db.ljq.remove({age: 39});

> db.ljq.remove({age: 39});
WriteResult({ "nRemoved" : 1 })
> db.ljq.find({});
{ "_id" : ObjectId("633adb3cc2973e9852d04dca"), "name" : "孙悟空", "age" : 29, "address" : "花果山" }
{ "_id" : ObjectId("633adb72c2973e9852d04dcc"), "name" : "沙和尚", "age" : 45, "address" : "流沙河" }
{ "_id" : ObjectId("633adbc5c2973e9852d04dcd"), "name" : "唐僧", "age" : 18 }
{ "_id" : ObjectId("633adbf1c2973e9852d04dce"), "name" : "白骨精", "age" : 20, "address" : "白骨洞" }
{ "_id" : ObjectId("633adbf1c2973e9852d04dcf"), "name" : "蜘蛛精", "age" : 24, "address" : "蜘蛛洞" }

练习

练习1:尝试阅读下面的语句

其实我们可以把下面的语句当作一个json对象来看待,db.collectionName.operate是固定的写法,之后可以用json的方式看待

向集合ljq中插入一个数组,数组中有两个文档,文档内有一个hobby的文档,这个hobby文档被称作内嵌文档,然后hobby文档内有两个数组。

注意:这里的代码我为了方便注释,写了# 在粘贴复制的时候请去掉这个

db.ljq.insert([
    {
	    name:"大大白", 
	    age: 28, 
	    hobby:{ # 是一个对象,对量里面分别是数组
	        music:['new Boy', '云烟成雨', '秋酿'],
	        games:['王者荣耀', '和平精英', '光遇']
	    }
    },
    {
        name: "小白",
        age: 21,
        hobby:{
            movies:['大话西游', '唐伯虎点秋香'],
            games:['王者荣耀', '旅行青蛙', '穿越火线']
        }
    }
]);

# 演示
> db.ljq.insert([
...     {
...     name:"大大白", 
...     age: 28, 
...     hobby:{ 
...         music:['new Boy', '云烟成雨', '秋酿'],
...         games:['王者荣耀', '和平精英', '光遇']
...     }
...     },
...     {
...         name: "小白",
...         age: 21,
...         hobby:{
...             movies:['大话西游', '唐伯虎点秋香'],
...             games:['王者荣耀', '旅行青蛙', '穿越火线']
...         }
...     }
... ]);
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})
> db.ljq.find({"hobby.games":"王者荣耀"});
{ "_id" : ObjectId("633ae22ec2973e9852d04dd0"), "name" : "大大白", "age" : 28, "hobby" : { "music" : [ "new Boy", "云烟成雨", "秋酿" ], "games" : [ "王者荣耀", "和平精英", "光遇" ] } }
{ "_id" : ObjectId("633ae22ec2973e9852d04dd1"), "name" : "小白", "age" : 21, "hobby" : { "movies" : [ "大话西游", "唐伯虎点秋香" ], "games" : [ "王者荣耀", "旅行青蛙", "穿越火线" ] } }

练习2:插入20000条数据,有哪种是比较快捷的方式?

吐个槽,这玩意真的不是用js写的嘛

# 这是方式一,这种方式是需要调用insert语句20000次,效率会十分低下,那么可不可以,
# 只调用一次insert语句呢?如果可以的话,应该怎么实现呢?
# 可以将数据先存储起来,然后一次性加入。
for(let i = 1; i <= 20000; i++){
    db.nums.insert({num:i});
}

# 这是将数据全部存到了数组中,随后再将数组插入,只执行了一次insert语句。
# 如果还有更好的想法都可以去尝试。还有就是自己给自己出题目,如果都能够实现的话,
# 那么说明你学的差不多了。
const arr = [];
for(let i = 1; i <= 20000; i++){
    arr.push({num:i});
}
db.nums.insert(arr);

文档间的关系(三种)

一对一

内联文档

# 可以看到一个妻子是对应一个丈夫的,这是使用内嵌文档来实现的。
db.ljq.insert([
    {
        name:"zcc",
        age: 26,
        handsband:{
            name:"ljq",
            age: 25
        }
    },
    {
        name: "ljq",
        age: 25,
        handsband:{
            name: "zcc",
            age: 26
        }
    }
]);

一对多

父母 - 孩子

文章 - 评论

内嵌文档也可以实现,就是一个文档内嵌多个文档,不过比较繁琐

用户一个文档,订单一个文档,在使用的时候再一一对应

# 第一句是创建了两个用户, 分别是花木兰和诸葛亮
db.users.insert([{name:"花木兰"}, {name:"诸葛亮"}]);
# 这一句是查询用户内容, 这是需要知道用户的_id,从而在第三句加入
db.users.find();
# 这是生成了一个订单,并且user_id存储的是花木兰的id
db.orders.insert({list_name:["配马", "鞍鞯"], user_id: ObjectId("633ae419c2973e9852d09bf4")})
# 再生成了一个订单,并且user_id存储的是花木兰的id
db.orders.insert({list_name:["蜜糖", "男装"], user_id: ObjectId("633ae419c2973e9852d09bf5")})
# 在users里查找到 花木兰 这个用户的id,并将其存储起来
let userId = db.users.findOne({name:"花木兰"})._id;
## 使用id来查找
db.orders.find({user_id: userId});



> db.users.insert([{name:"花木兰"}, {name:"诸葛亮"}]);
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})
> db.users.find();
{ "_id" : ObjectId("633ae419c2973e9852d09bf4"), "name" : "花木兰" }
{ "_id" : ObjectId("633ae419c2973e9852d09bf5"), "name" : "诸葛亮" }
> db.orders.insert({list_name:["配马", "鞍鞯"], user_id: ObjectId("633ae419c2973e9852d09bf4")})
WriteResult({ "nInserted" : 1 })
> db.orders.insert({list_name:["蜜糖", "男装"], user_id: ObjectId("633ae419c2973e9852d09bf5")})
WriteResult({ "nInserted" : 1 })
> let userId = db.users.findOne({name:"花木兰"})._id;
> db.orders.find({user_id: userId});
{ "_id" : ObjectId("633ae44f88b15d56aa4bd92c"), "list_name" : [ "配马", "鞍鞯" ], "user_id" : ObjectId("633ae419c2973e9852d09bf4") }

多对多

学生 - 老师

商品 - 分类

可以使用内嵌文档的形式完成

db.teas.insert([{name:"诸葛亮"}, {name:"龟仙人"}, {name:"唐僧"}]);
db.teas.find();
db.stus.insert([
    {
        name: "孙悟空",
        // 插入的是 龟仙人和唐僧的id
        teas_id:[ObjectId("633ae5b1aca876176e473c1c"), ObjectId("633ae5b1aca876176e473c1d")]
    },
    {
        name: "刘禅",
        // 插入的是 诸葛亮和龟仙人的id
        teas_id:[ObjectId("633ae5b1aca876176e473c1b"),ObjectId("633ae5b1aca876176e473c1c")]
    }
        
]);
db.stus.find();

投影

就是限制条件,显示效果

创建一个集合

# 这样创建的是一个列表集合。
db.staff.insert([
    {name:"小白", age: 18, salary: 3000},
    {name:"大白", age: 19, salary: 3500},
    {name:"熊大", age: 23, salary: 3200},
    {name:"熊二", age: 22, salary: 3100},
    {name:"光头强", age: 27, salary: 3400},
    {name:"小鲤鱼", age: 17, salary: 1500},
    {name:"奥特曼", age: 39, salary: 4500}
]);

需求一:按照薪资排列,可以使用sort()方法

sort(condition)	
# 表示按照薪资升序排列
sort({salary:  1})
# 表示按照薪资降序排列
sort({salary: -1})
# 这样使用
> db.staff.find().sort({salary: 1});
{ "_id" : ObjectId("633afb3492b25832d18a9f0f"), "name" : "小鲤鱼", "age" : 17, "salary" : 1500 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0a"), "name" : "小白", "age" : 18, "salary" : 3000 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0d"), "name" : "熊二", "age" : 22, "salary" : 3100 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0c"), "name" : "熊大", "age" : 23, "salary" : 3200 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0e"), "name" : "光头强", "age" : 27, "salary" : 3400 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0b"), "name" : "大白", "age" : 19, "salary" : 3500 }
{ "_id" : ObjectId("633afb3492b25832d18a9f10"), "name" : "奥特曼", "age" : 39, "salary" : 4500 }
# 多条件排序 这句的含义是按照薪资升序排序,如果出现了相同的薪资的人,则按照年龄降序排序。
> db.staff.find().sort({salary: 1, age: -1});
{ "_id" : ObjectId("633afb3492b25832d18a9f0f"), "name" : "小鲤鱼", "age" : 17, "salary" : 1500 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0a"), "name" : "小白", "age" : 18, "salary" : 3000 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0d"), "name" : "熊二", "age" : 22, "salary" : 3100 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0c"), "name" : "熊大", "age" : 23, "salary" : 3200 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0e"), "name" : "光头强", "age" : 27, "salary" : 3400 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0b"), "name" : "大白", "age" : 19, "salary" : 3500 }
{ "_id" : ObjectId("633afb3492b25832d18a9f10"), "name" : "奥特曼", "age" : 39, "salary" : 4500 }

分页

db.staff.find().limit(2);
# 表示只会显示前两个数据
> db.staff.find().limit(2);
{ "_id" : ObjectId("633afb3492b25832d18a9f0a"), "name" : "小白", "age" : 18, "salary" : 3000 }
{ "_id" : ObjectId("633afb3492b25832d18a9f0b"), "name" : "大白", "age" : 19, "salary" : 3500 }

在查询时可以在find()中的第二个参数的位置传入 投影,{name: 1, _id: 0}: 表示显示name属性,不显示_id属性。

# 这句的含义是只显示 name和salary属性,不显示 _id属性
db.staff.find({}, {name: 1, _id: 0, salary: 1});

> db.staff.find({}, {name: 1, _id: 0, salary: 1});
{ "name" : "小白", "salary" : 3000 }
{ "name" : "大白", "salary" : 3500 }
{ "name" : "熊大", "salary" : 3200 }
{ "name" : "熊二", "salary" : 3100 }
{ "name" : "光头强", "salary" : 3400 }
{ "name" : "小鲤鱼", "salary" : 1500 }
{ "name" : "奥特曼", "salary" : 4500 }

mongoose

概念

什么是mongoose?

一般我们不会直接用MongoDB的函数来操作MongoDB数据库 , 而Mongose就是一套操作MongoDB数据库的接口。

简而言之就是:mongoose也可以操作mongodb数据库,而且它来操作数据库会有更多好处

为什么要使用mongoose? mongoose的优点有哪些?

  1. 可以为mongodb的文档创建一个模式结构(Schema),说白了就是一个约束的条件
  2. 比原生的Node相比更容易操作mongodb数据库,也就是更方便
  3. 可以使用中间件和其他的应用业务挂钩,可以和其他的应用结合使用
  4. 数据可以通过类型转换转换成对象模型,也就是有方法可以使用。

Mongoose的三个新对象

Schema(模式对象)

定义了约束mongodb的文档结构的条件

Model

相当于mongodb中的 collection(集合)

Document

document表示集合中的具体文档

值得注意的是这三个都是对象,是对象的话则有它们对应的方法和属性。

vscode使用mongoose来操作数据库

// 1、引入 mongoose 模块
var mongoose = require('mongoose');

// 2、连接数据库 mongodb://账号:密码@192.168.76.128
var url = "mongodb://admin:admin@192.168.76.128";
mongoose.connect(url)
.then(() => console.log("Connection Successful"))
    .catch(err => console.log(err));;

// 2.1、这是对数据库的监听,而且是使用once, 只监听一次就行了
mongoose.connection.once('open', (err) => {
    if (!err) {
        console.log("数据库已连接...");
    }
});

mongoose.connection.once('close', (err) => {
    if (!err) {
        console.log("数据库已断开!!!");
    }
});

// 3、重命名,简化操作
var Schema = mongoose.Schema;

// 3.1、创建了约束条件:type是类型,default是默认是女,如果是女的话则不用写性别这个属性值了。
var stuSch = new Schema({
    name: String,
    age: Number,
    gender: {
        type: String,
        default: "女"
    },
    address: String
});

// 4、创建了模型, 数据库中的集合名是 stuSch(会自动加s), 第二个参数是标准,即是约束条件: stuSch
var stuModel = mongoose.model('student', stuSch);

// 5、使用模型创建对象, 当想创建多个的时候,可以使用数组里面存储对象的方式
// stuModel.create({
//     name: "西施",
//     age: 18,
//     address: "王者峡谷"
// }, (err) => {
//     console.log(err);
// });

// stuModel.deleteMany({},function(err) {
//     console.log(err)
// });

//stuModel.find({},function(err,doc) {
//    console.log(doc)
//});

// stuModel.updateOne({name:"西施", age:'123123123'},function(err,doc) {
//     console.log(doc)
// });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值