متن خبر

چگونه با استفاده از طرحواره های Mongoose کد تمیزتر بنویسیم

چگونه با استفاده از طرحواره های Mongoose کد تمیزتر بنویسیم

شناسهٔ خبر: 735167 -




اگر به ساخت برنامه های NodeJS با استفاده از Mongoose ORM عادت دارید، این مقاله برای شما مناسب است. در آن، برخی از آپشن های جالب طرحواره‌های Mongoose را مورد بحث قرار می‌دهیم که به شما کمک می‌کند کدهای سازمان‌یافته‌تر و قابل نگهداری‌تری بنویسید.

برای استفاده بیشتر از این راهنما، باید پیشینه ای در جاوا اسکریپت داشته باشید، نحوه عملکرد Mongoose را بدانید و اصول برنامه نویسی شی گرا را بدانید.

در اینجا چیزی است که ما پوشش خواهیم داد:

    طرحواره Mongoose چیست؟

    تبعیض کننده

    استاتیک

    روش ها

    Query Builder

    قلاب

    خلاصه

طرحواره Mongoose چیست؟

طرحواره های Mongoose روشی ساختاریافته برای مدل سازی داده ها در پایگاه داده MongoDB ارائه می دهند که به شما امکان می دهد خصوصیات و رفتار اسناد را تعریف کنید. طرحواره ها به عنوان طرحی برای سندی که در پایگاه داده ذخیره می شود عمل می کنند. آنها توسعه دهندگان را قادر می سازند یکپارچگی داده ها را اعمال کنند و با MongoDB به شیوه ای شهودی و سازماندهی شده کار کنند.

در یک مجموعه MongoDB، یک طرحواره فیلدهای اسناد، انواع داده‌ها، قوانین اعتبارسنجی، مقادیر پیش‌فرض، محدودیت‌ها و موارد دیگر را مشخص می‌کند.

از نظر برنامه ریزی، طرحواره Mongoose یک شی جاوا اسکریپت است. در واقع، این یک نمونه از یک کلاس داخلی به نام Schema در ماژول mongoose است. به همین دلیل می توانید روش های بیشتری را به نمونه اولیه آن اضافه کنید. این به شما کمک می کند بسیاری از ویژگی ها مانند میان افزار، روش ها، استاتیک و موارد دیگر را پیاده سازی کنید. در این آموزش با برخی از آنها آشنا خواهید شد.

ویژگی هایی که می آموزید چگونه پیاده سازی کنید:

تبعیض کننده

استاتیک

روش ها

Query Builder

قلاب

تبعیض کننده

تمایزگر ویژگی است که شما را قادر می سازد چندین مدل (زیرگروه) ایجاد کنید که از یک مدل پایه (والد) به ارث می برند. این امر با تعریف یک طرحواره پایه و سپس گسترش آن با فیلدهای اضافی خاص برای هر زیرگروه یا هر طرحواره فرزند اتفاق می افتد.

تمام اسناد، صرف نظر از مدل خاص آنها، در یک مجموعه MongoDB ذخیره می شوند. این کار داده های شما را در یک مجموعه واحد سازماندهی می کند و در عین حال امکان پرس و جو و مدیریت داده ها را فراهم می کند. همچنین، هر سند شامل یک فیلد خاص است که نوع مدل خاص خود را نشان می‌دهد و به Mongoose اجازه می‌دهد بین زیرگروه‌های مختلف تمایز قائل شود.

نحوه استفاده از discriminator :

    با تعریف یک طرحواره پایه شروع کنید، که دارای فیلدهای مشترک بین زیرگروه ها خواهد بود. پس از آن، یک مدل از آن ایجاد کنید.

     import mongoose from 'mongoose' ; const baseSchema = new mongoose.Schema({ name : { type : String , required : true }, }, { discriminatorKey : 'kind' }; // defaults to '__t'); const BaseModel = mongoose.model( 'Base' , baseSchema);

    زیرشاخه هایی را ایجاد کنید که طرحواره پایه را با تعریف discriminator برای هر یک گسترش دهند.

     const catSchema = new mongoose.Schema({ meow : { type : Boolean , default : true } }); // subtype const Cat = BaseModel.discriminator( 'Cat' , catSchema); const dogSchema = new mongoose.Schema({ bark : { type : Boolean , default : true } }); // subtype const Dog = BaseModel.discriminator( 'Dog' , dogSchema);

    سپس می توانید اسناد را به روش معمولی ایجاد کنید. همه اسناد در یک مجموعه ذخیره می شوند، اما هر کدام بسته به مدل فرعی خود، نوع خاص خود را دارند.

     const fluffy = await Cat.create({ name : 'Fluffy' }); const rover = await Dog.create({ name : 'Rover' });

مورد استفاده discriminator :

بیایید بگوییم که شما در حال ساختن یک برنامه وب چند کاربره تجارت الکترونیک هستید که سه نقش اصلی کاربر را در خود جای می دهد: مدیران ، مشتریان و فروشندگان . هر یک از این نقش ها نقش مهمی در اکوسیستم خرید آنلاین ایفا می کند.

اگر سعی کنید برای هر نقش یک کلاس بسازید، متوجه می شوید که هر سه فیلدها و متدهای مشترکی دارند. ممکن است تصمیم بگیرید که یک طرحواره والدین (کاربر) و برخی دیگر از طرحواره های فرزند (مشتری، فروشنده، مدیر) ایجاد کنید که از آن به ارث می برند.

برای رسیدن به این هدف می توانید از discriminator استفاده کنید.

در فایل user.model.js کد زیر را اضافه کنید:

 import mongoose from "mongoose" ; const userSchema = mongoose.Schema( { name : String , profilePic : String , email : String , password : String , birthDate : Date , accountAcctivated : { type : Boolean , default : false }, }, { timestamps : true , discriminatorKey : "role" , } ); const User = mongoose.model( "User" , userSchema); export default User;

اکنون شما مدل پایه ( User ) را دارید که زیرگروه های دیگر از آن به ارث می برند. در این طرح والد، شما فیلدهای مشترکی را تعریف می کنید که همه کاربران صرف نظر از نقششان به اشتراک بگذارند.

در فایل client.model.js شما:

 import mongoose from "mongoose" ; import User from "./user.model.js" ; const clientSchema = mongoose.Schema( { products : Array , address : String , phone : String , } ); const Client = User.discriminator( "Client" , clientSchema); export default Client;

در فایل seller.model.js شما:

 import mongoose from "mongoose" ; import User from "./user.model.js" ; export const sellerSchema = mongoose.Schema( { rating : Number , businessType : { type : String , enum : [ "individual" , "corporation" ] }, } ); const Seller = User.discriminator( "Seller" , sellerSchema); export default Seller;

در فایل admin.model.js شما:

 import mongoose from "mongoose" ; import User from "./user.model.js" ; export const adminSchema = mongoose.Schema( { permissions : Array , assignedTasks : Array , department : String , } ); const Admin = User.discriminator( "Admin" , adminSchema); export default Admin;

زیرشاخه ها یا فرزندان Client ، Seller و Admin خواهند بود. در هر طرح زیرمجموعه، شما باید هر گونه فیلد یا رفتار اضافی را فقط به این نوع فرعی اضافه کنید. با ایجاد مدل فرزند با استفاده از تفکیک کننده، مدل فرزند تمام فیلدها و روش های User مدل والد خود را به ارث می برد.

پس کد قبلی یک مجموعه user در پایگاه داده ایجاد می کند که هر سند دارای یک فیلد role یا Client یا Seller یا Admin است. همه اسناد اکنون فیلدهای والد ( user ) را به اشتراک می گذارند و بسته به role هر سند، هر کدام یک فیلد اضافی دیگر دارد.

اگرچه تمام اسناد در یک مجموعه ذخیره می‌شوند، مدل‌ها هنگام کدنویسی کاملاً از هم جدا می‌شوند. این به چه معناست؟

به عنوان مثال، اگر نیاز به بازیابی همه مشتریان از مجموعه User دارید، باید Client.find({}) را بنویسید. این عبارت از کلید تفکیک کننده برای یافتن تمام اسنادی که role آنها Client است استفاده می کند. به این ترتیب، هر عملیات یا درخواستی که به یکی از مدل‌های فرزند اشاره دارد، همچنان جدا از مدل والد نوشته می‌شود.

توجه: قبل از ورود به بخش‌های بعدی، فقط به خاطر داشته باشید که هر گونه استاتیک، متد، سازنده پرس و جو یا قلاب باید قبل از ایجاد خود مدل تعریف شود (یعنی قبل از const User = mongoose.model("User", userSchema); ).

استاتیک

استاتیک برای تعریف توابعی که در سطح مدل عمل می کنند مفید است. آنها به شما اجازه می دهند تا توابع قابل استفاده مجدد را برای عملیات مربوط به کل مدل تعریف کنید. آنها به کپسوله کردن منطقی کمک می کنند که در مدل به جای اسناد جداگانه اعمال می شود و کد شما را تمیزتر، سازماندهی شده تر و قابل نگهداری تر می کند.

روش‌هایی مانند find ، findOne ، findById و روش‌های دیگر همگی متدهایی هستند که به مدل متصل هستند. با استفاده از خاصیت statics طرحواره های Mongoose، می توانید روش مدل خود را بسازید.

استاتیک قدرتمند است. با استفاده از آنها، می توانید پرس و جوهای پیچیده ای را کپسوله کنید که ممکن است بخواهید دوباره از آنها استفاده کنید. همچنین، می‌توانید برای عملیات‌هایی که داده‌ها را اصلاح یا جمع‌آوری می‌کنند، استاتیک ایجاد کنید، مانند شمارش اسناد یا یافتن اسناد بر اساس معیارهای خاص.

مورد استفاده statics

استاتیک به راحتی ساخته می شود. شما با استفاده از شی statics یک متد استاتیک را روی طرحواره خود تعریف می کنید.

در فایل user.model.js خود، این متدهای ثابت، countUsers و findByEmail را اضافه کنید:

 // model method userSchema.statics.countUsers = function ( ) { return this .countDocuments({}); }; // model method userSchema.statics.findByEmail = async function ( email ) { return await this .findOne({ email }); };

در داخل هر روش ایستا، this به خود مدل اشاره دارد. در این مثال، this در this.findOne({ email }) به مدل User اشاره دارد.

مثال استفاده:

 const user = await User.findByEmail( "foo@bar.com" ); //or const client = await Client.findByEmail( "foo@bar.com" ); //or const seller = await Seller.findByEmail( "foo@bar.com" ); //or const admin = await Admin.findByEmail( "foo@bar.com" );

وقتی متد static را روی مدل خود فراخوانی می کنید، متد فراخوانی می شود و مدلی که استاتیک را روی آن فراخوانی کرده اید this می شود. این خط یک پرس و جو را برای یافتن یک سند در مجموعه MongoDB انجام می دهد که در آن فیلد email با آرگومان email ارائه شده مطابقت دارد.

روش ها

متدها توابعی هستند که می‌توانید روی یک طرحواره تعریف کنید و می‌توان آن‌ها را بر روی نمونه‌هایی از اسناد ایجاد شده از این طرح فراخوانی کرد. آنها به کپسوله کردن منطق در خود سند کمک می کنند و کد شما را تمیزتر و ماژولارتر می کنند.

با استفاده از روش‌های نمونه، می‌توانید به راحتی با داده‌های مرتبط با اسناد خاص تعامل داشته باشید و آنها را دستکاری کنید.

methods مورد استفاده

می‌توانید با استفاده از شی methods ، متدها را روی طرحواره تعریف کنید.

در فایل user.model.js خود، یک روش سند اضافه کنید که از طریق آن می توانید رمز عبور یک کاربر را تحلیل کنید:

 // instance or document method userSchema.methods.getProfile = function ( ) { return ` ${ this .name} ( ${ this .email} )` ; }; // instance or document method userSchema.methods.checkPassword = function ( password ) { return password === this .password ? true : false ; };

در داخل هر روش سند، this به خود سند اشاره دارد. در این مثال، this در this.password به سند user اشاره می کند که در آن متد فراخوانی می شود. یعنی شما می توانید به تمام فیلدهای این سند دسترسی داشته باشید. این بسیار ارزشمند است زیرا می توانید هر چیزی را که مربوط به این سند است بازیابی، اصلاح و تحلیل کنید.

مثال استفاده:

 const client = await Client.findById(...) client.checkPassword( "12345" ) //or const seller = await Seller.findById(...) seller.checkPassword( "12345" ) //or const admin = await Admin.findById(...) admin.checkPassword( "12345" )

از آنجایی که متدها توابع سطح نمونه هستند، روی اسناد فراخوانی می شوند. await Client.findById(...) سندی را برمی گرداند که دارای تمام متدهای داخلی و همچنین روش های از پیش تعریف شده شما checkPassword و getProfile . پس با فراخوانی، برای مثال client.checkPassword("12345") ، this کلمه کلیدی در تعریف تابع checkPassword با سند client جایگزین می شود. این به نوبه خود رمز عبور کاربر را با رمز عبور ذخیره شده قبلی در پایگاه داده مقایسه می کند.

Query Builder

سازنده پرس و جو در Mongoose یک روش سفارشی است که می توانید آن را روی شی پرس و جو تعریف کنید تا الگوهای پرس و جوی رایج را ساده و محصور کنید. این سازندگان پرس و جو به شما امکان می دهند منطق پرس و جو قابل استفاده مجدد و خواندنی ایجاد کنید و کار با داده های خود را آسان تر می کند.

یکی از با ارزش ترین کاربردهای سازندگان پرس و جو، زنجیره سازی است. آنها را می توان با سایر سازنده های پرس و جو که ساخته اید یا با روش های پرس و جوی استاندارد مانند پیدا کردن، مرتب سازی و غیره زنجیره کرد.

مورد استفاده سازنده پرس و جو

شما سازنده های پرس و جو را با گفت ن آنها به ویژگی query یک طرحواره Mongoose تعریف می کنید.

در فایل user.model.js خود، یک روش کمکی پرس و جو اضافه کنید که به شما امکان می دهد صفحه بندی را پیاده سازی کنید.

 // query helper userSchema.query.paginate = function ( { page, limit } ) { // some code const skip = limit * (page - 1 ); return this .skip(skip).limit(limit); };

برای پیاده سازی صفحه بندی، به دو متغیر مهم نیاز دارید: اول، شماره صفحه، و دوم، تعداد مواردی که در هر صفحه بازیابی می کنید.

برای پرس و جو از پایگاه داده برای تعداد خاصی از اسناد، شما همیشه از روش های جستجوی داخلی skip و limit در mongoose استفاده می کنید. skip برای تنظیم مکان نما بعد از تعداد معینی از اسناد استفاده می شود و پس از آن پرس و جو اجرا می شود. limit برای بازیابی تعداد خاصی از اسناد استفاده می شود.

در داخل هر روش سازنده پرس و جو، this به خود کوئری اشاره دارد. و از آنجایی که سازندگان پرس و جو زنجیره ای هستند، می توانید هر یک از آنها را پس از یکدیگر فراخوانی کنید.

در نهایت، هر متد سازنده پرس و جو باید یک mongoose query object را برگرداند، به همین دلیل است که باید return this.skip(skip).limit(limit) بنویسید.

مثال استفاده:

 const results = await Client.find().paginate({ page : 2 , limit : 5 }); //or const results = await Seller.find().paginate({ page : 2 , limit : 5 }); //or const results = await Admin.find().paginate({ page : 2 , limit : 5 });

سپس می‌توانید آن را در هر پرس و جوی فراخوانی کنید و await Client.find().paginate({ page: 2, limit: 5 }) تابع paginate را فراخوانی می‌کند و this کلمه کلیدی را با Client.find() با استفاده از سازنده query جایگزین می‌کند.

می‌توانید صفحه‌بندی را با شرایط خاصی پیاده‌سازی کنید، اما همیشه skip و limit فراخوانی می‌کنید. با تعریف سازنده پرس و جو paginate ، خود را تکرار نخواهید کرد و می توانید منطق را در یک تابع واحد کپسوله کنید.

قلاب

هوک ها (همچنین به عنوان میان افزار نیز شناخته می شوند) توابعی هستند که در نقاط خاصی از چرخه عمر یک سند اجرا می شوند. آنها به شما اجازه می دهند رفتار سفارشی را قبل یا بعد از عملیات خاصی مانند ذخیره، به روز رسانی یا حذف اسناد اضافه کنید.

انواع قلاب

Pre Hooks: قبل از عملیات اجرا می شود.

Post Hooks: بعد از یک عملیات اجرا می شود.

مورد استفاده قلاب

در فایل user.model.js خود، یک میان افزار ذخیره post اضافه کنید که از طریق آن می توانید پس از ذخیره سند کاربر در پایگاه داده، ایمیلی برای فعال سازی حساب ارسال کنید.

 // post hook userSchema.post( "save" , async function ( doc, next ) { // send email logic // if succeeded return next(); // if failed return next( new Error ( "Failed to send email!" )); });

پس از ایجاد یک کاربر از طریق model.create() یا هر زمانی که متد save() در سند کاربر فراخوانی کردید، تابع callback فراخوانی می شود.

در این مثال، اگر لازم است از ارسال ایمیل در ذخیره خودداری کنید، باید یک شرط بنویسید تا مطمئن شوید که این save فقط برای یک کاربر جدید است. می توانید چیزی شبیه if (doc.createdAt.getTime() === doc.updatedAt.getTime()) بنویسید.

خلاصه

در این مرور کلی از آپشن های Mongoose، ما چهار مفهوم کلیدی را تحلیل کرده‌ایم: تشخیص‌دهنده‌ها، استاتیک، روش‌ها و قلاب‌ها.

تفکیک‌کننده‌ها به شما اجازه می‌دهند چندین مدل ایجاد کنید که یک طرح مشترک را به اشتراک می‌گذارند که انواع مختلف اسناد را قادر می‌سازد در یک مجموعه واحد ذخیره شوند. این امر مدیریت داده ها و پرس و جو را تسهیل می کند.

استاتیک روش‌هایی در سطح مدل هستند که قابلیت‌های قابل استفاده مجدد را برای کل مدل ارائه می‌کنند. آنها پرس و جوهای پیچیده و منطق دستکاری داده ها را کپسوله می کنند و به تمیز نگه داشتن پایگاه کد شما کمک می کنند.

متدها توابع سطح نمونه هستند که بر روی نمونه های سند جداگانه عمل می کنند. آنها رفتارهای سفارشی و دستکاری داده ها را برای هر سند امکان پذیر می کنند، پس می توانید داده های سند را به روشی خاص تغییر دهید، مانند قالب بندی یا محاسبه مقادیر بر اساس فیلدهای آن.

قلاب‌ها (یا میان‌افزار) به شما امکان می‌دهند تا عملکردها را در نقاط خاصی از چرخه عمر سند اجرا کنید، مانند قبل یا بعد از ذخیره، به‌روزرسانی یا حذف یک سند. این برای پیاده سازی اعتبارسنجی، ورود به سیستم یا هر گونه عوارض جانبی دیگر مربوط به عملیات پایگاه داده مفید است.

این ویژگی ها با هم، تطبیق پذیری و سازماندهی مدل های Mongoose شما را افزایش می دهند و ساخت برنامه های قوی و قابل نگهداری با MongoDB را آسان تر می کنند.

در اینجا می توانید یک مخزن پیدا کنید که در آن می توانید درباره طرحواره های Mongoose و موارد استفاده بیشتر بیاموزید.

خبرکاو

ارسال نظر




تبليغات ايهنا تبليغات ايهنا

تمامی حقوق مادی و معنوی این سایت متعلق به خبرکاو است و استفاده از مطالب با ذکر منبع بلامانع است