Как да използваме MongoDB + Mongoose с Node.js - Най-добри практики за Back End Devs

MongoDB несъмнено е един от най-популярните избори за база данни NoSQL днес. И има страхотна общност и екосистема.

В тази статия ще разгледаме някои от най-добрите практики, които да следвате, когато настройвате MongoDB и Mongoose с Node.js.

Предварителни условия за тази статия

Тази статия е една от основните обучителни пътеки на частта codedamn, където започваме от основните основи и ги обхващаме в детайли. Затова предполагам, че вече имате известен опит с JavaScript (и Node.js).

В момента сме тук:

Ако имате много малко опит с Node.js / JavaScript или задната част като цяло, това вероятно е добро място за начало. Можете също така да намерите безплатен курс за Mongoose + MongoDB + Node.js тук. Да се ​​потопим.

Защо ти трябва Mongoose?

За да разберем защо се нуждаем от Mongoose, нека разберем как MongoDB (и база данни) работи на ниво архитектура.

  • Имате сървър на база данни (например MongoDB сървър на общността)
  • Имате изпълнен скрипт Node.js (като процес)

MongoDB сървърът слуша TCP сокет (обикновено) и вашият Node.js процес може да се свърже с него чрез TCP връзка.

Но на върха на TCP, MongoDB има и собствен протокол за разбиране какво точно клиентът (нашият процес Node.js) иска базата данни да прави.

За тази комуникация, вместо да научим съобщенията, които трябва да изпращаме на TCP слоя, ние ги абстрахираме с помощта на софтуер "драйвер", наречен в този случай драйвер MongoDB. Драйверът MongoDB е достъпен тук като npm пакет.

Сега не забравяйте, че драйверът на MongoDB е отговорен за свързването и абстрахирането от вас на искане / отговори за комуникация на ниско ниво - но това стига само до вас като разработчик.

Тъй като MongoDB е база данни без схема, тя ви дава много повече енергия, отколкото ви е необходима като начинаещ. Повече мощност означава повече повърхност, за да се объркат нещата. Трябва да намалите повърхността на грешките и грешките, които можете да направите в кода си. Имате нужда от нещо повече.

Запознайте се с Мангуста. Mongoose е абстракция над родния драйвер на MongoDB (npm пакетът, който споменах по-горе).

Общото правило на абстракциите (както аз разбирам) е, че с всяка абстракция губите част от оперативната мощност на ниско ниво. Но това не означава непременно, че е лошо. Понякога това повишава производителността 1000x +, защото така или иначе никога не е необходимо да имате пълен достъп до основния API.

Един добър начин да помислите за това е, че технически създавате приложение за чат в реално време както в C, така и в Python.

Примерът на Python ще бъде много по-лесен и по-бърз за вас като разработчик да внедрите с по-висока производителност.

C може да е по-ефективен, но ще има огромни разходи за производителност / скорост на развитие / грешки / сривове. Освен това, в по-голямата си част не е необходимо да имате мощност C, която ви дава да внедрите уебсайтове.

По същия начин, с Mongoose, можете да ограничите повърхността си от по-ниско ниво на достъп до API, но да отключите много потенциални печалби и добър DX.

Как да свържете Mongoose + MongoDB

Първо, нека бързо да видим как трябва да се свържете с вашата база данни MongoDB през 2020 г. с Mongoose:

mongoose.connect(DB_CONNECTION_STRING, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, useFindAndModify: false })

Този формат за връзка гарантира, че използвате новия анализатор на URL адреси от Mongoose и че не използвате никакви остарели практики. Можете да прочетете подробно за всички тези съобщения за оттегляне тук, ако искате.

Как се извършват операции с мангуста

Нека сега да продължим и бързо да обсъдим операциите с Mongoose и как трябва да ги извършите.

Mongoose ви дава възможности за две неща:

  1. Заявки, базирани на курсора
  2. Заявка за пълно извличане

Заявки, базирани на курсора

Заявките, базирани на курсора, означават, че работите с по един запис наведнъж, докато извличате по един или пакет документи в даден момент от базата данни. Това е ефективен начин за работа с огромни количества данни в ограничена среда на паметта.

Представете си, че трябва да анализирате документи с общ размер от 10 GB на 1GB / 1core облачен сървър. Не можете да изтеглите цялата колекция, защото това няма да се побере във вашата система. Курсорът е добра (и единствената?) Опция тук.

Пълно извличане на заявки

Това е видът на заявката, при която получавате пълния отговор на вашата заявка наведнъж. В по-голямата си част това ще използвате. Затова тук ще се фокусираме най-вече върху този метод.

Как да използваме модели Mongoose

Моделите са суперсила на Mongoose. Те ви помагат да наложите правилата за "схема" и осигуряват безпроблемна интеграция на вашия код на Node в извиквания на база данни.

Първата стъпка е да се определи добър модел:

import mongoose from 'mongoose' const CompletedSchema = new mongoose.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 }) const model = mongoose.model('Completed', CompletedSchema) export default model 

Това е един подрязан пример директно от кодовата база на codedamn. Няколко интересни неща, които трябва да отбележите тук:

  1. Опитайте се да запазите required: trueвсички полета, които са задължителни. Това може да ви спести огромна болка, ако не използвате статична система за проверка на типа като TypeScript, за да ви помогне с правилните имена на свойства, докато създавате обект. Плюс това безплатната проверка също е супер готина.
  2. Дефинирайте индекси и уникални полета. uniqueсвойство може да се добави и в схема. Индексите са широка тема, така че няма да навлизам в дълбочина тук. Но в голям мащаб те наистина могат да ви помогнат да ускорите много запитванията си.
  3. Определете изрично името на колекция. Въпреки че Mongoose може автоматично да даде име на колекция въз основа на името на модела ( Completedтук например), това е твърде много абстракция според мен. Трябва да знаете поне за имената на вашите бази данни и колекции във вашата кодова база.
  4. Ограничете стойностите, ако можете, като използвате изброявания.

Как се извършват CRUD операции

CRUD означава C reate, R ead, U pdate и D elete. Това са четирите основни опции, с които можете да извършвате всякакъв вид манипулация на данни в база данни. Нека бързо да видим някои примери за тези операции.

Операцията Създаване

Това просто означава създаване на нов запис в база данни. Нека използваме модела, който дефинирахме по-горе, за да създадем запис:

try { const res = await CompletedSchema.create(record) } catch(error) { console.error(error) // handle the error }

Отново няколко указателя тук:

  1. Използвайте async-await вместо обратни обаждания (приятно за очите, без новаторска полза от производителността като такава)
  2. Използвайте блокове try-catch около заявки, защото заявката ви може да се провали поради редица причини (дублиран запис, неправилна стойност и т.н.)

Операцията за четене

Това означава четене на съществуващи стойности от базата данни. просто е, както звучи, но има няколко неща, които трябва да знаете при Mongoose:

const res = await CompletedSchema.find(info).lean()
  1. Можете ли да видите lean()повикването на функцията там? Той е супер полезен за изпълнение. По подразбиране Mongoose обработва върнатите документи от базата данни и добавя своите магически методи към тях (например .save)
  2. When you use .lean(), Mongoose returns plain JSON objects instead of memory and resource heavy documents. Makes queries faster and less expensive on your CPU, too.
  3. However, you can omit .lean() if you are actually thinking of updating data (we'll see that next)

The Update Operation

If you already have a Mongoose document with you (without firing with .lean()), you can simply go ahead and modify the object property, and save it using object.save():

const doc = await CompletedSchema.findOne(info) doc.slug = 'something-else' await doc.save()

Remember that here, there are two database calls made. The first one is on findOne and the second one is on doc.save.

If you can, you should always reduce the number of requests hitting the database (because if you're comparing memory, network, and disk, network is almost always the slowest).

In the other case, you can use a query like this:

const res = await CompletedSchema.updateOne(, ).lean()

and it will only make a single call to the database.

The Delete Operation

Delete is also straightforward with Mongoose. Let's see how you can delete a single document:

const res = await CompletedSchema.deleteOne()

Just like updateOne, deleteOne also accepts the first argument as the matching condition for the document.

There is also another method called deleteMany which should be used only when you know you want to delete multiple documents.

In any other case, always use deleteOne to avoid accidental multiple deletes, especially when you're trying to execute queries yourself.

Conclusion

This article was a simple introduction to the Mongoose and MongoDB world for Node.js developers.

If you enjoyed this article, you can step up your game even more as a developer by following the codedamn backend learning path. Please feel free to reach out to me on Twitter for any feedback!