名词注释
下面的截图中,有一些名词需要解释一下,方便理解和应用:
cuckoo
叫库名,也就是数据库的名字users
叫集合名,也就是colloction的名字- 右侧带
_id
的一排排的, 那叫文档 - 而
_id
,username
,password
这些,叫字段 - 总结一下:
数据库 > 集合 > 文档 > 字段
mongodb的增删改查
增
往collection里增加一条数据
现在collection为users的数据是这样的:
现在要把他变成下面这样的数据:
方法:
db.collection('users').insertOne({hello: "world"}, function(err, data) {
if(err) {
console.log(err);
} else {
console.log(data);
}
})
// 不需要返回数据的话, function可以不写
往collection里增加多条数据
现在把上面的例子变成这样:
方法:
db.collection('users')
.insertMany([{one: '第一条数据'}, {two: '第二条数据'}], (err, data) => {
if(err) {
console.log(err);
} else {
console.log(data);
}
})
// 同样的, 如果不需要返回数据,后面的err data函数可以不写
注:原有的save()方法已废弃。现在介绍的都是3.2版本以后的新方法
删
删除单个文档
原始数据:
现在要把原始数据中的 “第一条数据” 文档删掉,变成这样:
做法:
db.collection('users')
.deleteOne({one: "第一条数据"}, (err, data) => {
if(err) {
console.log(err);
}else {
console.log(data);
}
})
同时删除包含某项数据的多条文档
直接写做法:
// 删除所有带有one: "第一条数据"的文档
db.collection('users')
.deleteMany({one: "第一条数据"}], (err, data) => {
if(err) {
console.log(err);
}else {
console.log(data);
}
})
删除该集合内的所有文档
db.collection('users').deleteMany({});
注意:原有的remove()方法已过时。
改(更新)
- 介绍操作符
$set: // 更新数据
更新文档中的一条数据
原始数据:
现在,把文档中的one的值,改为"dilireba" :
db.collection('users').updateOne({one: 'hello'}, {
$set: {one: "dilireba"}
})
更新文档中的多条数据
现在,把文档中one的值都改为"dilireba" :
db.collection('users').updateMany({one: 'hello'}, {
$set: {one: "dilireba"}
})
更新文档中的一条数据,并增加数据
现在,把文档中one的值改为"dilireba",同时再增加一条年龄数据"age": 18
db.collection('users').updateOne({one: 'hello'}, {
$set: {one: "dilireba", age: 18}
})
更新时删除一条数据
现在,把two: "world"这条数据删掉:
db.collection('users').update({two: 'world'}, {
$unset: {two: ''}
})
嵌套数据的更新
嵌套数据的处理,相对要复杂一点, 所以单出。
向嵌套数组内增加数据
原始数据:
这里的task是一个数组,现在往这里面添加对象和字段。让数据变成这样:
做法:
db.collection('users').updateOne({username: "222"}, {
$addToSet: {
task: {
date: new Date().getTime(),
newTask: [],
onGoing: [],
finished: [],
abandoned: []
}
}
})
// 注:$addToSet和$push同样是往数组内添加字段
// 前者是有同样的数据,就不添加。没有则添加。
// 后者是不管数组内有没有同样的数据,都会添加。
删除嵌套数组内的数据
db.collection('users').updateOne({username: "222"}, {
$pull: {
task: {
date: new Date().getTime(),
newTask: [],
onGoing: [],
finished: [],
abandoned: []
}
}
})
这里需要说明一下,以免误解:
$pull: { task: {...}}
这里的task
就是要操作的数组
task后面的{ ... }
数据对象,就是筛选条件。
所以上面的操作意思就是:
在task数组中找到date为new Date().getTime(),其他四个数组内容为[]的对象,
然后删除这个对象。
2022/6/13更新了该条说明
修改数组内数据中的某个字段的值
现在, 要把newTask的值,更新为['hello', 'world']
做法:
db.collection('users').updateOne({username: "222", 'task.newTask': []}, {
$set: {
{'task.$.newTask': ['hello', 'world']}
}
})
/*
需要注意两点:
一:查询条件里,必须要写上要被更新的元素。
比如要更新newTask,就要写上'task.newTask': []
二:注意$set里面的写法--'task.$.newTask' 。
这里的$是数组占位符,
task.$ 表示符合前面查找条件'task.newTask': []的元素,
比如经查询,task数组里第三个元素符合条件,那么task.$就代指第三个元素。
如果查询后,有多个元素都满足条件,那么task.$就代指第一个满足条件的元素。
task.$[] 表示task数组里的所有元素
task.$[index] 表示task数组里,下标为index的元素
*/
专门说说占位符$
上面有一段关于$的解释,
我觉得不仔细说下,很容易弄混。
给个原始数据:
[
{
_id: ObjectId335546da8fafwegs366,
nameArr: [
{
name: 'dilireba',
age: 18
},
{
name: 'haha',
age: 81
},
{
name: 'xiaosan',
age: 18
}
]
}
]
现在, 把name: "haha"改成name: “damimi”
db.collection('users').update({'nameArr.name': 'haha'}, {'nameArr.$.name': 'damimi'});
// 首先要清楚一点,$和前面的查询条件相关。
// 很容易看到,根据{'nameArr.name': 'haha'}这个查询条件,只有第二个元素符合条件,
// 这时 nameArr.$就可以理解为该元素:{ name: 'haha', age: 81 }
// 所以{'nameArr.$.name': 'damimi'}就表示把{ name: 'haha', age: 81 }的name值改为'damimi'
查
原始数据:
查找一条文档
现在,我们要查找one是"hello"且two是"world"的一条文档,
做法:
db.collection('users').findOne({one: 'hello', two: 'world'}).toArray ((err, data) => {
if(err) {
console.log(err);
}else {
console.log(data);
}
})
// 这里的findOne, 也可以换成find
查找多条文档
现在,我们要查找one是"hello"的所有文档:
db.collection('users').findMany({one: 'hello'}).toArray ((err, data) => {
if(err) {
console.log(err);
}else {
console.log(data);
}
})
查找集合内所有文档
db.collection('users').find({}).toArray ((err, data) => {
if(err) {
console.log(err);
}else {
console.log(data);
}
})
按条件查找
先介绍一下操作符:
$lt: // 小于
$lte: // 小于等于
$gt: // 大于
$gte: // 大于等于
$ne: // 不等于
现在,我们要查找number小于50的文档:
db.collection('users').find({number: [$lt: 50]}).toArray()
$or
操作符
现在,查找一下{one: ‘hello’} 或者{two: ‘world’}的文档:
db.collection('users').find({$or:[{one: 'hello'},{two: 'world'}]}).toArray()
注意:更新时是要先查找的,所以更新方法中的查找条件,同样可以用来做为查找条件。
参考文档
- 前端学习(吹水)群:
1064534163
- 菜鸟教程mongodb篇:
https://www.runoob.com/mongodb/mongodb-update.html
- 常用操作符:
https://www.cnblogs.com/angongIT/p/11170099.html
- 内嵌数组的增删改查:
https://www.cnblogs.com/KnowEditByW/p/9290593.html
- 多级文档更新数据:
https://www.jb51.net/article/215577.htm
- 修改嵌套数据指定顺序的字段值:
https://qa.1r1g.com/sf/ask/1372247971/
- $操作符:
https://blog.csdn.net/qq_34561892/article/details/113800916
- $unset操作符用法:
https://blog.csdn.net/yaomingyang/article/details/78791297
- 几种嵌套查询的方法:
http://t.zoukankan.com/xibuhaohao-p-12058248.html
- mongodb中文文档:
https://docs.mongoing.com/
- mongodb查找方法大全:
https://www.cnblogs.com/Sky-Ice/p/9429093.html
附赠mongodb的封装
- 源码可到
https://gitee.com/guozia007/cuckoo/tree/master/server/db
本项目中查看。 - 下为封装代码:
// 因为操作数据库最耗时的是连接数据库,所以对数据库进行封装,解决重复连接数据库问题
// 简单封装后存在多个实例化重复调用数据连接的问题,所以在封装时要解决。
const {MongoClient, ObjectId} = require('mongodb'); // 引入数据库
const config_db = require('./config'); // 引入db配置文件
/**
* 封装db库
*/
class Db {
// 创建一个静态方法,解决多个实例重复连接数据库的问题
// 比如实例testDb1已经连接过数据库了,
// 但是实例testDb2仍然会调用connect方法 去连接数据库,浪费了性能
// 我们需要的是,当前面有实例连接过数据库时,
// 数据库处于连接状态,那么以后的实例都不需要再去连接了
static getInstance() {
if(!Db.instance) { // 如果不存在实例
Db.instance = new Db(); // 就创建实例
}
return Db.instance;
}
constructor() {
// 设置一个属性 解决某个实例上多个方法重复调用数据库连接的问题
// 比如实例testDb已经连接过数据库了,那么在用find查询时,就不要再去重复连接了
this.dbClient = '';
this.connect(); // 初始化的时候就连接数据库
}
connect() { // 连接数据库
return new Promise((resolve, reject) => {
if(!this.dbClient) { // 如果dbClient不存在,就说明没调用过
MongoClient.connect(config_db.dbUrl, (err, client) => {
if(err) {
reject(err);
} else {
this.dbClient = client.db(config_db.dbName);
resolve(this.dbClient);
}
})
} else { // 如果已经存在 说明被调用过了
return resolve(this.dbClient);
}
})
}
add(collectionName, jsonData) { // 添加数据
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.insertOne(jsonData, (err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
})
.catch(err => reject(err));
})
}
addMany(collectionName, jsonArr) { // 添加多条数据
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.insertMany(jsonArr, (err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
})
.catch(err => reject(err));
})
}
/**删除 */
delOne(collectionName, jsonData) {
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.deleteOne(jsonData, (err, data) => {
if(err) {
reject(err);
}else {
resolve(data);
}
})
})
})
}
delMany(collectionName, jsonData) {
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.deleteMany(jsonData, (err, data) => {
if(err) {
reject(err);
}else {
resolve(data);
}
})
})
})
}
update(collectionName, jsonData1, jsonData2) { // 更新一条数据
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.updateOne(jsonData1,
{
$set: jsonData2,
},
(err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
})
})
}
update_delOne(collectionName, jsonData1, jsonData2) { // 更新时删除一条数据
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.updateOne(jsonData1,
{
$unset: jsonData2,
},
(err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
})
})
}
/**
* 当天第一次增加任务
* 需要往task数组里增加对象
* {
date: new Date().getTime(),
newTask: [],
onGoing: [],
finished: [],
abandoned: []
}
*/
update_new_task(collectionName,jsonData1, jsonData2) {
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.updateOne(jsonData1, {
$addToSet: {task: jsonData2}
}, (err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
})
})
}
update_$_data(collectionName,jsonData1, jsonData2) { // 更新嵌套数组内某个字段的值
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName)
.updateOne(jsonData1, {
$set: jsonData2
}, (err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
})
})
}
find(collectionName, jsonData) { // 查找数据
return new Promise((resolve, reject) => {
this.connect()
.then(db => {
db.collection(collectionName).find(jsonData)
.toArray((err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
})
.catch(err => reject(err));
})
}
getObjectId(id) { // 获取_id,因为查询时用到的_id的值是ObjectId()类型的数据
return new ObjectId(id);
}
}
// const testDb = Db.getInstance();
// testDb.add('users', {hello: "world"})
// .then(data => console.log('添加成功', data));
// testDb.addMany('users', [{one: '第一条数据'}, {two: '第二条数据'}]);
// testDb.delOne('users', {one: "第一条数据"});
// testDb.delMany('users', {one: "第一条数据"});
// testDb.update('users', {one: "hello"}, {one: 'dilireba', age: 18})
// .then(data => console.log('更新成功', data));
// testDb.update_delOne('users', {two: 'world'}, {two: ''});
// testDb.find('users', {})
// .then(data => console.log('获取数据', data));
// testDb.update_new_task('users', {username: "222"},
// {
// date: new Date().getTime(),
// newTask: [],
// onGoing: [],
// finished: [],
// abandoned: []
// })
// testDb.update_$_data('users', {username: "222", 'task.newTask': []}, {
// 'task.$.newTask': ['hello', 'world']
// })
module.exports = Db.getInstance();