【MongoDB】快速入门 - 数据库安装与基本操作 - mongoose - 增删改查

·  阅读 1550
【MongoDB】快速入门 - 数据库安装与基本操作 - mongoose - 增删改查

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

嗨!~ 大家好,我是YK菌 🐷 ,一个微系前端 ✨,喜欢分享自己学到的小知识 🏹,欢迎关注我呀 😘 ~ [微信号: yk2012yk2012,微信公众号:ykyk2012]

1. 简介

数据库是什么?

  • 数据库是按照数据结构来组织、存储和管理数据的仓库
  • 我们的程序都是在内存中运行的,一旦程序运行结束或者计算机断电,程序运行中的数据就会丢失
  • 所以我们要将一些程序运行的数据持久化到硬盘之中,以确保数据的安全性。而数据库就是数据持久化的最佳选择
  • 综上所述,数据库就是存储数据的仓库

数据库的分类

  • 关系型数据库(RDBMS)
    • MySQL、Oracle、DB2、SQL Server...
    • 关系型数据库中全是
  • 非关系型数据库(No SQL)
    • MongoDB、Redis...
    • 键值对数据库
    • 文档数据库MongoDB

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。

NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

在这里插入图片描述

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

RDBMS vs NoSQL

RDBMS

  • 高度组织化结构化数据
  • 结构化查询语言(SQL) (SQL)
  • 数据和关系都存储在单独的表中。
  • 数据操纵语言,数据定义语言
  • 严格的一致性
  • 基础事务

NoSQL

  • 代表着不仅仅是SQL
  • 没有声明性查询语言
  • 没有预定义的模式

-键 - 值对存储,列存储,文档存储,图形数据库

  • 最终一致性,而非ACID属性
  • 非结构化和不可预知的数据
  • CAP定理
  • 高性能,高可用性和可伸缩性

一些术语上的区别

SQL术语/概念MongoDB术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
cloumnfield数据字段/域
indexindex索引
table joins/表连接, MongoDB不支持
/嵌入文档MongoDB通过嵌入文档来替代多表连接
primary keyprimary key主键,MongoDB自动将_id字段设置为主键

NoSQL的优点/缺点

优点:

  • 高可扩展性
  • 分布式计算
  • 低成本
  • 架构的灵活性,半结构化数据
  • 没有复杂的关系

缺点:

  • 没有标准化

  • 有限的查询功能(到目前为止)

  • 最终一致是不直观的程序

  • MongoDB是为快速开发互联网Web应用而设计的数据库系统

  • MongoDB的设计目标是:极简、灵活、作为Web应用栈的一部分

  • MongoDB的数据模型是面向文档的,所谓文档是一种类似于JSON的结构,简单理解MongoDB这个数据库中存的是各种各样的JSON(BSON)

基本概念

在这里插入图片描述

  • 数据库 database
  • 集合(数组) collection

类似与SQL中的数据表,本质上是一个数组,里面包含看多个文档对象,[{},{},{}]

  • 文档对象 document

类似与SQL中的记录,一个文档对象{}就是一条记录

一个【数据库】由多个【集合】构成,一个【集合】包含多个【文档对象】

文档手册

官网 www.mongodb.com/

官网手册 docs.mongodb.com/manual/

菜鸟教程 www.runoob.com/mongodb/mon…

2. 安装&启动

下载安装 www.mongodb.com/try/downloa…

目前是4.4.6的版本 在这里插入图片描述

安装完成后添加到系统环境变量

在这里插入图片描述

在C盘创建data文件夹里面再创建一个db文件夹 在这里插入图片描述

在cmd中输入mongod即可 在这里插入图片描述 再打开一个CMD属于mongo 在这里插入图片描述 在这里插入图片描述

总结一下

  1. 启动mongodb服务器:mongod
  2. 修改默认端口:mongod --port 新的端口号
  3. mongodb默认的端口:27017
  4. 设置mongodb数据库的存储路径:mongod --dbpath 路径
  5. 连接mongodb数据库:mongo

可以使用MongoDB Compass图形化使用MongoDB

下载地址 www.mongodb.com/try/downloa…

在这里插入图片描述

3. 使用

3.1 基本使用

show dbsshow databases : 查看所有的数据库

use xxx : 切换到指定的数据库

db : 查看当前操作的数据库

show collections : 查看当前数据库中所有的集合

3.2 数据库的CRUD操作

插入数据insert

① 插入一条数据

db.collectionName.insertOne( {name:'liu'} )
复制代码

在这里插入图片描述

db表示的是当前操作的数据库

collectionName表示操作的集合,若没有,则会自动创建

插入的文档如果没有手动提供_id属性,则会自动创建一个

② 插入多条数据

db.collectionName.insertMany( [ {name:'ykyk'} , {name:'ykyk123'} ] )
复制代码

在这里插入图片描述

需要用【数组】包起来

③ 万能API【可以插入一个或多个】:

db.collectionName.insert()
复制代码

在这里插入图片描述 在这里插入图片描述 可视化页面用起来也很方便 在这里插入图片描述

查询数据find

db.collectionName.find()
复制代码

在这里插入图片描述

查询集合所有的文档,即所有的数据。 查询到的是整个数组对象。在最外层是有一个对象包裹起来的。

条件查询

db.collectionName.find({name:"hhh"})
复制代码

在这里插入图片描述 结果返回的是一个【数组】,可以在后面直接取索引下标 在这里插入图片描述 查找第一个

db.collectionName.findOne() 
复制代码

返回的是查询到的对象数组中的第一个对象(返回的是对象)

查询文档个数 db.collectionName.count()db.collectionName.length() 统计文档个数

注意:

> db.students.find({_id:222}).name  //错误
> db.students.findOne({_id:222}).name //正确
复制代码
  1. mongodb支持直接通过内嵌文档的属性值进行查询
{
	name:'ykyk',
	hobby:{
		movies:['movie1','movie2'],
		cities:['zhuhai','chengdu']
	}
}

db.users.find({hobby.movies:'movie1'}) //错误
db.users.find({"hobby.movies":'movie1'})//此时查询的属性名必须加上引号
复制代码
  1. 查询操作符的使用

比较操作符 gt大于gt 大于gte 大于等于 lt小于lt 小于lte 小于等于 ne不等于ne 不等于eq 等于的另一种写法 $or 或者

// 大于200
db.users.find({num:{$gt:200}})
// 大于200且小于300
db.users.find({num:{$gt:200,$lt:300}}) 

//大于300或小于200
db.users.find(
    {
        $or:[
            {num:{$gt:300}},
            {num:{$lt:200}}
        ]
    }
)  
复制代码
  1. 分页查询
db.users.find().skip(页码-1 * 每页显示的条数).limit(每页显示的条数)
复制代码
db.users.find().limit(10) // 前10条数据
db.users.find().skip(50).limit(10) // 跳过前50条数据,即查询的是第61-70条数据,即第6页的数据
复制代码
  1. 【排序】find查询结果默认是id升序排序查询的
// 1表示升序排列,-1表示降序排列
db.emp.find().sort({sal:1}) 
// 先按照sal升序排列,如果遇到相同的sal,则按empno降序排列
db.emp.find().sort({sal:1,empno:-1}) 
复制代码

注意:skip,limit,sort可以以任意的顺序调用,最终的结果都是先调sort,再调skip,最后调limit

设置查询结果的投影,即只过滤出自己想要的字段

// 在匹配到的文档中只显示ename字段
db.emp.find({},{ename:1,_id:0}) 
复制代码

通过可视化界面查询 在这里插入图片描述

修改数据update

替换整个文档

db.collectionName.update(condiction,newDocument)
复制代码

修改对应的属性,需要用到修改操作符,比如$set,$unset,$push,$addToSet

db.collectionName.update(
	// 查询条件
	{_id:222},
	{
		// 修改对应的属性
		$set:{ 
			name:'ykky',
			age:21
		}
		// 删除对应的[属性]
		$unset:{
			gender:1 //这里的1可以随便改为其他的值,无影响
		}
		
	}
)
复制代码

update默认与updateOne()等效,即对于匹配到的文档只更改其中的第一个 updateMany()可以用来更改匹配到的所有文档

db.students.updateMany(
	{name:'ykky'},
	{
		$set:{
			age:21,
			gender:222
		}
	}
)
复制代码

向数组中添加数据

db.users.update({username:'yk'},{$push:{"hobby.movies":'movie4'}})

// 如果数据已经存在,则不会添加
db.users.update({username:'yk'},{$addToSet:{"hobby.movies":'movie4'}})
复制代码

自增自减操作符$inc

// 让num自增100
{$inc:{num:100}} 
// 让num自减100
{$inc:{num:-100}} 
// 给工资低于1000的员工增加400的工资
db.emp.updateMany({sal:{$lt:1000}},{$inc:{sal:400}}) 
复制代码

删除数据remove

【一般不会删除】

db.collectionName.remove() 
复制代码

remove默认会删除所有匹配的文档。相当于deleteMany()

remove可以加第二个参数,表示只删除匹配到的第一个文档。此时相当于deleteOne()

db.students.remove({name:'ykky',true})
复制代码
db.collectionName.deleteOne()
db.collectionName.deleteMany()
复制代码

删除所有数据

db.students.remove({})
复制代码

性格较差,内部是在一条一条的删除文档。删除整个集合来提高效率。

删除集合

db.students.drop()
复制代码

删除数据库

db.dropDatabase()
复制代码

注意:删除某一个文档的属性,应该用update

remove以及delete系列删除的是整个文档

当删除的条件为内嵌的属性时:

db.users.remove({"hobby.movies":'movie3'})
复制代码

4. 文档之间的关系

一对一(one to one)

通过内嵌文档体现一对一的关系

一对多/多对一 (one to many / many to one)

【用户】 - 【订单】 每个订单数据用一个属性保存用户的id

db.users.insert([
	{_id:100,username:'yk'},
	{_id:101,username:'ykyk'}
])

db.orders.insert([
	{list:['apple','banana'],user_id:100},
	{list:['apple','banana2'],user_id:100},
	{list:['apple'],user_id:101}
])
复制代码

查询yk的所有订单:

①首先获取yk的id

var user_id=db.users.findOne({name:'yk'})._id;
复制代码

②根据id从订单集合中查询对应的订单

db.orders.find({user_id:user_id})
复制代码

多对多(many to many)

【商品】-【分类】

  • 每个商品数据用一个属性保存多个分类的id
  • 每个分类数据用一个属性保存多个商品的id

【老师】- 【学生】

db.teachers.insert([
    {
        _id:100,
        name:'yk'
    },
    {
        _id:101,
        name:'ykyk'
    },
    {
    	_id:102,
    	name:'ykky'
    }
])

db.students.insert([
	{
		_id:1000,
		name:'jun',
		tech_ids:[100,101]
	},
	{
		_id:1001,
		name:'jun2',
		tech_ids:[102]
	}
])
复制代码

5. mongoose

中文文档 www.mongoosejs.net/

建议直接看下面的英文文档 mongoosejs.com/docs/guide.…

5.1 简介

  • 之前我们都是通过shell来完成对数据库的各种操作,在开发中大部分时候我们都需要通过程序来完成对数据库的操作
  • 而mongoose就是一个让我们可以通过Node来操作MongoDB的模块
  • mongoose是一个对象文档模型(ODM)库,它对Node原生的MongoDB模块进行了进一步的优化封装,并提供了更多的功能
  • 在大多数情况下,它被用来把结构化的模式应用到一个MongoDB集合,并提供了验证和类型转换等好处
  1. mongoose是nodejs中的专门用于操作mongodb数据库的js库

mongoose的好处 1. 可以为文档创建一个模式结构(Schema) 2. 可以对模型中的对象/文档进行验证 3. 数据可以通过类型转换为对象模型 4. 可以使用中间件来应用业务逻辑挂钩 5. 比Node原生的MongoDB驱动更加容易

  1. mongoose中的对象:
  • Schema 模式对象(用于约束文档的结构)
  • Model 模型对象(即mongodb中的集合)
  • Document 文档对象(即mongodb中的文档)

5.2 安装

npm i mongoose
复制代码

在这里插入图片描述

5.3 连接与断开数据库

// 1.引入mongoose
const mongoose = require("mongoose");

// 2.连接mongodb数据库
mongoose.connect("mongodb://localhost/admin", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

// 3.监听mongodb数据库的连接状态
// 绑定数据库连接成功事件
mongoose.connection.once("open", () => {
  console.log("数据库连接成功!");
});
// 绑定数据库连接失败事件
mongoose.connection.once("close", () => {
  console.log("数据库已断开");
});

// 4.断开数据库连接(一般不用)【一般只需连接一次,不会断开,除非手动断开】
mongoose.disconnect();
复制代码

在这里插入图片描述

5.4 创建模式Schema对象和模型Model对象

const mongoose = require("mongoose");

mongoose.connect("mongodb://localhost/admin", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

mongoose.connection.once("open", () => {
  console.log("数据库连接成功!");
});

const Schema = mongoose.Schema;
// 创建Schema(模式)对象
const stuSchema = new Schema({
  name: String,
  age: Number,
  gender: {
    type: String,
    default: "female",
  },
  address: String,
});

// 通过Schema创建Model(模型)对象
// Model 代表的是数据库中的集合,通过Model才能对数据库进行操作
// mongoose.model(modelName, schema)
const StuModel = mongoose.model("student", stuSchema);
复制代码

5.5 利用模型Model对象进行增删查改操作

添加操作create

创建一个或多个文档并添加到数据库中

Model.create(doc(s), [callback])
复制代码

参数

  • doc(s) 可以是一个文档对象,也可以是一个文档对象的数组
  • callback 当操作完成后调用的回调函数
// 向数据库中插入一个文档
// StuModel.create(doc, err=>{})
StuModel.create(
  {
    name: "YK",
    age: 18,
    gender: "male",
    address: "WuHu",
  },
  (err) => {
    if (!err) {
      console.log("插入成功!");
    }
  }
);
复制代码

在这里插入图片描述

在这里插入图片描述

mongoose会自动把集合名称变成复数形式

UserModel.create({ user_id: 100, name: "ykyk" }, function (err) {
  if (!err) hui{
    console.log("插入成功");
  } else {
    console.log(err);
  }
});

let data = [
  { user_id: 101, name: "yk1", age: 19 },
  { user_id: 102, name: "yk2" },
];
UserModel.create(data, function (err) {
  console.log(arguments[1]); //第二个值表示的是所添加的文档对象,是一个数组
});
复制代码

还有一种添加数据的方式通过new 然后save

const u = new User({
	name:'ykJun',
	age: 18,
});

u.save(err=>{
	if(err){
		console.log(err);
		return;
	}
	console.log('成功');
});
复制代码

查询操作find

Model.find(conditions,[projection],[options],callback)
Model.findOne(conditions,[projection],[options],callback)
Model.findById(conditions,[projection],[options],callback)
复制代码

参数

  • conditions: 查询的条件
  • projection: 投影 { name: 1, gender: 1, _id: 0 }'name gender -_id'
  • options: 查询选项 { skip: xx, limit: xx }
  • callback:查询结果会通过回调函数返回,返回数组
UserModel.find({}, function (err, data) {
  console.log(data);
});

UserModel.find(
  { name: /yk/i },
  "name gender -_id",
  { skip: 2, limit: 1 },
  function (err, data) {
    console.log(data); //返回的是一个文档对象数组
  }
);
复制代码

findById 返回的是文档对象Document【Document对象是Model的实例】

findById
UserModel.findById("5f9fbfba14319e492c0f5bc4", function (err, data) {
  console.log(data);
  console.log(data instanceof UserModel); //true 返回的文档对象属于模型对象(即集合)的实例对象
});
复制代码

查询文档的数量

UserModel.count({}, function (err, count) {
  console.log(count);
});
复制代码

修改操作update

Model.update(conditions,[doc],[options],callback)
Model.updateMany(conditions,[doc],[options],callback)
Model.updateOne(conditions,[doc],[options],callback)
Model.replaceOne(conditions,[doc],[options],callback)
复制代码

参数

  • conditions 查询条件
  • doc 修改后的文档对象
  • options 配置参数 {multi: true}
  • callback 回调函数
UserModel.updateOne({ name: "yk" }, { $set: { age: 22 } }, (err,data) => {
  if (!err) {
    console.log("修改成功");
  }
});
复制代码

删除操作remove

model.remove(conditions,callback)
model.deleteOne(conditions,callback)
model.deleteMany(conditions,callback)
复制代码
UserModel.remove(
  {
    name: "yk",
  },
  (err, data) => {
    console.log("删除成功");
  }
);
复制代码

备注

Document 和 集合文档 一一对应, Document是Model的实例 通过Model查询到的结果都是Document

也可以通过document来处理操作数据库

const stu = new StuModel({
	name: 'ykykyk',
	age: 19
})
console.log(stu)
stu.save()

StuModel.findOne({}, (err, doc) => {
	if(!err){
		doc.update({$set:{age:22}}, err => {
			if(!err) {
				console.log('修改成功');
			}
		}
	}
	// 还有很多方法
	// get()
	// set()
	// toObject()
});


复制代码

5.6 模块化处理

  1. 单独创建一个数据库连接文件dbconncet.js
const mongooes = require("mongoose");

mongooes.connect("mongodb://localhost/mongooes_test", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

mongooes.connection.once("open", function () {
  console.log("连接成功");
});
复制代码
  1. 为每一个集合创建一个模型对象文件xxxModel.js
const mongooes = require("mongoose");
const Schema = mongooes.Schema;

const userSchema = new Schema({
  user_id: String,
  name: String,
  age: Number,
  gender: {
    type: Number,
    default: 0,
  },
});

// 定义模型
const UserModel = mongooes.model("user", userSchema);

module.exports = UserModel;
复制代码
  1. 在最终的文件index.js中引入数据库连接文件和创建模型的文件
require("./dbconncet");
const UserModel = require("./models/userModel");

UserModel.findOne({}, function (err, docs) {
  if (!err) {
    console.log(docs);
  }
});
复制代码
分类:
前端
分类:
前端