当前位置: 首页 > 后端技术 > Node.js

使用MongoDB+Mongoose和Node.js进行后端开发的最佳实践

时间:2023-04-03 13:04:03 Node.js

MongoDB无疑是当今最流行的NoSQL数据库选择之一,拥有强大的社区和生态系统。在本文中,我们将介绍在使用Node.js设置MongoDB和Mongoose时应遵循的一些最佳实践。1、为什么需要猫鼬?为了理解我们为什么需要Mongoose,我们先来了解一下MongoDB(也是一个数据库)在架构层面是如何工作的。你有一个数据库服务器(例如MongoDB社区服务器)你正在运行一个Node.js脚本(作为一个进程)MongoDB服务器监听一个TCP套接字(通常)并且你的Node.js进程可以使用TCP连接连接到它。但是在TCP之上,MongoDB也有自己的协议来确切地知道客户端(我们的Node.js进程)想要数据库做什么。对于这种通信,我们不需要在TCP层去学习我们要发送的消息,而是用一个“驱动”软件抽象出来,这里称之为MongoDB驱动。MongoDB驱动程序在此处作为npm包提供。现在请记住,MongoDB驱动程序负责连接和抽象您的低级通信请求/响应——但作为开发人员,这只能让您到此为止。因为MongoDB是一个无模式数据库,所以它为您提供了比初学者需要的更多的功能。更强大的功能意味着更大的错误可能性,您需要减少代码中出现错误和损坏表格的可能性。Mongoose是对原生MongoDB驱动程序(我上面提到的npm包)的抽象。抽象的一般经验法则(我的理解方式)是,每次抽象都会失去一些底层的操作能力。但这并不一定意味着它不好,有时它可以将生产率提高1000倍以上,因为您根本不需要完全访问底层API。一个好主意是您在技术上使用C和Python创建一个实时聊天应用程序。作为开发人员,Python示例将更容易、更快速地实施,而且效率更高。C可能更高效,但它在生产力、开发速度、错误和崩溃方面付出了巨大的代价。此外,在大多数情况下,您不需要拥有C为您提供的实现websockets的功能。同样,使用Mongoose,您可以限制低级API访问的范围,但可以释放很多潜在收益和良好的DX。2.如何连接Mongoose+MongoDB首先,让我们快速了解一下2020年如何使用Mongoose连接您的MongoDB数据库。})这种连接格式确保您使用的是Mongoose的新URL解析器,并且您没有使用任何已弃用的做法。3.如何执行Mongoose操作现在让我们快速讨论一下Mongoose操作以及您应该如何执行它们。Mongoose为您提供以下两种选择:Cursor-basedqueryFullfetchingquery3.1Cursor-basedqueryCursor-basedquery是指一次只处理一条记录从数据库中获取一个或一批文档。这是在有限内存环境中处理大量数据的有效方式。想象一下,你想在1GB/1核的云服务器上解析总大小为10GB的文档。您无法获取整个集合,因为这在您的系统中不合适。游标是一个不错的(也是唯一的)选择。3.2Fullfetchingquery这是查询的一种,一次可以得到查询的所有响应。在大多数情况下,这是您要使用的方法。因此,我们这里主要关注这个方法。4.如何使用Mongoose模型(Models)模型是Mongoose的超强能力,它们帮助你执行“schema”规则,并为你的Node代码无缝集成到数据库调用中。第一步是定义一个好的模型:importmongoosefrom'mongoose'constCompletedSchema=newmongoose.Schema({type:{type:String,enum:['course','classroom'],required:true},parentslug:{type:String,required:true},slug:{type:String,required:true},userid:{type:String,required:true}},{collection:'completed'})CompletedSchema.index({slug:1,userid:1},{unique:true})constmodel=mongoose.model('Completed',CompletedSchema)exportdefaultmodel这是一个直接从codedamn的代码库中截取的示例。以下是您应该注意的一些有趣的事情。尽量保持required:true在所有必填字段中。如果您不使用像TypeScript这样的静态类型检查系统来帮助您在创建对象时正确检查属性名称,这可以为您省去很多麻烦。此外,免费验证非常酷。定义索引和唯一字段。也可以在模式中添加唯一属性。索引是一个广泛的话题,所以我不会在这里深入。但是在大范围内,它们确实可以帮助您加快很多查询的速度。显式定义集合名称。虽然Mongoose可以根据模型的名称自动给出一个集合名称(例如Completedhere),但在我看来这太抽象了。您至少应该知道代码库中的数据库名称和集合。如果可以,请使用枚举来限制值。5、如何进行CRUD操作CRUD指的是创建、读取、更新和删除。这些是您可以在数据库中执行任何形式的数据操作的四个基本选项。让我们快速看一下这些操作的一些示例。5.1创建这只是在数据库中创建一条新记录。让我们使用上面定义的模型创建一条记录。try{constres=awaitCompletedSchema.create(record)}catch(error){console.error(error)//处理错误}同样,这里有一些提示:使用async-await而不是回调(看起来不错,但不是突破性的性能优势)在查询周围使用try-catch块,因为您的查询可能由于某些原因(重复记录、错误值等)而失败。5.2读取表示从数据库中读取已有的值。听起来很简单,但您应该对Mongoose有所了解。constres=awaitCompletedSchema.find(info).lean()你能看到那里的lean()函数调用吗?它对性能非常有用。默认情况下,Mongoose将处理从数据库返回的文档并在其上添加它的魔术方法(例如.save)。当您使用.lean()时,Mongoose返回纯JSON对象而不是内存和资源繁重的文档。使查询更快并消耗更少的CPU。但是,如果你确实想更新数据,你可以省略.lean()(我们接下来会看到)5.3更新如果你已经有一个Mongoose文档(不是用.lean()触发的),你可以简单地去修改对象属性,然后使用object.save()保存它。constdoc=awaitCompletedSchema.findOne(info)doc.slug='something-else'awaitdoc.save()请记住,这里有两个数据库调用。第一个在findOne上,第二个在doc.save上。如果可以的话,你应该总是减少对数据库的请求数量(因为如果你比较内存、网络和磁盘,网络几乎总是最慢的)。在另一种情况下,您可以使用这样的查询:constres=awaitCompletedSchema.updateOne(,).lean()并且只调用一次数据库。5.4Delete使用Mongoose删除也很简单,让我们看看如何删除单个文档:constres=awaitCompletedSchema.deleteOne()同updateOnedeleteOne也接受第一个参数作为文档的匹配条件。还有另一种称为deleteMany的方法,仅当您知道要删除多个文档时才应使用该方法。在任何其他情况下,始终使用deleteOne以避免意外的多次删除,尤其是当您尝试自己执行查询时。