• 个人博客项目中遇到的 mongodb 操作


    本篇博客的内容均是我在开发个人博客的时候所遇到的问题,由于我还是个 mongodb 新手,所以这些操作还是花了些时间的,这里分享出来,希望能帮助到更多新手。

    mongoose 读取的数据无法修改

    在开发中我发现使用 mongoose 从 mongodb 中读取的数据可以修改属性值,但是无法新增属性。

    // model中的读取操作
    const getBlogs = () => {
    	return BlogModel.find();
    };
    // 处理拿到的数据
    const getBlogs = async (req, res) => {
    	const blogs = await BlogsModel.getBlogs();
    	console.log('修改前:',blogs);
    	blogs[0].title = "狗、猫、鼠";
    	console.log('修改后:',blogs);
    	blogs[0].bg = "#000";
    	console.log('新增后:',blogs);
    };
    //修改前:
    {
    	_id: new ObjectId("630daa261e82e63d97d073e7"),
    	title: 'fd',
    	browse: 0,
    	state: false,
    	...
    }
    //修改后: 
    {
    	_id: new ObjectId("630daa261e82e63d97d073e7"),
    	title: 'fd',
    	browse: 0,
    	state: false,
    	...
    }
    //新增后: 
    {
    	_id: new ObjectId("630daa261e82e63d97d073e7"),
    	title: 'fd',
    	browse: 0,
    	state: false,
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    原因

    实际上 mongoose 返回的数据并不是object,虽然通过typeof判断类型是Object,但其实 mongoose 自己封装的一个对象,并且这个对象会对数据进行实时查询以保证其符合预定义的 model,而无论添加删除 model 都不会改变,所以设置无效。

    解决方案

    方案一:赋值给新变量

    通过将读取的数据赋值给一个新的变量,就可以实现属性的新增。

    const getBlogs = async (req, res) => {
    	const blogs = await BlogsModel.getBlogs();
    	const newBlogs = blogs;
    	blogs[0].bg = "#000";
    	console.log('新增后:',blogs);
    };
    //新增后: 
    {
    	_id: new ObjectId("630daa261e82e63d97d073e7"),
    	title: 'fd',
    	browse: 0,
    	state: false,
    	bg:'#000'
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    方案二:使用 lean 函数或属性

    lean函数的作用:转换mongoose查询结果类型,从 MongooseDocuments 转换为 JS Object,从而便于我们修改查询结果。

    如下例:在 model 中读取操作时调用 lean() 函数处理。将读取结果转换为 JS 对象。

    // model中的读取操作
    // 方式一
    const getBlogs = () => {
    	return BlogModel.find().lean();
    };
    //方式二
    const getBlogs = () => {
    	return BlogModel.find({},{},{lean:true});
    };
    // 处理拿到的数据
    const getBlogs = async (req, res) => {
    	const blogs = await BlogsModel.getBlogs();
    	blogs[0].bg = "#000";
    	console.log('新增后:',blogs);
    };
    //新增后: 
    {
    	_id: new ObjectId("630daa261e82e63d97d073e7"),
    	title: 'fd',
    	browse: 0,
    	state: false,
    	bg:'#000'
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    方案三:使用 toObject || toJSON 函数

    注意:

    本质上使用toObject和toJSON都是可以的,其实这两个方法一般来说是没区别的,而如果要讲究区别的话,那就是如果使用toJSON的话,对生成的对象用JSON.stringify,此时用的是这个对象本身的toJSON方法来序列化,而不是原生对象Object的方法。

    const getBlogs = async (req, res) => {
    	const blogs = await BlogsModel.getBlogs();
    // 	const newBlogs = blogs.pop().toJSON();
    	const newBlogs = blogs.pop().toObject();
    	newBlogs.bg = "#000";
    	console.log('新增后:',newBlogs);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    mongoose 实现模糊查询

    在开发我的博客项目时我需要根据输入的关键字去数据库中模糊查询文章标题中包含关键字的文章,mongoose 可以很好的完成这项工作。

    • $or :可以查询多个键值的任意给定值,只要满足其中一个就可返回,用于存在多个条件判定的情况下使用。
    • $regex:为模糊查询的字符串提供正则表达式功能。
    // 模糊查询所有文章
    const searchBlogs = searchValue => {
    	// 首先根据关键字实例一个不区分大小写的正则对象
    	let regexp = new RegExp(searchValue, 'i');
    	return BlogModel.find({
    		title: { $regex: regexp } ,
    	}).lean();
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    若有多个模糊查询条件,满足任何一个条件都会返回该条数据。

    // 模糊查询所有文章
    const searchBlogs = (key1,key2) => {
    	// 首先根据关键字实例一个不区分大小写的正则对象
    	let regexp1 = new RegExp(key1, 'i');
    	let regexp2 = new RegExp(key2, 'i');
    	return BlogModel.find({
    		$or:[
    			{title: { $regex: regexp1 }},
    			{author: { $regex: regexp2 }},
    		] ,
    	}).lean();
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    根据标签获取文章数据

    场景是:可以给文章添加多个标签,这些标签的 id 保存在文章 tags 数组中,现在我需要根据某一标签获取所有添加了该标签的文章。这里主要是利用了 mongoose 提供的 i n , in , in,in相当于包含、等于,查询时查找包含于指定字段条件的数据。

    const getBlogsOfTag = searchTag => {
    	return BlogModel.find({ tags: { $in: searchTag } }).lean();
    };
    
    • 1
    • 2
    • 3

    给深层嵌套数组增加数据

    有如下数据结构

    {
        "_id" : ObjectId("62fe0d40b4a29bcb24adb751"),
        "favour" : [],
        "replyInfo" : [
    		{
    			"_id" : ObjectId("62fe0d40b4a29bcb24adb752"),
    			"favour" : [],
    			...
    		},
    		{
    			"_id" : ObjectId("62fe0d40b4a29bcb24adb753"),
    			"favour" : [],
    			...
    		}
    	],
    		...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    可以看到在最外层数据中有一个 replyInfo 字段,他是一个数组,数组元素是一个个对象,同时在一个 mongodb 集合中可能有多个最外层这样的数据。我现在需要先找到一个这样的数据对象,再从其 replyInfo 数组中找到一个符合目标的对象后向其 favour 数组中添加一个数据。下面的代码就能满足这样的需求。

    • $elemMatch:它匹配包含至少一个或一个元素的数组字段的文档,该元素将匹配所有指定的查询条件。
    • $:当前匹配到的那一条数据。
    const addSecondFavour = async (_id, replyId, favourMurmur) => {
    	return CommentModel.updateOne({ _id, replyInfo: { $elemMatch: { _id: replyId } } }, { $push: { 'replyInfo.$.favour': favourMurmur } });
    };
    
    • 1
    • 2
    • 3

    分页

    mongodb 也可以实现分页查询的效果,借助 skip 和 limit 函数。

    • skip(num):传给它一个参数 num ,表示跳过 num 条数据。
    • limit(num):传给它一个参数 num ,表示只返回 num 条数据。
    const getPublishBlogs = (pageStart = 0, pageSize = 5) => {
    	return BlogModel.find({ state: true }).skip(pageStart).limit(pageSize).lean();
    };
    
    • 1
    • 2
    • 3

    参考文章:

    关于mongoose返回的数据无法修改的原因以及解决方法

    本篇博客就到这了,我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎你的关注。

  • 相关阅读:
    python数据类型
    Linux 新建log文件
    数据库update、delete误操作,使用binlog2sql手动回退
    nordic平台SDK包下载地址
    三维卡通数字人解决方案,支持unity和Maya
    不可不知的4个搜索技巧——你真的会“百度一下”么?
    智能计量系统配套设备有哪些
    NodeJs内置模块child_process
    CentOS8安装部署DzzOffice协同办公平台
    JAVA Swing + Jdbc 实现宿舍管理系统
  • 原文地址:https://blog.csdn.net/weixin_46015333/article/details/127406666