برنامه نویسی

MongoDB Fundamentals – DEV Community

Summarize this content to 400 words in Persian Lang

معماری MongoDB

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

پایگاه داده: ظرفی برای مجموعه ها.

مجموعه: گروهی از اسناد MongoDB.

سند: مجموعه ای از جفت های کلید-مقدار (شبیه به اشیاء JSON).

عملیات CRUD

CRUD مخفف Create، Read، Update و Delete است. اینها عملیات اساسی برای تعامل با داده ها در MongoDB هستند.

ایجاد کنید

برای درج یک سند جدید در مجموعه، از insertOne() یا insertMany() روش ها

insertOne

db.collection(‘users’).insertOne({ name: ‘Alice’, age: 25 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

db.collection(‘users’).insertOne({ _id: 1, name: ‘Alice’, age: 25 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اطمینان حاصل کنید که به موارد تکراری رسیدگی کنید _id ارزش ها برای جلوگیری از تعارض

درج بسیاری

برای درج چندین سند، می توانید از insertMany() روش

db.collection(‘users’).insertMany([
{ name: ‘Alice’, age: 25 },
{ name: ‘Bob’, age: 30 }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر یکی از اسناد درج نشود، عملیات لغو می شود مگر اینکه شما آن را مشخص کنید ordered: false گزینه

به عنوان مثال اگر شما طرحواره وادیاسیون مانند این دارید. به Schema Validation بروید

db.createCollection(‘users’, {
validator: {
$jsonSchema: {
bsonType: ‘object’,
required: [‘name’, ‘age’],
properties: {
name: { bsonType: ‘string’ },
age: { bsonType: ‘int’ }
}
}
}
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

db.collection(‘users’).insertMany([
{ name: ‘Alice’, age: 25 },
{ name: ‘Bob’, age: 30 },
{ name: ‘Charlie’ } // missing age field
], { ordered: false });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سند سوم نادیده گرفته می شود و دو سند اول درج می شود.

نتیجه خواهد بود

{
acknowledged: true,
insertedIds: {
‘0’: ObjectId(“…”), // ID for Alice
‘1’: ObjectId(“…”) // ID for Bob
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بخوانید

برای خواندن اسناد از یک مجموعه، از find() روش متد find() چندین سند را بازیابی می کند و مکان نما را برمی گرداند که می تواند برای دسترسی به اسناد تکرار شود.

پیدا کردن

const cursor = db.collection(‘users’).find({ age: { $gte: 18 } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در Node.js می توانید مکان نما را با استفاده از آرایه تبدیل کنید toArray() روش

const docs = await cursor.toArray();
console.log(docs)

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه:

[
{ “_id”: 1, “name”: “Alice”, “age”: 25 },
{ “_id”: 2, “name”: “Bob”, “age”: 30 }
]

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

findOne

برای بازیابی یک سند، می توانید از findOne() روش

db.collection(‘users’).findOne({ name: ‘Alice’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه:

{ “_id”: 1, “name”: “Alice”, “age”: 25 }

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فرافکنی

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

db.collection(‘users’).find({}, { projection: { name: 1, age: 1 } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه:

[
{ “_id”: 1, “name”: “Alice”, “age”: 25 },
{ “_id”: 2, “name”: “Bob”, “age”: 30 }
]

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

به روز رسانی

برای به روز رسانی اسناد موجود، از updateOne() یا updateMany() روش ها

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $set: { age: 26 } });

db.collection(‘users’).updateMany({ city: ‘New York’ }, { $set: { city: ‘San Francisco’ } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

{
“acknowledged”: true,
“matchedCount”: 5,
“modifiedCount”: 5,
“upsertedId”: null,
“upsertedCount”: 0
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بر اساس شی نتیجه شما می توانید پاسخ مناسب را برگردانید.

const result = await db.collection(‘users’).updateOne(
{ _id: userId },
{ $set: { age: 30 } }
);

if (result.matchedCount === 0) {
return { success: false, message: ‘No matching document found’ };
}

if (result.modifiedCount === 0) {
return { success: true, message: ‘Document already up-to-date’ };
}

return { success: true, message: ‘Document updated successfully’ };

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ناراحت

اگر می‌خواهید سند جدیدی را در زمانی که هیچ سند منطبقی یافت نشد وارد کنید، می‌توانید از آن استفاده کنید upsert گزینه

db.collection(‘users’).updateOne(
{ name: ‘Alice’ },
{ $set: { age: 26 }
},
{ upsert: true }
);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

{
“acknowledged”: true,
“matchedCount”: 1,
“modifiedCount”: 1,
“upsertedId”: ObjectId(“…”),
“upsertedCount”: 1
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

FindOneAndUpdate

این روش ترکیبی از findOne() و updateOne() به عنوان عملیات اتمی عملیات اتمی عملیاتی است که به صورت یک واحد کار انجام می شود. این به جلوگیری از شرایط مسابقه و اطمینان از سازگاری داده ها کمک می کند.

// Update or create a user profile
db.users.findOneAndUpdate(
{ email: “alice@example.com” },
{ $set: { age: 26 } },
{ returnDocument: ‘after’ }
);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

returnDocument می تواند “قبل” یا “بعد” برای بازگرداندن سند قبل یا بعد از به روز رسانی باشد.

{
“value”: {
“_id”: ObjectId(“5f7d3b1c8e1f9a1c9c8e1f9a”),
“email”: “alice@example.com”,
“age”: 26,
“name”: “Alice”
},
“lastErrorObject”: {
“updatedExisting”: true,
“n”: 1
},
“ok”: 1
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی اپراتورها

// $set operator to update fields
db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $set: { age: 26, city: ‘New York’ } });

// $set operator to update nested fields
db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $set: { ‘address.city’: ‘New York’ } });

// $inc operator to increment a field
db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $inc: { age: 1 } });

// $mul operator to multiply a field value
db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $mul: { age: 2 } });

// $unset operator to remove a field
db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $unset: { city: ” } });

// $rename operator to rename a field
db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $rename: { city: ‘location’ } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف کنید

برای حذف اسناد از مجموعه، از deleteOne() یا deleteMany() روش ها

db.collection(‘users’).deleteOne({ name: ‘Alice’ }); // delete first matching document

db.collection(‘users’).deleteMany({ city: ‘New York’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

{
“acknowledged”: true,
“deletedCount”: 1
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بر اساس شی نتیجه شما می توانید پاسخ مناسب را برگردانید.

const result = await db.collection(‘users’).deleteOne({ _id: userId });

if (result.deletedCount === 0) {
return { success: false, message: ‘No matching document found’ };
}

return { success: true, message: ‘Document deleted successfully’ };

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

حذف نرم: به جای حذف دائمی اسناد، می توانید با افزودن a آنها را به عنوان حذف شده علامت گذاری کنید deletedAt زمینه این به شما امکان می دهد داده ها را برای اهداف ممیزی یا بازیابی نگهداری کنید.

db.collection(‘users’).updateOne(
{ _id: userId },
{ $set: { deletedAt: new Date() } }
);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فیلتر دقیق: هنگام حذف اسناد، از یک فیلتر دقیق استفاده کنید تا از حذف ناخواسته اسناد بیشتر از آنچه در نظر گرفته شده است جلوگیری کنید. می توانید از find() برای پیش نمایش اسنادی که قبل از اجرای عملیات حذف حذف می شوند استفاده کنید.

اعتبار سنجی طرحواره

مدل در مقابل الگوی DAO

نمونه همه مدل

الگوی مدل

داده ها و رفتار را در بر می گیرد. این ممکن است شامل اعتبار سنجی، منطق تجاری و عملیات پایگاه داده باشد. این می تواند به “چاق” اما اجرای ساده تر تبدیل شود.

این نمونه ای از ساختار فایل ها برای الگوی مدل است

src/
├── config/
│ └── database.js

├── models/
│ └── todo.model.js

├── controllers/
│ └── todo.controller.js

├── routes/
│ └── todo.routes.js

├── middleware/
│ └── auth.middleware.js

└── app.js

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

پیکربندی داده ها

// src/config/database.js
const { MongoClient } = require(‘mongodb’);

const dbConfig = {
url: process.env.MONGODB_URI || ‘mongodb://localhost:27017’,
dbName: ‘todoapp’
};

let db = null;

const connectDB = async () => {
try {
const client = await MongoClient.connect(dbConfig.url, {
useUnifiedTopology: true
});
db = client.db(dbConfig.dbName);
console.log(‘Connected to MongoDB successfully’);
return db;
} catch (error) {
console.error(‘MongoDB connection error:’, error);
process.exit(1);
}
};

const getDB = () => {
if (!db) {
throw new Error(‘Database not initialized’);
}
return db;
};

module.exports = { connectDB, getDB };

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مدل

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

// src/models/todo.model.js
onst { ObjectId } = require(‘mongodb’);
const { getDB } = require(‘../config/database’);

const COLLECTION_NAME = ‘todos’;

// Validation functions
const validateTodoData = (todoData) => {
const errors = [];

if (!todoData.title) {
errors.push(‘Title is required’);
} else if (todoData.title.length < 3) {
errors.push(‘Title must be at least 3 characters long’);
}

if (todoData.status && ![‘pending’, ‘in-progress’, ‘completed’].includes(todoData.status)) {
errors.push(‘Invalid status. Must be pending, in-progress, or completed’);
}

if (todoData.dueDate && new Date(todoData.dueDate) < new Date()) {
errors.push(‘Due date cannot be in the past’);
}

if (todoData.priority && ![‘low’, ‘medium’, ‘high’].includes(todoData.priority)) {
errors.push(‘Invalid priority. Must be low, medium, or high’);
}

return errors;
};

const todoModel = {
async create(todoData) {
const errors = validateTodoData(todoData);
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(‘, ‘)}`);
}

const db = getDB();
const todo = {
…todoData,
title: todoData.title.trim(),
status: todoData.status || ‘pending’,
priority: todoData.priority || ‘medium’,
createdAt: new Date(),
updatedAt: new Date(),
completedAt: null
};

const result = await db.collection(COLLECTION_NAME).insertOne(todo);
return { …todo, _id: result.insertedId };
},

async findById(id) {
if (!ObjectId.isValid(id)) {
throw new Error(‘Invalid todo ID’);
}

const db = getDB();
const todo = await db.collection(COLLECTION_NAME).findOne({
_id: new ObjectId(id)
});

if (!todo) {
throw new Error(‘Todo not found’);
}

return todo;
},

async find(query = {}, options = {}) {
const db = getDB();
const {
page = 1,
limit = 10,
sortBy = ‘createdAt’,
sortOrder = -1
} = options;

if (page < 1 || limit < 1) {
throw new Error(‘Invalid pagination parameters’);
}

const skip = (page – 1) * limit;
const sortOptions = { [sortBy]: sortOrder };

// Apply filters
const filters = { …query };
if (filters.priority) {
if (![‘low’, ‘medium’, ‘high’].includes(filters.priority)) {
throw new Error(‘Invalid priority filter’);
}
}
if (filters.status) {
if (![‘pending’, ‘in-progress’, ‘completed’].includes(filters.status)) {
throw new Error(‘Invalid status filter’);
}
}

const [todos, totalCount] = await Promise.all([
db.collection(COLLECTION_NAME)
.find(filters)
.sort(sortOptions)
.skip(skip)
.limit(limit)
.toArray(),
db.collection(COLLECTION_NAME)
.countDocuments(filters)
]);

return {
todos,
pagination: {
total: totalCount,
page,
limit,
pages: Math.ceil(totalCount / limit)
}
};
},

async update(id, updateData) {
if (!ObjectId.isValid(id)) {
throw new Error(‘Invalid todo ID’);
}

const errors = validateTodoData(updateData);
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(‘, ‘)}`);
}

const db = getDB();
const existingTodo = await this.findById(id);

// Business logic for status changes
if (updateData.status === ‘completed’ && existingTodo.status !== ‘completed’) {
updateData.completedAt = new Date();
}
if (updateData.status && updateData.status !== ‘completed’) {
updateData.completedAt = null;
}

const result = await db.collection(COLLECTION_NAME).findOneAndUpdate(
{ _id: new ObjectId(id) },
{
$set: {
…updateData,
updatedAt: new Date()
}
},
{ returnDocument: ‘after’ }
);

if (!result.value) {
throw new Error(‘Todo not found’);
}

return result.value;
},

async delete(id) {
if (!ObjectId.isValid(id)) {
throw new Error(‘Invalid todo ID’);
}

const db = getDB();
const result = await db.collection(COLLECTION_NAME).deleteOne({
_id: new ObjectId(id)
});

if (result.deletedCount === 0) {
throw new Error(‘Todo not found’);
}

return true;
},

// Additional business logic methods
async markAsComplete(id) {
return await this.update(id, {
status: ‘completed’
});
},

async findOverdue() {
const db = getDB();
return await db.collection(COLLECTION_NAME).find({
dueDate: { $lt: new Date() },
status: { $ne: ‘completed’ }
}).toArray();
}
};

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمونه همه DAO

الگوی DAO

منطق دسترسی به داده ها از منطق تجاری جدا شده است.
لایه سرویس برای منطق تجاری و لایه DAO برای دسترسی به داده ها.
برای آزمایش با تمسخر لایه دسترسی به داده بهتر است.
تزریق وابستگی را بهتر پشتیبانی می کند.
پیاده سازی پیچیده تر و تکرار کد.

src/
├── config/
│ └── database.js # Database connection configuration

├── models/
│ └── todo.entity.js # Data structure/schema definition

├── daos/
│ └── todo.dao.js # Data Access Object – handles database operations

├── services/
│ └── todo.service.js # Business logic layer

├── controllers/
│ └── todo.controller.js # Request handling & response formatting

├── routes/
│ └── todo.routes.js # Route definitions

└── app.js # Application entry point

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پیکربندی پایگاه داده

// src/config/database.js
const { MongoClient } = require(‘mongodb’);

class Database {
constructor(config) {
this.config = {
url: config.url || process.env.MONGODB_URI || ‘mongodb://localhost:27017’,
dbName: config.dbName || process.env.DB_NAME || ‘todoapp’,
options: {
useUnifiedTopology: true,
…config.options
}
};
this.client = null;
this.db = null;
}

async connect() {
try {
this.client = await MongoClient.connect(this.config.url, this.config.options);
this.db = this.client.db(this.config.dbName);
console.log(‘Connected to MongoDB successfully’);
return this.db;
} catch (error) {
console.error(‘MongoDB connection error:’, error);
throw new DatabaseError(‘Failed to connect to database’, error);
}
}

async disconnect() {
try {
if (this.client) {
await this.client.close();
this.client = null;
this.db = null;
console.log(‘Disconnected from MongoDB’);
}
} catch (error) {
console.error(‘MongoDB disconnection error:’, error);
throw new DatabaseError(‘Failed to disconnect from database’, error);
}
}

getDB() {
if (!this.db) {
throw new DatabaseError(‘Database not initialized. Call connect() first.’);
}
return this.db;
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

موجودیت

// src/models/todo.entity.js
class Todo {
static STATUS = {
PENDING: ‘pending’,
IN_PROGRESS: ‘in-progress’,
COMPLETED: ‘completed’
};

static PRIORITY = {
LOW: ‘low’,
MEDIUM: ‘medium’,
HIGH: ‘high’
};

constructor(data = {}) {
this._id = data._id || null;
this.title = data.title || ”;
this.description = data.description || ”;
this.status = data.status || Todo.STATUS.PENDING;
this.priority = data.priority || Todo.PRIORITY.MEDIUM;
this.dueDate = data.dueDate ? new Date(data.dueDate) : null;
this.createdAt = data.createdAt ? new Date(data.createdAt) : new Date();
this.updatedAt = data.updatedAt ? new Date(data.updatedAt) : new Date();
this.completedAt = data.completedAt ? new Date(data.completedAt) : null;
this.tags = Array.isArray(data.tags) ? […data.tags] : [];
this.assignedTo = data.assignedTo || null;
}

validate() {
const errors = [];

if (!this.title?.trim()) {
errors.push(‘Title is required’);
} else if (this.title.trim().length < 3) {
errors.push(‘Title must be at least 3 characters long’);
}

if (this.status && !Object.values(Todo.STATUS).includes(this.status)) {
errors.push(`Invalid status. Must be one of: ${Object.values(Todo.STATUS).join(‘, ‘)}`);
}

if (this.dueDate) {
if (!(this.dueDate instanceof Date) || isNaN(this.dueDate.getTime())) {
errors.push(‘Invalid due date format’);
} else if (this.dueDate < new Date()) {
errors.push(‘Due date cannot be in the past’);
}
}

if (this.priority && !Object.values(Todo.PRIORITY).includes(this.priority)) {
errors.push(`Invalid priority. Must be one of: ${Object.values(Todo.PRIORITY).join(‘, ‘)}`);
}

if (this.tags && !Array.isArray(this.tags)) {
errors.push(‘Tags must be an array’);
}

return errors;
}

isOverdue() {
return this.dueDate && this.dueDate < new Date() && this.status !== Todo.STATUS.COMPLETED;
}

toJSON() {
return {
_id: this._id,
title: this.title,
description: this.description,
status: this.status,
priority: this.priority,
dueDate: this.dueDate,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
completedAt: this.completedAt,
tags: this.tags,
assignedTo: this.assignedTo
};
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کلاس خطاهای اعتبارسنجی

// src/errors/index.js
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = ‘ValidationError’;
this.status = 400;
}
}

class DatabaseError extends Error {
constructor(message, originalError = null) {
super(message);
this.name = ‘DatabaseError’;
this.status = 500;
this.originalError = originalError;
}
}

class NotFoundError extends Error {
constructor(message) {
super(message);
this.name = ‘NotFoundError’;
this.status = 404;
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

DAO

مبتنی بر کلاس با تزریق سازنده

این سبک یک کلاس BaseDAO را تعریف می کند که یک رابط تمیز و قابل استفاده مجدد با متدهایی مانند findOne، insertOne و updateOne ارائه می کند. هر DAO (مانند TodoDAO) BaseDAO را گسترش می دهد و در یک نام مجموعه خاص عبور می کند

جوانب مثبت:

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

اعتبار سنجی سازنده: اطمینان حاصل می‌کند که db و collectionName ارائه شده‌اند، که کمک می‌کند مشکلات را زودتر شناسایی کنید.

خوانایی و قابلیت نگهداری: عملیات CRUD به خوبی کپسوله شده است و می تواند به راحتی در DAO های مختلف با گسترش BaseDAO دوباره استفاده شود.

معایب:

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

// src/daos/base.dao.js
class BaseDAO {
constructor(db, collectionName) {
if (!db) {
throw new Error(‘Database connection is required’);
}
if (!collectionName) {
throw new Error(‘Collection name is required’);
}
this.db = db;
this.collection = this.db.collection(collectionName);
}

async findOne(filter) {
try {
return await this.collection.findOne(filter);
} catch (error) {
throw new DatabaseError(‘Database query failed’, error);
}
}

async find(filter = {}, options = {}) {
try {
return await this.collection.find(filter, options).toArray();
} catch (error) {
throw new DatabaseError(‘Database query failed’, error);
}
}

async insertOne(data) {
try {
return await this.collection.insertOne(data);
} catch (error) {
throw new DatabaseError(‘Database insert failed’, error);
}
}

async updateOne(filter, update, options = {}) {
try {
return await this.collection.updateOne(filter, update, options);
} catch (error) {
throw new DatabaseError(‘Database update failed’, error);
}
}

async deleteOne(filter) {
try {
return await this.collection.deleteOne(filter);
} catch (error) {
throw new DatabaseError(‘Database delete failed’, error);
}
}
}

// src/daos/todo.dao.js
class TodoDAO extends BaseDAO {
constructor(db) {
super(db, ‘todos’);
}

async create(todoData) {
const todo = new Todo(todoData);
const errors = todo.validate();

if (errors.length > 0) {
throw new ValidationError(errors.join(‘, ‘));
}

const todoToInsert = {
…todo.toJSON(),
title: todo.title.trim(),
createdAt: new Date(),
updatedAt: new Date()
};

try {
const result = await this.insertOne(todoToInsert);
return new Todo({ …todoToInsert, _id: result.insertedId });
} catch (error) {
throw new DatabaseError(‘Failed to create todo’, error);
}
}

async findById(id) {
if (!ObjectId.isValid(id)) {
throw new ValidationError(‘Invalid todo ID’);
}

const todo = await this.findOne({ _id: new ObjectId(id) });

if (!todo) {
throw new NotFoundError(‘Todo not found’);
}

return new Todo(todo);
}

async find(query = {}, options = {}) {
const {
page = 1,
limit = 10,
sortBy = ‘createdAt’,
sortOrder = -1,
status,
priority,
searchTerm,
fromDate,
toDate,
tags
} = options;

if (page < 1 || limit < 1) {
throw new ValidationError(‘Invalid pagination parameters’);
}

const filter = this._buildFilter({
…query,
status,
priority,
searchTerm,
fromDate,
toDate,
tags
});

const skip = (page – 1) * limit;
const sortOptions = { [sortBy]: sortOrder };

try {
const [todos, totalCount] = await Promise.all([
this.collection
.find(filter)
.sort(sortOptions)
.skip(skip)
.limit(limit)
.toArray(),
this.collection.countDocuments(filter)
]);

return {
todos: todos.map(todo => new Todo(todo)),
pagination: {
total: totalCount,
page,
limit,
pages: Math.ceil(totalCount / limit)
}
};
} catch (error) {
throw new DatabaseError(‘Failed to fetch todos’, error);
}
}

async update(id, updateData) {
const existingTodo = await this.findById(id);

const updatedTodo = new Todo({
…existingTodo,
…updateData,
_id: existingTodo._id,
updatedAt: new Date()
});

const errors = updatedTodo.validate();
if (errors.length > 0) {
throw new ValidationError(errors.join(‘, ‘));
}

const result = await this.updateOne(
{ _id: new ObjectId(id) },
{ $set: updatedTodo.toJSON() },
{ returnDocument: ‘after’ }
);

if (result.matchedCount === 0) {
throw new NotFoundError(‘Todo not found’);
}

return updatedTodo;
}

async delete(id) {
if (!ObjectId.isValid(id)) {
throw new ValidationError(‘Invalid todo ID’);
}

const result = await this.deleteOne({ _id: new ObjectId(id) });

if (result.deletedCount === 0) {
throw new NotFoundError(‘Todo not found’);
}

return true;
}

_buildFilter(options) {
const filter = {};

if (options.status) {
if (!Object.values(Todo.STATUS).includes(options.status)) {
throw new ValidationError(‘Invalid status filter’);
}
filter.status = options.status;
}

if (options.priority) {
if (!Object.values(Todo.PRIORITY).includes(options.priority)) {
throw new ValidationError(‘Invalid priority filter’);
}
filter.priority = options.priority;
}

if (options.searchTerm) {
filter.$or = [
{ title: { $regex: options.searchTerm, $options: ‘i’ } },
{ description: { $regex: options.searchTerm, $options: ‘i’ } }
];
}

if (options.fromDate || options.toDate) {
filter.dueDate = {};
if (options.fromDate) {
filter.dueDate.$gte = new Date(options.fromDate);
}
if (options.toDate) {
filter.dueDate.$lte = new Date(options.toDate);
}
}

if (options.tags && Array.isArray(options.tags)) {
filter.tags = { $all: options.tags };
}

return filter;
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

روش استاتیک با اتصال تزریقی

استفاده ساده شده: روش‌های استاتیک نیاز به نمونه‌سازی کلاس را برطرف می‌کند و سربار حافظه را کاهش می‌دهد.

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

قابلیت استفاده مجدد محدود: روش‌های استاتیک از وراثت پشتیبانی نمی‌کنند، بنابراین متمرکز کردن منطق مشترک بین DAOها دشوارتر است.

import { ObjectId } from ‘mongodb’;

let todos;

export default class TodoDAO {
static async injectDB(conn) {
if (todos) return;
try {
todos = await conn.db(process.env.DB_NAME).collection(‘todos’);
} catch (error) {
console.error(`Unable to establish collection handles in TodoDAO: ${error}`);
}
}

static async create(todoData) {
// Initialize and validate the Todo, build the data object, and insert into the collection
}

static async findById(id) {
// Validate ID, query by `_id`, throw `NotFoundError` if not found
}

static async find(query = {}, options = {}) {
// Construct filter, pagination, and sort options, execute the find query with pagination
}

static async update(id, updateData) {
// Fetch the todo, validate and update with `updateOne`
}

static async delete(id) {
// Validate ID, delete by `_id`, and handle `NotFoundError`
}

static _buildFilter(options) {
// Generate the filter object for queries based on status, priority, search terms, etc.
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خدمات

// src/services/todo.service.js
class TodoService {
constructor(todoDAO) {
if (!todoDAO) {
throw new Error(‘TodoDAO is required’);
}
this.todoDAO = todoDAO;
}

async createTodo(todoData) {
return await this.todoDAO.create(todoData);
}

async getTodoById(id) {
return await this.todoDAO.findById(id);
}

async updateTodo(id, updateData) {
return await this.todoDAO.update(id, updateData);
}

async deleteTodo(id) {
return await this.todoDAO.delete(id);
}

async updateTodoStatus(id, status) {
const todo = await this.todoDAO.findById(id);

const updates = {
status,
updatedAt: new Date()
};

if (status === Todo.STATUS.COMPLETED && todo.status !== Todo.STATUS.COMPLETED) {
updates.completedAt = new Date();
} else if (status !== Todo.STATUS.COMPLETED && todo.status === Todo.STATUS.COMPLETED) {
updates.completedAt = null;
}

return await this.todoDAO.update(id, updates);
}

async findTodos(options = {}) {
return await this.todoDAO.find({}, options);
}

async findOverdueTodos() {
const now = new Date();
return await this.todoDAO.find({
dueDate: { $lt: now },
status: { $ne: Todo.STATUS.COMPLETED }
});
}

async findTodosByPriority(priority) {
return await this.todoDAO.find({ priority });
}

async assignTodo(id, userId) {
return await this.todoDAO.update(id, {
assignedTo: userId,
updatedAt: new Date()
});
}

async addTags(id, tags) {
const todo = await this.todoDAO.findById(id);
const uniqueTags = […new Set([…todo.tags, …tags])];
return await this.todoDAO.update(id, { tags: uniqueTags });
}

async removeTags(id, tags) {
const todo = await this.todoDAO.findById(id);
const updatedTags = todo.tags.filter(tag => !tags.includes(tag));
return await this.todoDAO.update(id, { tags: updatedTags });
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمایه سازی

نمایه سازی در MongoDB عملکرد پرس و جو را با ایجاد ساختارهای داده کارآمد برای بازیابی سریعتر بهبود می بخشد.

شاخص تک فیلد: نمایه در یک فیلد واحد از یک سند.

شاخص مرکب: فهرست در چندین فیلد.

شاخص چند کلیدی: نمایه در فیلدهای آرایه.

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

شاخص تک فیلد

برای ایجاد ایندکس، از createIndex() روش

db.collection(‘users’).createIndex({ name: 1 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاخص مرکب

یک شاخص ترکیبی در MongoDB یک نمایه در چندین فیلد در یک سند است.

db.collection(‘users’).createIndex({ name: 1, age: 1, city: 1, country: 1, hobbies: 1 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

db.collection(‘users’).find({ name: ‘Alice’, age: 25 });
db.collection(‘users’).find({ name: ‘Alice’, age: 25, city: ‘New York’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاخص چند کلیدی

یک شاخص چند کلیدی در MongoDB یک شاخص در یک فیلد آرایه است.

db.collection(‘users’).createIndex({ hobbies: 1 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

محدودیت های شاخص های چند کلیدی

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

db.collection(‘users’).createIndex({ “hobbies”: 1, “tags”: 1 }); // Not allowed if both hobbies and tags are arrays

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فهرست متن

فهرست های متنی در MongoDB راهی برای انجام پرس و جوهای جستجوی متنی در محتوای رشته ارائه می دهند. هر مجموعه می تواند حداکثر یک فهرست متنی داشته باشد.

db.products.createIndex({
name: “text”,
description: “text”,
tags: “text”
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

// Sample Data
db.products.insertMany([
{
name: “MacBook Pro 16-inch”,
description: “Powerful laptop for developers”,
tags: [“apple”, “laptop”, “computer”] },
{
name: “iPhone 15 Pro”,
description: “Latest smartphone with great camera”,
tags: [“apple”, “smartphone”, “mobile”] },
{
name: “Gaming Laptop ROG”,
description: “High-performance gaming laptop”,
tags: [“asus”, “laptop”, “gaming”] }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تطابق دقیق کلمات

db.products.find({ $text: { $search: “laptop” }}); // Will find MacBook and ROG

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چند کلمه (OR)

// By default, multiple words are treated as logical OR
// Will find documents containing “laptop” or “gaming”
db.products.find({ $text: { $search: “laptop gaming” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چند کلمه (منطقی و)

// Using the plus sign to ensure both terms are present
// Will find documents containing both “laptop” and “gaming”
db.products.find({ $text: { $search: “+laptop +gaming” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

عبارات دقیق

// Using double quotes to search for an exact phrase
// Will find documents containing the exact phrase “gaming laptop”
db.products.find({ $text: { $search: “\”gaming laptop\”” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف کلماتdb.products.find({ $text: { $search: “laptop -gaming” }}); // لپ تاپ اما نه گیمینگ

مسابقات بدون حروف بزرگ

// Text search is case-insensitive by default
// Will find all documents containing “laptop” regardless of case
db.products.find({ $text: { $search: “LAPTOP” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دیاکریتیک غیر حساس

// Text search ignores diacritic marks by default
// Will match “cafe” and “café”
db.products.find({ $text: { $search: “café” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مطابقت جزئی کلمه

// Searching for “lap” won’t find “laptop”
db.products.find({ $text: { $search: “lap” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوهای حروف عامیانه

// Wildcards are not supported in text search
db.products.find({ $text: { $search: “lap*” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تطبیق فازی (اشتباهات تایپی)

// Regular expressions cannot be used inside $text search
db.products.find({ $text: { $search: “/lap.*/” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

عبارات بولی پیچیده

// Nested boolean logic is not supported in $text search
db.products.find({ $text: { $search: “(laptop AND gaming) OR (smartphone AND camera)” }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوهای حساس به حروف کوچک و بزرگ

// Cannot perform case-sensitive searches using $text
db.products.find({ $text: { $search: “MacBook”, caseSensitive: true }});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوی اطلس

Atlas Search یک سرویس جستجوی کاملاً مدیریت شده است که به شما امکان می دهد قابلیت های جستجوی سریع، مرتبط و متن کامل را در بالای داده های MongoDB خود ایجاد کنید. در مقایسه با فهرست‌های متنی، جستجوی اطلس ویژگی‌های پیشرفته‌تری مانند جستجوی وجهی، تطبیق فازی و امتیازدهی مرتبط را فراهم می‌کند.

// Basic Atlas Search Setup
db.products.createSearchIndex({
“mappings”: {
“dynamic”: true,
“fields”: {
“name”: {
“type”: “string”,
“analyzer”: “lucene.standard”
},
“description”: {
“type”: “string”,
“analyzer”: “lucene.english”
}
}
}
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تطبیق فازی – غلط املایی و غلط املایی را کنترل می کند

db.products.aggregate([
{
$search: {
text: {
query: “labtop”, // Will match “laptop”
path: “name”,
fuzzy: { maxEdits: 1 }
}
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تکمیل خودکار – پیشنهادات در زمان واقعی

db.products.aggregate([
{
$search: {
autocomplete: {
query: “mac”, // Will suggest “macbook”, “machine”, etc.
path: “name”
}
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

امتیازدهی سفارشی – ارتباط را افزایش دهید

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

db.products.aggregate([
{
$search: {
compound: {
must: [
{ text: { query: “laptop”, path: “name” } }
],
should: [
{
text: {
query: “gaming”,
path: “category”,
score: { boost: { value: 2.0 } } // Boost gaming laptops
}
}
] }
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اسناد باید حاوی “لپ تاپ” در قسمت نام باشند
اسناد حاوی “بازی” در قسمت توضیحات تقویت می شوند.

پشتیبانی چند زبانه

db.products.aggregate([
{
$search: {
text: {
query: “ordinateur portable”, // French for “laptop”
path: “description”,
analyzer: “lucene.french”
}
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوی وجهی – نتایج را فیلتر کنید

جستجوی وجهی به کاربران اجازه می دهد تا نتایج جستجو را بر اساس دسته بندی ها، محدوده قیمت و سایر ویژگی ها فیلتر کنند.

db.products.aggregate([
{
$searchMeta: {
facet: {
operator: { text: { query: “laptop”, path: “description” } },
facets: {
categories: { type: “string”, path: “category” },
priceRanges: {
type: “number”,
path: “price”,
boundaries: [500, 1000, 2000] }
}
}
}
}
]);

**Facets:**
– categoryFacet: Groups results by the category field.
– priceFacet: Groups results into price ranges defined by the boundaries.

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مبادلات

علاوه بر مزایا، جستجوی اطلس در مقایسه با نمایه‌های متنی سنتی دارای برخی معاوضه‌ها نیز است.

به خوشه های M10+ نیاز دارد
هزینه اضافی
راه اندازی پیچیده تر
استفاده بیشتر از منابع

عملیات آرایه

MongoDB از انواع عملیات آرایه برای کار با آرایه ها در اسناد پشتیبانی می کند.

افزودن عناصر به آرایه

برای افزودن عناصر به آرایه در یک سند، از $push اپراتور

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $push: { hobbies: ‘Reading’ } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پرس و جو آرایه ها با $elemMatch

با توجه به مجموعه ای از دانش آموزان با نمرات در موضوعات مختلف:

{
“_id”: ObjectId(“64d39a7a8b0e8c284a2c1234”),
“name”: “Alice”,
“scores”: [
{ “subject”: “Math”, “score”: 95 },
{ “subject”: “English”, “score”: 88 }
] },
{
“_id”: ObjectId(“64d39a808b0e8c284a2c1235”),
“name”: “Bob”,
“scores”: [
{ “subject”: “Math”, “score”: 78 },
{ “subject”: “English”, “score”: 92 }
] }

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پرس و جو با $elemMatch:

برای یافتن دانش‌آموزانی که به طور خاص در ریاضی نمره 90 کسب کرده‌اند، نیاز داریم $elemMatch:

db.students.find({
scores: {
$elemMatch: { subject: “Math”, score: { $gt: 90 } }
}
})

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه: این فقط آلیس را برمی گرداند، زیرا او تنها دانش آموز با نمره بالای 90 در درس “ریاضی” است. $elemMatch آن را تضمین می کند همه شرایط درون عنصر آرایه باید برآورده شود.

بدون $elemMatch – این امر هم آلیس و هم باب را برمی گرداند، زیرا هر دو در دروس مختلف و نه لزوماً در درس “ریاضی” نمرات بالای 90 دارند.

اضافه کردن منحصر به فرد – $addToSet

را $addToSet عملگر در MongoDB برای افزودن عناصر به آرایه تنها در صورتی استفاده می شود که از قبل وجود نداشته باشند. این از ورودهای تکراری در آرایه جلوگیری می کند.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $addToSet: { hobbies: ‘Reading’ } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چندگانه اضافه کنید $push و $each

را $push عملگر در MongoDB برای افزودن عناصر به آرایه استفاده می شود. را $each modifier به شما اجازه می دهد چندین عنصر را به آرایه اضافه کنید.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $push: { hobbies: { $each: [‘Reading’, ‘Swimming’] } } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن مرتب شده – $push و $sort

را $push عملگر در MongoDB برای افزودن عناصر به آرایه استفاده می شود. را $sort modifier به شما اجازه می دهد تا عناصر آرایه را مرتب کنید.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $push: { scores: { $each: [85, 90], $sort: -1 } } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فشار و مرتب سازی آرایه از اشیاء بر اساس یک فیلد خاص

db.collection(‘users’).updateOne(
{ name: ‘Alice’ },
{
$push: {
scores: {
$each: [
{ score: 85, date: “2023-03-01” },
{ score: 90, date: “2023-04-01” }
],
$sort: { date: -1 }
}
}
}
);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن محدود – $push و $slice

را $push عملگر در MongoDB برای افزودن عناصر به آرایه استفاده می شود. را $slice اصلاح کننده به شما امکان می دهد تعداد عناصر موجود در آرایه را محدود کنید.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $push: { scores: { $each: [85, 90], $slice: -3 } } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر آلیس در حال حاضر یک آرایه نمرات مانند [70, 75, 80]، این کوئری 85 و 90 را فشار می دهد و آن را ایجاد می کند [70, 75, 80, 85, 90]. سپس $slice: -3 آن را به 3 عنصر آخر برش می‌دهد و در نتیجه [80, 85, 90].

حذف عناصر از یک آرایه

برای حذف عناصر از آرایه در یک سند، از $pull اپراتور

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $pull: { hobbies: ‘Reading’ } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اولین یا آخرین عنصر را حذف کنید – $pop

را $pop عملگر در MongoDB برای حذف اولین یا آخرین عنصر از یک آرایه استفاده می شود.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $pop: { hobbies: 1 } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف چندگانه – $pullAll

را $pullAll عملگر در MongoDB برای حذف تمام رخدادهای مقادیر مشخص شده از یک آرایه استفاده می شود.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $pullAll: { hobbies: [‘Reading’, ‘Swimming’] } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف چندگانه – $pull و $in

را $pull عملگر در MongoDB برای حذف عناصر از یک آرایه استفاده می شود. را $in modifier به شما امکان می دهد چندین مقدار را برای حذف مشخص کنید.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $pull: { hobbies: { $in: [‘Reading’, ‘Swimming’] } } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف شرط – $pull و $gt

را $pull عملگر در MongoDB برای حذف عناصر از یک آرایه استفاده می شود. را $gt modifier به شما اجازه می دهد تا یک شرط برای حذف عناصر مشخص کنید.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $pull: { scores: { $gt: 85 } } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف نه برابر – $pull و $ne

را $pull عملگر در MongoDB برای حذف عناصر از یک آرایه استفاده می شود. را $ne modifier به شما اجازه می دهد تا یک شرط برای حذف عناصری که برابر با یک مقدار نیستند، تعیین کنید.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $pull: { scores: { $ne: 85 } } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شرایط به روز رسانی – $set و $

را $set عملگر در MongoDB برای به روز رسانی فیلدهای یک سند استفاده می شود. را $ عملگر موقعیتی به شما امکان می دهد اولین عنصری را که با یک شرط در یک آرایه مطابقت دارد به روز کنید.

db.collection(‘users’).updateOne({ name: ‘Alice’, ‘scores.subject’: ‘Math’ }, { $set: { ‘scores.$.score’: 90 } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی همه – $[]

را $[] عملگر در MongoDB برای به روز رسانی تمام عناصر یک آرایه که با یک شرط مطابقت دارند استفاده می شود.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $set: { ‘scores.$[].score’: 90 } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای افزایش یک فیلد در تمام عناصر یک آرایه، می توانید از $[] اپراتور با $inc اپراتور

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $inc: { ‘scores.$[].score’: 5 } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی فیلتر شده – $[]

را $[] عملگر در MongoDB برای به روز رسانی عناصر در یک آرایه که با یک شرط مطابقت دارند استفاده می شود.

db.collection(‘users’).updateOne({ name: ‘Alice’ }, { $set: { ‘scores.$[elem].score’: 90 } }, { arrayFilters: [{ ‘elem.subject’: ‘Math’ }] });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تجمیع

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

خط لوله تجمع

چارچوب تجمیع در MongoDB از یک رویکرد خط لوله استفاده می کند، که در آن چندین مرحله اسناد را تغییر می دهد.

db.collection(‘orders’).aggregate([
{ $match: { status: ‘A’ } },
{ $group: { _id: ‘$cust_id’, total: { $sum: ‘$amount’ } } },
{ $sort: { total: -1 } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به مجموعه ها بپیوندید

MongoDB از اتصالات مانند پایگاه داده های رابطه ای پشتیبانی نمی کند. در عوض، شما می توانید استفاده کنید $lookup عملگر برای انجام یک اتصال بیرونی سمت چپ بین دو مجموعه.

db.collection(‘orders’).aggregate([
{
$lookup: {
from: ‘customers’,
localField: ‘cust_id’,
foreignField: ‘_id’,
as: ‘customer’
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باز کردن آرایه ها

را $unwind عملگر در MongoDB برای تجزیه یک فیلد آرایه به چندین سند استفاده می شود.

db.collection(‘orders’).aggregate([
{ $unwind: ‘$items’ }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باز کردن و گروه

db.collection(‘orders’).aggregate([
{ $unwind: ‘$items’ },
{
$group: {
_id: ‘$items.productId’,
totalQuantity: { $sum: ‘$items.quantity’ },
totalRevenue: { $sum: { $multiply: [‘$items.price’, ‘$items.quantity’] } },
ordersCount: { $sum: 1 }
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باز کردن چند آرایه

db.collection(‘restaurants’).aggregate([
{ $unwind: ‘$categories’ },
{ $unwind: ‘$reviews’ },
{
$group: {
_id: ‘$categories’,
averageRating: { $avg: ‘$reviews.rating’ },
reviewCount: { $sum: 1 }
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اسناد گروهی

را $group عملگر در MongoDB برای گروه بندی اسناد توسط یک کلید مشخص استفاده می شود.

db.collection(‘orders’).aggregate([
{ $group: { _id: ‘$cust_id’, total: { $sum: ‘$amount’ } } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و شمارش

db.collection(‘orders’).aggregate([
{ $group: { _id: ‘$status’, count: { $sum: 1 } } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و جمع

db.collection(‘orders’).aggregate([
{ $group: { _id: ‘$status’, total: { $sum: ‘$amount’ } } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و میانگین

db.collection(‘orders’).aggregate([
{ $group: { _id: ‘$status’, average: { $avg: ‘$amount’ } } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و فشار

db.collection(‘orders’).aggregate([
{ $group: { _id: ‘$cust_id’, items: { $push: ‘$item’ } } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه با فیلدهای متعدد

db.collection(‘orders’).aggregate([
{
$group: {
_id: {
status: ‘$status’,
category: ‘$category’
},
count: { $sum: 1 },
totalAmount: { $sum: ‘$amount’ }
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه با عملیات تاریخ

db.collection(‘orders’).aggregate([
{
$group: {
_id: {
year: { $year: ‘$orderDate’ },
month: { $month: ‘$orderDate’ }
},
totalOrders: { $sum: 1 },
revenue: { $sum: ‘$amount’ }
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمینه های پروژه

را $project عملگر در MongoDB برای گنجاندن، حذف یا تغییر نام فیلدها در اسناد خروجی استفاده می شود.

db.collection(‘orders’).aggregate([
{ $project: { _id: 0, cust_id: 1, amount: 1 } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با فیلدهای محاسبه شده

db.collection(‘orders’).aggregate([
{
$project: {
_id: 0,
cust_id: 1,
amount: 1,
discount: { $subtract: [‘$total’, ‘$amount’] }
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با عملیات آرایه

db.collection(‘orders’).aggregate([
{
$project: {
orderId: 1,
itemCount: { $size: ‘$items’ },
firstItem: { $arrayElemAt: [‘$items’, 0] },
lastItem: { $arrayElemAt: [‘$items’, -1] },
items: {
$map: {
input: ‘$items’,
as: ‘item’,
in: {
name: ‘$$item.name’,
subtotal: {
$multiply: [‘$$item.price’, ‘$$item.quantity’] }
}
}
}
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با عملیات رشته

db.collection(‘users’).aggregate([
{
$project: {
fullName: { $concat: [‘$firstName’, ‘ ‘, ‘$lastName’] },
email: { $toLower: ‘$email’ },
age: { $toString: ‘$age’ }
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با فیلدهای شرطی

db.collection(‘users’).aggregate([
{
$project: {
name: 1,
status: {
$cond: {
if: { $gte: [‘$age’, 18] },
then: ‘Adult’,
else: ‘Minor’
}
}
}
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چندین تجمع را اجرا کنید

شما می توانید چندین خط لوله تجمیع را در یک پرس و جو با استفاده از $facet اپراتور

db.collection(‘orders’).aggregate([
{
$facet: {
totalAmount: [
{ $group: { _id: null, total: { $sum: ‘$amount’ } } }
],
averageAmount: [
{ $group: { _id: null, average: { $avg: ‘$amount’ } } }
] }
}
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

معاملات

تراکنش ها در MongoDB به شما این امکان را می دهند که چندین عملیات را به عنوان یک واحد کار، همه یا هیچ انجام دهید. آنها یکپارچگی و سازگاری داده ها را در اسناد و مجموعه های متعدد تضمین می کنند.

ویژگی های تراکنش (ACID)

استفاده از تراکنش ها

برای استفاده از تراکنش ها در MongoDB، معمولاً این مراحل را دنبال کنید:

یک جلسه را شروع کنید
یک معامله را شروع کنید
عملیات را انجام دهد
معامله را متعهد یا لغو کنید

// Define a client

const { MongoClient } = require(‘mongodb’);
const client = new MongoClient(‘mongodb://localhost:27017’);
//…

// Start a session

const session = client.startSession();

try {
session.startTransaction();

// Perform multiple operations
await collection1.updateOne({ _id: 1 }, { $set: { status: ‘processing’ } }, { session });
await collection2.insertOne({ orderId: 1, items: [‘item1’, ‘item2’] }, { session });

// Commit the transaction
await session.commitTransaction();
} catch (error) {
// If an error occurred, abort the transaction
await session.abortTransaction();
console.error(‘Transaction aborted:’, error);
} finally {
// End the session
session.endSession();
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ملاحظات برای معاملات

عملکرد: تراکنش ها ممکن است بر عملکرد تأثیر بگذارد، به ویژه برای بارهای کاری سنگین.

تایم اوت: تراکنش ها دارای مهلت زمانی پیش فرض 60 ثانیه هستند.

مجموعه های ماکت: تراکنش ها نیاز به پیکربندی مجموعه ماکت دارند.

خوشه های خرد شده: معاملات روی خوشه های خرد شده ملاحظات و محدودیت های بیشتری دارند.

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

مجموعه های ماکت

مجموعه replica گروهی از نمونه های MongoDB است که مجموعه داده های یکسانی را حفظ می کند. مجموعه‌های Replica افزونگی و در دسترس بودن بالا را فراهم می‌کنند.

اجزای یک مجموعه ماکت

اولیه: تمام عملیات نوشتن را دریافت می کند.

ثانویه: داده های اولیه را تکرار می کند. می تواند برای عملیات خواندن استفاده شود.

داور: در انتخابات مقدماتی شرکت می کند اما داده ای ندارد.

پیکربندی مجموعه کپی

برای پیکربندی یک مجموعه کپی، از rs.initiate() روش

rs.initiate({
_id: ‘rs0’,
members: [
{ _id: 0, host: ‘mongo1:27017’ },
{ _id: 1, host: ‘mongo2:27017’ },
{ _id: 2, host: ‘mongo3:27017’, arbiterOnly: true }
] });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اولویت را بخوانید

اولویت خواندن در MongoDB نحوه توزیع عملیات خواندن در مجموعه replica را تعیین می کند.

اولیه: از ابتدایی می خواند.

ثانویه: از ثانویه می خواند.

Primary Preferred: در صورت وجود از اولیه می خواند، در غیر این صورت از ثانویه.

ثانویه ترجیح داده شده است: در صورت موجود بودن از ثانویه می خواند، در غیر این صورت از اولیه می خواند.

نزدیکترین: از نزدیکترین عضو مجموعه ماکت می خواند.

db.collection(‘users’).find().readPref(‘secondary’);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نگرانی را بنویسید

نگرانی نوشتن در MongoDB تعیین کننده سطح تایید برای عملیات نوشتن است.

w: 0: بدون تایید

w: 1: قدردانی از ابتدایی.

w: اکثریت: تصدیق اکثریت مجموعه ماکت.

db.collection(‘users’).insertOne({ name: ‘Alice’ }, { writeConcern: { w: ‘major

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Failover خودکار

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

Primary بر اساس تعداد آرای اعضای مجموعه ماکت انتخاب می شود.

Secondary در صورتی که اولیه در دسترس نباشد می توان به ابتدایی ارتقا داد.

Arbiter برای شکستن تساوی در انتخابات استفاده می شود.

Failover دستی

شما می توانید با وادار کردن یک عضو مجموعه replica برای تبدیل شدن به اصلی، یک Failover دستی را در MongoDB آغاز کنید.

rs.stepDown();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاردینگ

Sharding روشی برای توزیع داده ها در چندین ماشین است. این به شما امکان می دهد با افزودن ماشین های بیشتر به سیستم خود، مقیاس افقی را انجام دهید.

یک مجموعه به تکه‌هایی تقسیم می‌شود و هر تکه روی یک قطعه متفاوت ذخیره می‌شود.

هر Shard زیرمجموعه ای از داده ها در یک خوشه خرد شده است.

اجزای شاردینگ

شارد: زیر مجموعه ای از داده ها در یک خوشه خرد شده.

سرور پیکربندی: فوق داده ها و تنظیمات پیکربندی را برای خوشه ذخیره می کند.

کوئری روتر: کوئری ها را به قطعه مناسب هدایت می کند.

کلید شاردینگ

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

db.collection.createIndex({ _id: ‘hashed’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگام انتخاب یک کلید خرد، فاکتورهای زیر را در نظر بگیرید:

کاردینالیته: تعداد مقادیر منحصر به فرد در کلید خرده.

مقیاس بندی را بنویسید: توانایی توزیع عملیات نوشتن در بین خرده ها.

جداسازی پرس و جو: توانایی هدف قرار دادن خرده های خاص برای عملیات خواندن.

استراتژی های کلید شارد

Hashed Sharding: با استفاده از یک تابع هش، داده ها را به طور یکنواخت در بین خرده ها توزیع می کند.

Range Sharding: داده ها را بر اساس محدوده ای از مقادیر در کلید خرده توزیع می کند.

شاردینگ مرکب: داده ها را بر اساس چند فیلد در کلید خرده توزیع می کند.

db.collection.createIndex({ _id: ‘hashed’ });
db.collection.createIndex({ date: 1 });
db.collection.createIndex({ country: 1, city: 1 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مانگوس

Mongoose یک کتابخانه Object Data Modeling (ODM) برای MongoDB و Node.js است. این یک راه حل مبتنی بر طرحواره برای مدل سازی داده های برنامه شما ارائه می دهد.

اتصال به MongoDB

برای اتصال به MongoDB با استفاده از Mongoose، از connect() روش

const mongoose = require(‘mongoose’);

mongoose.connect(‘mongodb://localhost:27017/myapp’, { useNewUrlParser: true, useUnifiedTopology: true });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تعریف طرحواره

طرحواره Mongoose ساختار اسناد را در یک مجموعه تعریف می کند.

const userSchema = new mongoose.Schema({
name: String,
age: Number
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد یک مدل

مدل Mongoose کلاسی است که مجموعه ای را در MongoDB نشان می دهد.

const User = mongoose.model(‘User’, userSchema);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

درج اسناد

برای درج یک سند در یک مجموعه، یک نمونه از مدل ایجاد می کنید و آن را فراخوانی می کنید save() روش

const user = new User({ name: ‘Alice’, age: 25 });

user.save();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استعلام اسناد

برای درخواست اسناد از یک مجموعه، از find() روش

User.find({ name: ‘Alice’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با پروجکشن:

User.find({ name: ‘Alice’ }, { name: 1, age: 1 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی اسناد

برای به روز رسانی اسناد در یک مجموعه، از updateOne() روش

User.updateOne({ name: ‘Alice’ }, { age: 26 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف اسناد

برای حذف اسناد از مجموعه، از deleteOne() روش

User.deleteOne({ name: ‘Alice’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

میان افزار

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

userSchema.pre(‘save’, function(next) {
console.log(‘Saving user…’);
next();
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مجازی

مجازی‌های Mongoose ویژگی‌های سندی هستند که می‌توانید آنها را دریافت و تنظیم کنید، اما در MongoDB حفظ نمی‌شوند.

userSchema.virtual(‘fullName’).get(function() {
return this.name + ‘ ‘ + this.age;
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پلاگین ها

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

const timestampPlugin = require(‘./plugins/timestamp’);

userSchema.plugin(timestampPlugin);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

معاملات

معاملات Mongoose به شما این امکان را می دهد که چندین عملیات را روی چندین سند در یک تراکنش انجام دهید.

const session = await mongoose.startSession();
session.startTransaction();

try {
await User.create({ name: ‘Alice’ }, { session });
await User.create({ name: ‘Bob’ }, { session });

await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
} finally {
session.endSession();
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تجمیع

Mongoose یک API روان برای ساخت خطوط لوله تجمیع ارائه می دهد.

const result = await User.aggregate([
{ $match: { name: ‘Alice’ } },
{ $group: { _id: ‘$name’, total: { $sum: ‘$age’ } }
]);

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاخص ها

Mongoose به شما این امکان را می دهد که شاخص ها را روی طرحواره های خود تعریف کنید.

userSchema.index({ name: 1 });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جمعیت

جمعیت Mongoose به شما امکان می دهد به اسناد موجود در مجموعه های دیگر مراجعه کنید.

const userSchema = new mongoose.Schema({
name: String,
posts: [{ type: mongoose.Schema.Types.ObjectId, ref: ‘Post’ }] });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اعتبار سنجی

Mongoose اعتبار سنجی داخلی را برای فیلدهای طرحواره فراهم می کند.

const userSchema = new mongoose.Schema({
name: { type: String, required: true }
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

الگوها

الگو
توضیحات
استفاده از مورد
مزایا
معایب

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

الگوی صفت
مجموعه ای از فیلدها را با الگوهای دسترسی مشابه به عنوان یک سند جاسازی شده ذخیره می کند
محصولات با ویژگی های متفاوت
– طرحواره انعطاف پذیر- پرس و جو کارآمد از ویژگی های رایج
– پرس و جوهای پیچیده تر برای ویژگی های خاص- پتانسیل برای زمینه های استفاده نشده

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

الگوی زیر مجموعه
زیر مجموعه ای از فیلدها را از یک سند در مجموعه ای جداگانه ذخیره می کند
نمایه‌های کاربر با فیلدهای پربازدید
– عملکرد خواندن را برای پرس و جوهای رایج بهبود می بخشد- اندازه مجموعه کاری را کاهش می دهد
– تکرار داده ها- نیاز به همگام نگه داشتن زیر مجموعه ها دارد

پرسش و پاسخ

Exec() در Mongoose چیست؟

را exec() تابع در Mongoose برای اجرای یک پرس و جو و بازگشت یک وعده استفاده می شود. این به شما امکان می دهد تا متدهای پرس و جو را زنجیره ای کنید و سپس در پایان پرس و جو را اجرا کنید.

User.find({ name: ‘Alice’ }).exec();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

می توانید پرس و جو را بدون آن اجرا کنید exec()، با پاسخ به تماس یا استفاده از async/wait.

User.find({ name: ‘Alice’ }, (error, users) => {
console.log(users);
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

const users = await User.find({ name: ‘Alice’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چه فرقی با هم دارند findOne() و find() در مونگوس؟

find(): آرایه ای از تمام اسنادی را برمی گرداند که با معیارهای پرس و جو مطابقت دارند.

findOne(): اولین سندی را برمی گرداند که با معیارهای پرس و جو مطابقت دارد.

تفاوت بین Model.create() و new Model().save() در Mongoose چیست؟

Model.create(): یک سند جدید ایجاد می کند و آن را در یک مرحله در پایگاه داده ذخیره می کند.

User.create({ name: ‘Alice’ });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

new Model().save(): نمونه جدیدی از مدل را تکرار می کند اما بلافاصله آن را در پایگاه داده ذخیره نمی کند. قبل از فراخوانی .save() می توانید نمونه را تغییر دهید، اعتبارسنجی انجام دهید یا هر عملیات دیگری را اجرا کنید تا تغییرات ادامه یابد.

const doc = new Model({ name: ‘John’, age: 30 });
doc.age = 31; // Modify the document
await doc.save(); // Save the document after modification

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف از متد lean() در کوئری های Mongoose چیست و چه زمانی باید از آن استفاده کرد؟

را lean() متد در جستارهای Mongoose، اشیاء جاوا اسکریپت ساده را به جای اسناد Mongoose برمی گرداند که دارای ویژگی های اضافی زیادی هستند، مانند دریافت کننده ها، تنظیم کننده ها و روش هایی که برای کار با سند مفید هستند. زمانی که به ویژگی‌های کامل سند Mongoose نیاز ندارید و می‌خواهید عملکرد پرس و جو را بهبود ببخشید، باید از آن استفاده کنید.

User.find({ name: ‘Alice’ }).lean();

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چگونه حذف های نرم را در Mongoose پیاده سازی کنیم؟

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

const userSchema = new mongoose.Schema({
name: String,
deleted: { type: Boolean, default: false }
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

از پیش میان افزار برای حذف اسناد حذف شده از نتایج پرس و جو استفاده کنید.

userSchema.pre(/^find/, function(next) {
this.where({ deleted: false });
next();
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

یک روش برای “حذف نرم” یک سند اضافه کنید.

userSchema.methods.softDelete = function() {
this.deleted = true;
return this.save();
};

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فهرست مطالب

معماری MongoDB

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

  • پایگاه داده: ظرفی برای مجموعه ها.
  • مجموعه: گروهی از اسناد MongoDB.
  • سند: مجموعه ای از جفت های کلید-مقدار (شبیه به اشیاء JSON).

عملیات CRUD

CRUD مخفف Create، Read، Update و Delete است. اینها عملیات اساسی برای تعامل با داده ها در MongoDB هستند.

نمودار پری دریایی

ایجاد کنید

برای درج یک سند جدید در مجموعه، از insertOne() یا insertMany() روش ها

insertOne

db.collection('users').insertOne({ name: 'Alice', age: 25 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

db.collection('users').insertOne({ _id: 1, name: 'Alice', age: 25 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اطمینان حاصل کنید که به موارد تکراری رسیدگی کنید _id ارزش ها برای جلوگیری از تعارض

درج بسیاری

برای درج چندین سند، می توانید از insertMany() روش

db.collection('users').insertMany([
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر یکی از اسناد درج نشود، عملیات لغو می شود مگر اینکه شما آن را مشخص کنید ordered: false گزینه

به عنوان مثال اگر شما طرحواره وادیاسیون مانند این دارید. به Schema Validation بروید

db.createCollection('users', {
  validator: {
    $jsonSchema: {
      bsonType: 'object',
      required: ['name', 'age'],
      properties: {
        name: { bsonType: 'string' },
        age: { bsonType: 'int' }
      }
    }
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

db.collection('users').insertMany([
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie' } // missing age field
], { ordered: false });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سند سوم نادیده گرفته می شود و دو سند اول درج می شود.

نتیجه خواهد بود

{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId("..."),  // ID for Alice
    '1': ObjectId("...")   // ID for Bob
  }
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بخوانید

برای خواندن اسناد از یک مجموعه، از find() روش متد find() چندین سند را بازیابی می کند و مکان نما را برمی گرداند که می تواند برای دسترسی به اسناد تکرار شود.

پیدا کردن

const cursor = db.collection('users').find({ age: { $gte: 18 } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در Node.js می توانید مکان نما را با استفاده از آرایه تبدیل کنید toArray() روش

const docs = await cursor.toArray();
console.log(docs)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه:

[
  { "_id": 1, "name": "Alice", "age": 25 },
  { "_id": 2, "name": "Bob", "age": 30 }
]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

findOne

برای بازیابی یک سند، می توانید از findOne() روش

db.collection('users').findOne({ name: 'Alice' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه:

{ "_id": 1, "name": "Alice", "age": 25 }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فرافکنی

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

db.collection('users').find({}, { projection: { name: 1, age: 1 } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه:

[
  { "_id": 1, "name": "Alice", "age": 25 },
  { "_id": 2, "name": "Bob", "age": 30 }
]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

به روز رسانی

برای به روز رسانی اسناد موجود، از updateOne() یا updateMany() روش ها

db.collection('users').updateOne({ name: 'Alice' }, { $set: { age: 26 } });

db.collection('users').updateMany({ city: 'New York' }, { $set: { city: 'San Francisco' } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

{
  "acknowledged": true,
  "matchedCount": 5,
  "modifiedCount": 5,
  "upsertedId": null,
  "upsertedCount": 0
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بر اساس شی نتیجه شما می توانید پاسخ مناسب را برگردانید.

const result = await db.collection('users').updateOne(
  { _id: userId },
  { $set: { age: 30 } }
);

if (result.matchedCount === 0) {
  return { success: false, message: 'No matching document found' };
}

if (result.modifiedCount === 0) {
  return { success: true, message: 'Document already up-to-date' };
}

return { success: true, message: 'Document updated successfully' };
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ناراحت

اگر می‌خواهید سند جدیدی را در زمانی که هیچ سند منطبقی یافت نشد وارد کنید، می‌توانید از آن استفاده کنید upsert گزینه

db.collection('users').updateOne(
  { name: 'Alice' },
  { $set: { age: 26 }
  },
  { upsert: true }
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

{
  "acknowledged": true,
  "matchedCount": 1,
  "modifiedCount": 1,
  "upsertedId": ObjectId("..."),
  "upsertedCount": 1
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

FindOneAndUpdate

این روش ترکیبی از findOne() و updateOne() به عنوان عملیات اتمی عملیات اتمی عملیاتی است که به صورت یک واحد کار انجام می شود. این به جلوگیری از شرایط مسابقه و اطمینان از سازگاری داده ها کمک می کند.

// Update or create a user profile
db.users.findOneAndUpdate(
    { email: "alice@example.com" },
    { $set: { age: 26 } },
    { returnDocument: 'after' }
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

returnDocument می تواند “قبل” یا “بعد” برای بازگرداندن سند قبل یا بعد از به روز رسانی باشد.

{
    "value": {
        "_id": ObjectId("5f7d3b1c8e1f9a1c9c8e1f9a"),
        "email": "alice@example.com",
        "age": 26,
        "name": "Alice"
    },
    "lastErrorObject": {
        "updatedExisting": true,
        "n": 1
    },
    "ok": 1
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی اپراتورها

نمودار پری دریایی

// $set operator to update fields
db.collection('users').updateOne({ name: 'Alice' }, { $set: { age: 26, city: 'New York' } });

// $set operator to update nested fields
db.collection('users').updateOne({ name: 'Alice' }, { $set: { 'address.city': 'New York' } });

// $inc operator to increment a field
db.collection('users').updateOne({ name: 'Alice' }, { $inc: { age: 1 } });

// $mul operator to multiply a field value
db.collection('users').updateOne({ name: 'Alice' }, { $mul: { age: 2 } });

// $unset operator to remove a field
db.collection('users').updateOne({ name: 'Alice' }, { $unset: { city: '' } });

// $rename operator to rename a field
db.collection('users').updateOne({ name: 'Alice' }, { $rename: { city: 'location' } });

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف کنید

برای حذف اسناد از مجموعه، از deleteOne() یا deleteMany() روش ها

db.collection('users').deleteOne({ name: 'Alice' }); // delete first matching document

db.collection('users').deleteMany({ city: 'New York' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف نتیجه

{
  "acknowledged": true,
  "deletedCount": 1
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بر اساس شی نتیجه شما می توانید پاسخ مناسب را برگردانید.

const result = await db.collection('users').deleteOne({ _id: userId });

if (result.deletedCount === 0) {
  return { success: false, message: 'No matching document found' };
}

return { success: true, message: 'Document deleted successfully' };
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

حذف نرم: به جای حذف دائمی اسناد، می توانید با افزودن a آنها را به عنوان حذف شده علامت گذاری کنید deletedAt زمینه این به شما امکان می دهد داده ها را برای اهداف ممیزی یا بازیابی نگهداری کنید.

db.collection('users').updateOne(
  { _id: userId },
  { $set: { deletedAt: new Date() } }
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فیلتر دقیق: هنگام حذف اسناد، از یک فیلتر دقیق استفاده کنید تا از حذف ناخواسته اسناد بیشتر از آنچه در نظر گرفته شده است جلوگیری کنید. می توانید از find() برای پیش نمایش اسنادی که قبل از اجرای عملیات حذف حذف می شوند استفاده کنید.

اعتبار سنجی طرحواره

مدل در مقابل الگوی DAO

نمونه همه مدل

الگوی مدل

  • داده ها و رفتار را در بر می گیرد. این ممکن است شامل اعتبار سنجی، منطق تجاری و عملیات پایگاه داده باشد. این می تواند به “چاق” اما اجرای ساده تر تبدیل شود.

نمودار پری دریایی

این نمونه ای از ساختار فایل ها برای الگوی مدل است

src/
├── config/
│   └── database.js
│
├── models/
│   └── todo.model.js
│
├── controllers/
│   └── todo.controller.js
│
├── routes/
│   └── todo.routes.js
│
├── middleware/
│   └── auth.middleware.js
│
└── app.js
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

پیکربندی داده ها

// src/config/database.js
const { MongoClient } = require('mongodb');

const dbConfig = {
  url: process.env.MONGODB_URI || 'mongodb://localhost:27017',
  dbName: 'todoapp'
};

let db = null;

const connectDB = async () => {
  try {
    const client = await MongoClient.connect(dbConfig.url, {
      useUnifiedTopology: true
    });
    db = client.db(dbConfig.dbName);
    console.log('Connected to MongoDB successfully');
    return db;
  } catch (error) {
    console.error('MongoDB connection error:', error);
    process.exit(1);
  }
};

const getDB = () => {
  if (!db) {
    throw new Error('Database not initialized');
  }
  return db;
};

module.exports = { connectDB, getDB };
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مدل

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

// src/models/todo.model.js
onst { ObjectId } = require('mongodb');
const { getDB } = require('../config/database');

const COLLECTION_NAME = 'todos';

// Validation functions
const validateTodoData = (todoData) => {
  const errors = [];

  if (!todoData.title) {
    errors.push('Title is required');
  } else if (todoData.title.length < 3) {
    errors.push('Title must be at least 3 characters long');
  }

  if (todoData.status && !['pending', 'in-progress', 'completed'].includes(todoData.status)) {
    errors.push('Invalid status. Must be pending, in-progress, or completed');
  }

  if (todoData.dueDate && new Date(todoData.dueDate) < new Date()) {
    errors.push('Due date cannot be in the past');
  }

  if (todoData.priority && !['low', 'medium', 'high'].includes(todoData.priority)) {
    errors.push('Invalid priority. Must be low, medium, or high');
  }

  return errors;
};

const todoModel = {
  async create(todoData) {
    const errors = validateTodoData(todoData);
    if (errors.length > 0) {
      throw new Error(`Validation failed: ${errors.join(', ')}`);
    }

    const db = getDB();
    const todo = {
      ...todoData,
      title: todoData.title.trim(),
      status: todoData.status || 'pending',
      priority: todoData.priority || 'medium',
      createdAt: new Date(),
      updatedAt: new Date(),
      completedAt: null
    };

    const result = await db.collection(COLLECTION_NAME).insertOne(todo);
    return { ...todo, _id: result.insertedId };
  },

  async findById(id) {
    if (!ObjectId.isValid(id)) {
      throw new Error('Invalid todo ID');
    }

    const db = getDB();
    const todo = await db.collection(COLLECTION_NAME).findOne({ 
      _id: new ObjectId(id) 
    });

    if (!todo) {
      throw new Error('Todo not found');
    }

    return todo;
  },

  async find(query = {}, options = {}) {
    const db = getDB();
    const { 
      page = 1, 
      limit = 10,
      sortBy = 'createdAt',
      sortOrder = -1
    } = options;

    if (page < 1 || limit < 1) {
      throw new Error('Invalid pagination parameters');
    }

    const skip = (page - 1) * limit;
    const sortOptions = { [sortBy]: sortOrder };

    // Apply filters
    const filters = { ...query };
    if (filters.priority) {
      if (!['low', 'medium', 'high'].includes(filters.priority)) {
        throw new Error('Invalid priority filter');
      }
    }
    if (filters.status) {
      if (!['pending', 'in-progress', 'completed'].includes(filters.status)) {
        throw new Error('Invalid status filter');
      }
    }

    const [todos, totalCount] = await Promise.all([
      db.collection(COLLECTION_NAME)
        .find(filters)
        .sort(sortOptions)
        .skip(skip)
        .limit(limit)
        .toArray(),
      db.collection(COLLECTION_NAME)
        .countDocuments(filters)
    ]);

    return {
      todos,
      pagination: {
        total: totalCount,
        page,
        limit,
        pages: Math.ceil(totalCount / limit)
      }
    };
  },

  async update(id, updateData) {
    if (!ObjectId.isValid(id)) {
      throw new Error('Invalid todo ID');
    }

    const errors = validateTodoData(updateData);
    if (errors.length > 0) {
      throw new Error(`Validation failed: ${errors.join(', ')}`);
    }

    const db = getDB();
    const existingTodo = await this.findById(id);

    // Business logic for status changes
    if (updateData.status === 'completed' && existingTodo.status !== 'completed') {
      updateData.completedAt = new Date();
    }
    if (updateData.status && updateData.status !== 'completed') {
      updateData.completedAt = null;
    }

    const result = await db.collection(COLLECTION_NAME).findOneAndUpdate(
      { _id: new ObjectId(id) },
      { 
        $set: {
          ...updateData,
          updatedAt: new Date()
        }
      },
      { returnDocument: 'after' }
    );

    if (!result.value) {
      throw new Error('Todo not found');
    }

    return result.value;
  },

  async delete(id) {
    if (!ObjectId.isValid(id)) {
      throw new Error('Invalid todo ID');
    }

    const db = getDB();
    const result = await db.collection(COLLECTION_NAME).deleteOne({
      _id: new ObjectId(id)
    });

    if (result.deletedCount === 0) {
      throw new Error('Todo not found');
    }

    return true;
  },

  // Additional business logic methods
  async markAsComplete(id) {
    return await this.update(id, { 
      status: 'completed'
    });
  },

  async findOverdue() {
    const db = getDB();
    return await db.collection(COLLECTION_NAME).find({
      dueDate: { $lt: new Date() },
      status: { $ne: 'completed' }
    }).toArray();
  }
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمونه همه DAO

الگوی DAO

  • منطق دسترسی به داده ها از منطق تجاری جدا شده است.
  • لایه سرویس برای منطق تجاری و لایه DAO برای دسترسی به داده ها.
  • برای آزمایش با تمسخر لایه دسترسی به داده بهتر است.
  • تزریق وابستگی را بهتر پشتیبانی می کند.
  • پیاده سازی پیچیده تر و تکرار کد.

نمودار پری دریایی

src/
├── config/
│   └── database.js         # Database connection configuration
│
├── models/
│   └── todo.entity.js      # Data structure/schema definition
│
├── daos/
│   └── todo.dao.js         # Data Access Object - handles database operations
│
├── services/
│   └── todo.service.js     # Business logic layer
│
├── controllers/
│   └── todo.controller.js  # Request handling & response formatting
│
├── routes/
│   └── todo.routes.js      # Route definitions
│
└── app.js                  # Application entry point
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پیکربندی پایگاه داده

// src/config/database.js
const { MongoClient } = require('mongodb');

class Database {
  constructor(config) {
    this.config = {
      url: config.url || process.env.MONGODB_URI || 'mongodb://localhost:27017',
      dbName: config.dbName || process.env.DB_NAME || 'todoapp',
      options: {
        useUnifiedTopology: true,
        ...config.options
      }
    };
    this.client = null;
    this.db = null;
  }

  async connect() {
    try {
      this.client = await MongoClient.connect(this.config.url, this.config.options);
      this.db = this.client.db(this.config.dbName);
      console.log('Connected to MongoDB successfully');
      return this.db;
    } catch (error) {
      console.error('MongoDB connection error:', error);
      throw new DatabaseError('Failed to connect to database', error);
    }
  }

  async disconnect() {
    try {
      if (this.client) {
        await this.client.close();
        this.client = null;
        this.db = null;
        console.log('Disconnected from MongoDB');
      }
    } catch (error) {
      console.error('MongoDB disconnection error:', error);
      throw new DatabaseError('Failed to disconnect from database', error);
    }
  }

  getDB() {
    if (!this.db) {
      throw new DatabaseError('Database not initialized. Call connect() first.');
    }
    return this.db;
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

موجودیت

// src/models/todo.entity.js
class Todo {
  static STATUS = {
    PENDING: 'pending',
    IN_PROGRESS: 'in-progress',
    COMPLETED: 'completed'
  };

  static PRIORITY = {
    LOW: 'low',
    MEDIUM: 'medium',
    HIGH: 'high'
  };

  constructor(data = {}) {
    this._id = data._id || null;
    this.title = data.title || '';
    this.description = data.description || '';
    this.status = data.status || Todo.STATUS.PENDING;
    this.priority = data.priority || Todo.PRIORITY.MEDIUM;
    this.dueDate = data.dueDate ? new Date(data.dueDate) : null;
    this.createdAt = data.createdAt ? new Date(data.createdAt) : new Date();
    this.updatedAt = data.updatedAt ? new Date(data.updatedAt) : new Date();
    this.completedAt = data.completedAt ? new Date(data.completedAt) : null;
    this.tags = Array.isArray(data.tags) ? [...data.tags] : [];
    this.assignedTo = data.assignedTo || null;
  }

  validate() {
    const errors = [];

    if (!this.title?.trim()) {
      errors.push('Title is required');
    } else if (this.title.trim().length < 3) {
      errors.push('Title must be at least 3 characters long');
    }

    if (this.status && !Object.values(Todo.STATUS).includes(this.status)) {
      errors.push(`Invalid status. Must be one of: ${Object.values(Todo.STATUS).join(', ')}`);
    }

    if (this.dueDate) {
      if (!(this.dueDate instanceof Date) || isNaN(this.dueDate.getTime())) {
        errors.push('Invalid due date format');
      } else if (this.dueDate < new Date()) {
        errors.push('Due date cannot be in the past');
      }
    }

    if (this.priority && !Object.values(Todo.PRIORITY).includes(this.priority)) {
      errors.push(`Invalid priority. Must be one of: ${Object.values(Todo.PRIORITY).join(', ')}`);
    }

    if (this.tags && !Array.isArray(this.tags)) {
      errors.push('Tags must be an array');
    }

    return errors;
  }

  isOverdue() {
    return this.dueDate && this.dueDate < new Date() && this.status !== Todo.STATUS.COMPLETED;
  }

  toJSON() {
    return {
      _id: this._id,
      title: this.title,
      description: this.description,
      status: this.status,
      priority: this.priority,
      dueDate: this.dueDate,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt,
      completedAt: this.completedAt,
      tags: this.tags,
      assignedTo: this.assignedTo
    };
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کلاس خطاهای اعتبارسنجی

// src/errors/index.js
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
    this.status = 400;
  }
}

class DatabaseError extends Error {
  constructor(message, originalError = null) {
    super(message);
    this.name = 'DatabaseError';
    this.status = 500;
    this.originalError = originalError;
  }
}

class NotFoundError extends Error {
  constructor(message) {
    super(message);
    this.name = 'NotFoundError';
    this.status = 404;
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

DAO

مبتنی بر کلاس با تزریق سازنده

این سبک یک کلاس BaseDAO را تعریف می کند که یک رابط تمیز و قابل استفاده مجدد با متدهایی مانند findOne، insertOne و updateOne ارائه می کند. هر DAO (مانند TodoDAO) BaseDAO را گسترش می دهد و در یک نام مجموعه خاص عبور می کند

نمودار پری دریایی

جوانب مثبت:

  • تمیز و قابل استفاده مجدد: BaseDAO یک پایه خوب است، به خصوص اگر چندین DAO دارید که از ساختارهای مشابه پیروی می کنند.
  • اعتبار سنجی سازنده: اطمینان حاصل می‌کند که db و collectionName ارائه شده‌اند، که کمک می‌کند مشکلات را زودتر شناسایی کنید.
  • خوانایی و قابلیت نگهداری: عملیات CRUD به خوبی کپسوله شده است و می تواند به راحتی در DAO های مختلف با گسترش BaseDAO دوباره استفاده شود.

معایب:

  • کد دیگ بخار بیشتر
  • هر DAO باید BaseDAO را گسترش دهد، که ممکن است انعطاف پذیری را برای مواردی که یک DAO ممکن است نیاز به مقداردهی اولیه اضافی یا یک ساختار منحصر به فرد داشته باشد، محدود کند.
// src/daos/base.dao.js
class BaseDAO {
  constructor(db, collectionName) {
    if (!db) {
      throw new Error('Database connection is required');
    }
    if (!collectionName) {
      throw new Error('Collection name is required');
    }
    this.db = db;
    this.collection = this.db.collection(collectionName);
  }

  async findOne(filter) {
    try {
      return await this.collection.findOne(filter);
    } catch (error) {
      throw new DatabaseError('Database query failed', error);
    }
  }

  async find(filter = {}, options = {}) {
    try {
      return await this.collection.find(filter, options).toArray();
    } catch (error) {
      throw new DatabaseError('Database query failed', error);
    }
  }

  async insertOne(data) {
    try {
      return await this.collection.insertOne(data);
    } catch (error) {
      throw new DatabaseError('Database insert failed', error);
    }
  }

  async updateOne(filter, update, options = {}) {
    try {
      return await this.collection.updateOne(filter, update, options);
    } catch (error) {
      throw new DatabaseError('Database update failed', error);
    }
  }

  async deleteOne(filter) {
    try {
      return await this.collection.deleteOne(filter);
    } catch (error) {
      throw new DatabaseError('Database delete failed', error);
    }
  }
}

// src/daos/todo.dao.js
class TodoDAO extends BaseDAO {
  constructor(db) {
    super(db, 'todos');
  }

  async create(todoData) {
    const todo = new Todo(todoData);
    const errors = todo.validate();

    if (errors.length > 0) {
      throw new ValidationError(errors.join(', '));
    }

    const todoToInsert = {
      ...todo.toJSON(),
      title: todo.title.trim(),
      createdAt: new Date(),
      updatedAt: new Date()
    };

    try {
      const result = await this.insertOne(todoToInsert);
      return new Todo({ ...todoToInsert, _id: result.insertedId });
    } catch (error) {
      throw new DatabaseError('Failed to create todo', error);
    }
  }

  async findById(id) {
    if (!ObjectId.isValid(id)) {
      throw new ValidationError('Invalid todo ID');
    }

    const todo = await this.findOne({ _id: new ObjectId(id) });

    if (!todo) {
      throw new NotFoundError('Todo not found');
    }

    return new Todo(todo);
  }

  async find(query = {}, options = {}) {
    const { 
      page = 1, 
      limit = 10,
      sortBy = 'createdAt',
      sortOrder = -1,
      status,
      priority,
      searchTerm,
      fromDate,
      toDate,
      tags
    } = options;

    if (page < 1 || limit < 1) {
      throw new ValidationError('Invalid pagination parameters');
    }

    const filter = this._buildFilter({
      ...query,
      status,
      priority,
      searchTerm,
      fromDate,
      toDate,
      tags
    });

    const skip = (page - 1) * limit;
    const sortOptions = { [sortBy]: sortOrder };

    try {
      const [todos, totalCount] = await Promise.all([
        this.collection
          .find(filter)
          .sort(sortOptions)
          .skip(skip)
          .limit(limit)
          .toArray(),
        this.collection.countDocuments(filter)
      ]);

      return {
        todos: todos.map(todo => new Todo(todo)),
        pagination: {
          total: totalCount,
          page,
          limit,
          pages: Math.ceil(totalCount / limit)
        }
      };
    } catch (error) {
      throw new DatabaseError('Failed to fetch todos', error);
    }
  }

  async update(id, updateData) {
    const existingTodo = await this.findById(id);

    const updatedTodo = new Todo({
      ...existingTodo,
      ...updateData,
      _id: existingTodo._id,
      updatedAt: new Date()
    });

    const errors = updatedTodo.validate();
    if (errors.length > 0) {
      throw new ValidationError(errors.join(', '));
    }

    const result = await this.updateOne(
      { _id: new ObjectId(id) },
      { $set: updatedTodo.toJSON() },
      { returnDocument: 'after' }
    );

    if (result.matchedCount === 0) {
      throw new NotFoundError('Todo not found');
    }

    return updatedTodo;
  }

  async delete(id) {
    if (!ObjectId.isValid(id)) {
      throw new ValidationError('Invalid todo ID');
    }

    const result = await this.deleteOne({ _id: new ObjectId(id) });

    if (result.deletedCount === 0) {
      throw new NotFoundError('Todo not found');
    }

    return true;
  }

  _buildFilter(options) {
    const filter = {};

    if (options.status) {
      if (!Object.values(Todo.STATUS).includes(options.status)) {
        throw new ValidationError('Invalid status filter');
      }
      filter.status = options.status;
    }

    if (options.priority) {
      if (!Object.values(Todo.PRIORITY).includes(options.priority)) {
        throw new ValidationError('Invalid priority filter');
      }
      filter.priority = options.priority;
    }

    if (options.searchTerm) {
      filter.$or = [
        { title: { $regex: options.searchTerm, $options: 'i' } },
        { description: { $regex: options.searchTerm, $options: 'i' } }
      ];
    }

    if (options.fromDate || options.toDate) {
      filter.dueDate = {};
      if (options.fromDate) {
        filter.dueDate.$gte = new Date(options.fromDate);
      }
      if (options.toDate) {
        filter.dueDate.$lte = new Date(options.toDate);
      }
    }

    if (options.tags && Array.isArray(options.tags)) {
      filter.tags = { $all: options.tags };
    }

    return filter;
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

روش استاتیک با اتصال تزریقی

استفاده ساده شده: روش‌های استاتیک نیاز به نمونه‌سازی کلاس را برطرف می‌کند و سربار حافظه را کاهش می‌دهد.

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

قابلیت استفاده مجدد محدود: روش‌های استاتیک از وراثت پشتیبانی نمی‌کنند، بنابراین متمرکز کردن منطق مشترک بین DAOها دشوارتر است.

import { ObjectId } from 'mongodb';

let todos;

export default class TodoDAO {
  static async injectDB(conn) {
    if (todos) return;
    try {
      todos = await conn.db(process.env.DB_NAME).collection('todos');
    } catch (error) {
      console.error(`Unable to establish collection handles in TodoDAO: ${error}`);
    }
  }

  static async create(todoData) {
    // Initialize and validate the Todo, build the data object, and insert into the collection
  }

  static async findById(id) {
    // Validate ID, query by `_id`, throw `NotFoundError` if not found
  }

  static async find(query = {}, options = {}) {
    // Construct filter, pagination, and sort options, execute the find query with pagination
  }

  static async update(id, updateData) {
    // Fetch the todo, validate and update with `updateOne`
  }

  static async delete(id) {
    // Validate ID, delete by `_id`, and handle `NotFoundError`
  }

  static _buildFilter(options) {
    // Generate the filter object for queries based on status, priority, search terms, etc.
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خدمات

// src/services/todo.service.js
class TodoService {
  constructor(todoDAO) {
    if (!todoDAO) {
      throw new Error('TodoDAO is required');
    }
    this.todoDAO = todoDAO;
  }

  async createTodo(todoData) {
    return await this.todoDAO.create(todoData);
  }

  async getTodoById(id) {
    return await this.todoDAO.findById(id);
  }

  async updateTodo(id, updateData) {
    return await this.todoDAO.update(id, updateData);
  }

  async deleteTodo(id) {
    return await this.todoDAO.delete(id);
  }

  async updateTodoStatus(id, status) {
    const todo = await this.todoDAO.findById(id);

    const updates = { 
      status,
      updatedAt: new Date()
    };

    if (status === Todo.STATUS.COMPLETED && todo.status !== Todo.STATUS.COMPLETED) {
      updates.completedAt = new Date();
    } else if (status !== Todo.STATUS.COMPLETED && todo.status === Todo.STATUS.COMPLETED) {
      updates.completedAt = null;
    }

    return await this.todoDAO.update(id, updates);
  }

  async findTodos(options = {}) {
    return await this.todoDAO.find({}, options);
  }

  async findOverdueTodos() {
    const now = new Date();
    return await this.todoDAO.find({
      dueDate: { $lt: now },
      status: { $ne: Todo.STATUS.COMPLETED }
    });
  }

  async findTodosByPriority(priority) {
    return await this.todoDAO.find({ priority });
  }

  async assignTodo(id, userId) {
    return await this.todoDAO.update(id, {
      assignedTo: userId,
      updatedAt: new Date()
    });
  }

  async addTags(id, tags) {
    const todo = await this.todoDAO.findById(id);
    const uniqueTags = [...new Set([...todo.tags, ...tags])];
    return await this.todoDAO.update(id, { tags: uniqueTags });
  }

  async removeTags(id, tags) {
    const todo = await this.todoDAO.findById(id);
    const updatedTags = todo.tags.filter(tag => !tags.includes(tag));
    return await this.todoDAO.update(id, { tags: updatedTags });
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمایه سازی

نمایه سازی در MongoDB عملکرد پرس و جو را با ایجاد ساختارهای داده کارآمد برای بازیابی سریعتر بهبود می بخشد.

نمودار پری دریایی

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

شاخص تک فیلد

برای ایجاد ایندکس، از createIndex() روش

db.collection('users').createIndex({ name: 1 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاخص مرکب

یک شاخص ترکیبی در MongoDB یک نمایه در چندین فیلد در یک سند است.

نمودار پری دریایی

db.collection('users').createIndex({ name: 1, age: 1, city: 1, country: 1, hobbies: 1 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

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

db.collection('users').find({ name: 'Alice', age: 25 });
db.collection('users').find({ name: 'Alice', age: 25, city: 'New York' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاخص چند کلیدی

یک شاخص چند کلیدی در MongoDB یک شاخص در یک فیلد آرایه است.

نمودار پری دریایی

db.collection('users').createIndex({ hobbies: 1 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

محدودیت های شاخص های چند کلیدی

  • اگر بیش از یک فیلد یک آرایه باشد، نمی توانید یک شاخص ترکیبی ایجاد کنید.
db.collection('users').createIndex({ "hobbies": 1, "tags": 1 }); // Not allowed if both hobbies and tags are arrays
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فهرست متن

فهرست های متنی در MongoDB راهی برای انجام پرس و جوهای جستجوی متنی در محتوای رشته ارائه می دهند. هر مجموعه می تواند حداکثر یک فهرست متنی داشته باشد.

db.products.createIndex({ 
  name: "text", 
  description: "text", 
  tags: "text" 
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

// Sample Data
db.products.insertMany([
  {
    name: "MacBook Pro 16-inch",
    description: "Powerful laptop for developers",
    tags: ["apple", "laptop", "computer"]
  },
  {
    name: "iPhone 15 Pro",
    description: "Latest smartphone with great camera",
    tags: ["apple", "smartphone", "mobile"]
  },
  {
    name: "Gaming Laptop ROG",
    description: "High-performance gaming laptop",
    tags: ["asus", "laptop", "gaming"]
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تطابق دقیق کلمات

db.products.find({ $text: { $search: "laptop" }}); // Will find MacBook and ROG
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چند کلمه (OR)

// By default, multiple words are treated as logical OR
// Will find documents containing "laptop" or "gaming"
db.products.find({ $text: { $search: "laptop gaming" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چند کلمه (منطقی و)

// Using the plus sign to ensure both terms are present
// Will find documents containing both "laptop" and "gaming"
db.products.find({ $text: { $search: "+laptop +gaming" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

عبارات دقیق

// Using double quotes to search for an exact phrase
// Will find documents containing the exact phrase "gaming laptop"
db.products.find({ $text: { $search: "\"gaming laptop\"" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف کلمات
db.products.find({ $text: { $search: “laptop -gaming” }}); // لپ تاپ اما نه گیمینگ

مسابقات بدون حروف بزرگ

// Text search is case-insensitive by default
// Will find all documents containing "laptop" regardless of case
db.products.find({ $text: { $search: "LAPTOP" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دیاکریتیک غیر حساس

// Text search ignores diacritic marks by default
// Will match "cafe" and "café"
db.products.find({ $text: { $search: "café" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مطابقت جزئی کلمه

// Searching for "lap" won't find "laptop"
db.products.find({ $text: { $search: "lap" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوهای حروف عامیانه

// Wildcards are not supported in text search
db.products.find({ $text: { $search: "lap*" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تطبیق فازی (اشتباهات تایپی)

// Regular expressions cannot be used inside $text search
db.products.find({ $text: { $search: "/lap.*/" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

عبارات بولی پیچیده

// Nested boolean logic is not supported in $text search
db.products.find({ $text: { $search: "(laptop AND gaming) OR (smartphone AND camera)" }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوهای حساس به حروف کوچک و بزرگ

// Cannot perform case-sensitive searches using $text
db.products.find({ $text: { $search: "MacBook", caseSensitive: true }});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوی اطلس

Atlas Search یک سرویس جستجوی کاملاً مدیریت شده است که به شما امکان می دهد قابلیت های جستجوی سریع، مرتبط و متن کامل را در بالای داده های MongoDB خود ایجاد کنید. در مقایسه با فهرست‌های متنی، جستجوی اطلس ویژگی‌های پیشرفته‌تری مانند جستجوی وجهی، تطبیق فازی و امتیازدهی مرتبط را فراهم می‌کند.

//  Basic Atlas Search Setup
db.products.createSearchIndex({
  "mappings": {
    "dynamic": true,
    "fields": {
      "name": {
        "type": "string",
        "analyzer": "lucene.standard"
      },
      "description": {
        "type": "string",
        "analyzer": "lucene.english"
      }
    }
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تطبیق فازی – غلط املایی و غلط املایی را کنترل می کند

db.products.aggregate([
  {
    $search: {
      text: {
        query: "labtop",  // Will match "laptop"
        path: "name",
        fuzzy: { maxEdits: 1 }
      }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تکمیل خودکار – پیشنهادات در زمان واقعی

db.products.aggregate([
  {
    $search: {
      autocomplete: {
        query: "mac",     // Will suggest "macbook", "machine", etc.
        path: "name"
      }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

امتیازدهی سفارشی – ارتباط را افزایش دهید

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

db.products.aggregate([
  {
    $search: {
      compound: {
        must: [
          { text: { query: "laptop", path: "name" } }
        ],
        should: [
          {
            text: {
              query: "gaming",
              path: "category",
              score: { boost: { value: 2.0 } }  // Boost gaming laptops
            }
          }
        ]
      }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • اسناد باید حاوی “لپ تاپ” در قسمت نام باشند
  • اسناد حاوی “بازی” در قسمت توضیحات تقویت می شوند.

پشتیبانی چند زبانه

db.products.aggregate([
  {
    $search: {
      text: {
        query: "ordinateur portable",  // French for "laptop"
        path: "description",
        analyzer: "lucene.french"
      }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جستجوی وجهی – نتایج را فیلتر کنید

جستجوی وجهی به کاربران اجازه می دهد تا نتایج جستجو را بر اساس دسته بندی ها، محدوده قیمت و سایر ویژگی ها فیلتر کنند.

db.products.aggregate([
  {
    $searchMeta: {
      facet: {
        operator: { text: { query: "laptop", path: "description" } },
        facets: {
          categories: { type: "string", path: "category" },
          priceRanges: {
            type: "number",
            path: "price",
            boundaries: [500, 1000, 2000]
          }
        }
      }
    }
  }
]);

**Facets:**
- categoryFacet: Groups results by the category field.
- priceFacet: Groups results into price ranges defined by the boundaries.


وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مبادلات

علاوه بر مزایا، جستجوی اطلس در مقایسه با نمایه‌های متنی سنتی دارای برخی معاوضه‌ها نیز است.

  • به خوشه های M10+ نیاز دارد
  • هزینه اضافی
  • راه اندازی پیچیده تر
  • استفاده بیشتر از منابع

عملیات آرایه

MongoDB از انواع عملیات آرایه برای کار با آرایه ها در اسناد پشتیبانی می کند.

نمودار پری دریایی

افزودن عناصر به آرایه

برای افزودن عناصر به آرایه در یک سند، از $push اپراتور

db.collection('users').updateOne({ name: 'Alice' }, { $push: { hobbies: 'Reading' } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پرس و جو آرایه ها با $elemMatch

با توجه به مجموعه ای از دانش آموزان با نمرات در موضوعات مختلف:

{
  "_id": ObjectId("64d39a7a8b0e8c284a2c1234"),
  "name": "Alice",
  "scores": [
    { "subject": "Math", "score": 95 },
    { "subject": "English", "score": 88 }
  ]
},
{
  "_id": ObjectId("64d39a808b0e8c284a2c1235"),
  "name": "Bob",
  "scores": [
    { "subject": "Math", "score": 78 },
    { "subject": "English", "score": 92 }
  ]
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پرس و جو با $elemMatch:

برای یافتن دانش‌آموزانی که به طور خاص در ریاضی نمره 90 کسب کرده‌اند، نیاز داریم $elemMatch:

db.students.find({ 
  scores: { 
    $elemMatch: { subject: "Math", score: { $gt: 90 } } 
  } 
})
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه: این فقط آلیس را برمی گرداند، زیرا او تنها دانش آموز با نمره بالای 90 در درس “ریاضی” است. $elemMatch آن را تضمین می کند همه شرایط درون عنصر آرایه باید برآورده شود.

بدون $elemMatch – این امر هم آلیس و هم باب را برمی گرداند، زیرا هر دو در دروس مختلف و نه لزوماً در درس “ریاضی” نمرات بالای 90 دارند.

نمودار پری دریایی

اضافه کردن منحصر به فرد – $addToSet

را $addToSet عملگر در MongoDB برای افزودن عناصر به آرایه تنها در صورتی استفاده می شود که از قبل وجود نداشته باشند. این از ورودهای تکراری در آرایه جلوگیری می کند.

db.collection('users').updateOne({ name: 'Alice' }, { $addToSet: { hobbies: 'Reading' } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چندگانه اضافه کنید $push و $each

را $push عملگر در MongoDB برای افزودن عناصر به آرایه استفاده می شود. را $each modifier به شما اجازه می دهد چندین عنصر را به آرایه اضافه کنید.

db.collection('users').updateOne({ name: 'Alice' }, { $push: { hobbies: { $each: ['Reading', 'Swimming'] } } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن مرتب شده – $push و $sort

را $push عملگر در MongoDB برای افزودن عناصر به آرایه استفاده می شود. را $sort modifier به شما اجازه می دهد تا عناصر آرایه را مرتب کنید.

db.collection('users').updateOne({ name: 'Alice' }, { $push: { scores: { $each: [85, 90], $sort: -1 } } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فشار و مرتب سازی آرایه از اشیاء بر اساس یک فیلد خاص

db.collection('users').updateOne(
  { name: 'Alice' },
  {
    $push: {
      scores: {
        $each: [
          { score: 85, date: "2023-03-01" },
          { score: 90, date: "2023-04-01" }
        ],
        $sort: { date: -1 }
      }
    }
  }
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

افزودن محدود – $push و $slice

را $push عملگر در MongoDB برای افزودن عناصر به آرایه استفاده می شود. را $slice اصلاح کننده به شما امکان می دهد تعداد عناصر موجود در آرایه را محدود کنید.

db.collection('users').updateOne({ name: 'Alice' }, { $push: { scores: { $each: [85, 90], $slice: -3 } } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اگر آلیس در حال حاضر یک آرایه نمرات مانند [70, 75, 80]، این کوئری 85 و 90 را فشار می دهد و آن را ایجاد می کند [70, 75, 80, 85, 90]. سپس $slice: -3 آن را به 3 عنصر آخر برش می‌دهد و در نتیجه [80, 85, 90].

حذف عناصر از یک آرایه

برای حذف عناصر از آرایه در یک سند، از $pull اپراتور


db.collection('users').updateOne({ name: 'Alice' }, { $pull: { hobbies: 'Reading' } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اولین یا آخرین عنصر را حذف کنید – $pop

را $pop عملگر در MongoDB برای حذف اولین یا آخرین عنصر از یک آرایه استفاده می شود.

db.collection('users').updateOne({ name: 'Alice' }, { $pop: { hobbies: 1 } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف چندگانه – $pullAll

را $pullAll عملگر در MongoDB برای حذف تمام رخدادهای مقادیر مشخص شده از یک آرایه استفاده می شود.

db.collection('users').updateOne({ name: 'Alice' }, { $pullAll: { hobbies: ['Reading', 'Swimming'] } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف چندگانه – $pull و $in

را $pull عملگر در MongoDB برای حذف عناصر از یک آرایه استفاده می شود. را $in modifier به شما امکان می دهد چندین مقدار را برای حذف مشخص کنید.

db.collection('users').updateOne({ name: 'Alice' }, { $pull: { hobbies: { $in: ['Reading', 'Swimming'] } } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف شرط – $pull و $gt

را $pull عملگر در MongoDB برای حذف عناصر از یک آرایه استفاده می شود. را $gt modifier به شما اجازه می دهد تا یک شرط برای حذف عناصر مشخص کنید.

db.collection('users').updateOne({ name: 'Alice' }, { $pull: { scores: { $gt: 85 } } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف نه برابر – $pull و $ne

را $pull عملگر در MongoDB برای حذف عناصر از یک آرایه استفاده می شود. را $ne modifier به شما اجازه می دهد تا یک شرط برای حذف عناصری که برابر با یک مقدار نیستند، تعیین کنید.

db.collection('users').updateOne({ name: 'Alice' }, { $pull: { scores: { $ne: 85 } } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شرایط به روز رسانی – $set و $

را $set عملگر در MongoDB برای به روز رسانی فیلدهای یک سند استفاده می شود. را $ عملگر موقعیتی به شما امکان می دهد اولین عنصری را که با یک شرط در یک آرایه مطابقت دارد به روز کنید.

db.collection('users').updateOne({ name: 'Alice', 'scores.subject': 'Math' }, { $set: { 'scores.$.score': 90 } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی همه – $[]

را $[] عملگر در MongoDB برای به روز رسانی تمام عناصر یک آرایه که با یک شرط مطابقت دارند استفاده می شود.

db.collection('users').updateOne({ name: 'Alice' }, { $set: { 'scores.$[].score': 90 } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای افزایش یک فیلد در تمام عناصر یک آرایه، می توانید از $[] اپراتور با $inc اپراتور

db.collection('users').updateOne({ name: 'Alice' }, { $inc: { 'scores.$[].score': 5 } });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی فیلتر شده – $[]

را $[] عملگر در MongoDB برای به روز رسانی عناصر در یک آرایه که با یک شرط مطابقت دارند استفاده می شود.

db.collection('users').updateOne({ name: 'Alice' }, { $set: { 'scores.$[elem].score': 90 } }, { arrayFilters: [{ 'elem.subject': 'Math' }] });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تجمیع

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

خط لوله تجمع

چارچوب تجمیع در MongoDB از یک رویکرد خط لوله استفاده می کند، که در آن چندین مرحله اسناد را تغییر می دهد.

db.collection('orders').aggregate([
  { $match: { status: 'A' } },
  { $group: { _id: '$cust_id', total: { $sum: '$amount' } } },
  { $sort: { total: -1 } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمودار پری دریایی

به مجموعه ها بپیوندید

MongoDB از اتصالات مانند پایگاه داده های رابطه ای پشتیبانی نمی کند. در عوض، شما می توانید استفاده کنید $lookup عملگر برای انجام یک اتصال بیرونی سمت چپ بین دو مجموعه.

db.collection('orders').aggregate([
  {
    $lookup: {
      from: 'customers',
      localField: 'cust_id',
      foreignField: '_id',
      as: 'customer'
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نمودار پری دریایی

باز کردن آرایه ها

را $unwind عملگر در MongoDB برای تجزیه یک فیلد آرایه به چندین سند استفاده می شود.

db.collection('orders').aggregate([
  { $unwind: '$items' }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باز کردن و گروه

db.collection('orders').aggregate([
  { $unwind: '$items' },
  {
    $group: {
      _id: '$items.productId',
      totalQuantity: { $sum: '$items.quantity' },
      totalRevenue: { $sum: { $multiply: ['$items.price', '$items.quantity'] } },
      ordersCount: { $sum: 1 }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باز کردن چند آرایه

db.collection('restaurants').aggregate([
  { $unwind: '$categories' },
  { $unwind: '$reviews' },
  {
    $group: {
      _id: '$categories',
      averageRating: { $avg: '$reviews.rating' },
      reviewCount: { $sum: 1 }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اسناد گروهی

را $group عملگر در MongoDB برای گروه بندی اسناد توسط یک کلید مشخص استفاده می شود.

db.collection('orders').aggregate([
  { $group: { _id: '$cust_id', total: { $sum: '$amount' } } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و شمارش

db.collection('orders').aggregate([
  { $group: { _id: '$status', count: { $sum: 1 } } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و جمع

db.collection('orders').aggregate([
  { $group: { _id: '$status', total: { $sum: '$amount' } } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و میانگین

db.collection('orders').aggregate([
  { $group: { _id: '$status', average: { $avg: '$amount' } } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه و فشار

db.collection('orders').aggregate([
  { $group: { _id: '$cust_id', items: { $push: '$item' } } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه با فیلدهای متعدد

db.collection('orders').aggregate([
  {
    $group: {
      _id: { 
        status: '$status',
        category: '$category'
      },
      count: { $sum: 1 },
      totalAmount: { $sum: '$amount' }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گروه با عملیات تاریخ

db.collection('orders').aggregate([
  {
    $group: {
      _id: {
        year: { $year: '$orderDate' },
        month: { $month: '$orderDate' }
      },
      totalOrders: { $sum: 1 },
      revenue: { $sum: '$amount' }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

زمینه های پروژه

را $project عملگر در MongoDB برای گنجاندن، حذف یا تغییر نام فیلدها در اسناد خروجی استفاده می شود.

db.collection('orders').aggregate([
  { $project: { _id: 0, cust_id: 1, amount: 1 } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با فیلدهای محاسبه شده

db.collection('orders').aggregate([
  {
    $project: {
      _id: 0,
      cust_id: 1,
      amount: 1,
      discount: { $subtract: ['$total', '$amount'] }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با عملیات آرایه

db.collection('orders').aggregate([
  {
    $project: {
      orderId: 1,
      itemCount: { $size: '$items' },
      firstItem: { $arrayElemAt: ['$items', 0] },
      lastItem: { $arrayElemAt: ['$items', -1] },
      items: {
        $map: {
          input: '$items',
          as: 'item',
          in: {
            name: '$$item.name',
            subtotal: {
              $multiply: ['$$item.price', '$$item.quantity']
            }
          }
        }
      }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با عملیات رشته

db.collection('users').aggregate([
  {
    $project: {
      fullName: { $concat: ['$firstName', ' ', '$lastName'] },
      email: { $toLower: '$email' },
      age: { $toString: '$age' }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پروژه با فیلدهای شرطی

db.collection('users').aggregate([
  {
    $project: {
      name: 1,
      status: {
        $cond: {
          if: { $gte: ['$age', 18] },
          then: 'Adult',
          else: 'Minor'
        }
      }
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چندین تجمع را اجرا کنید

شما می توانید چندین خط لوله تجمیع را در یک پرس و جو با استفاده از $facet اپراتور

db.collection('orders').aggregate([
  {
    $facet: {
      totalAmount: [
        { $group: { _id: null, total: { $sum: '$amount' } } }
      ],
      averageAmount: [
        { $group: { _id: null, average: { $avg: '$amount' } } }
      ]
    }
  }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

معاملات

تراکنش ها در MongoDB به شما این امکان را می دهند که چندین عملیات را به عنوان یک واحد کار، همه یا هیچ انجام دهید. آنها یکپارچگی و سازگاری داده ها را در اسناد و مجموعه های متعدد تضمین می کنند.

نمودار پری دریایی

ویژگی های تراکنش (ACID)

نمودار پری دریایی

استفاده از تراکنش ها

برای استفاده از تراکنش ها در MongoDB، معمولاً این مراحل را دنبال کنید:

  1. یک جلسه را شروع کنید
  2. یک معامله را شروع کنید
  3. عملیات را انجام دهد
  4. معامله را متعهد یا لغو کنید
// Define a client

const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
//...

// Start a session

const session = client.startSession();

try {
  session.startTransaction();

  // Perform multiple operations
  await collection1.updateOne({ _id: 1 }, { $set: { status: 'processing' } }, { session });
  await collection2.insertOne({ orderId: 1, items: ['item1', 'item2'] }, { session });

  // Commit the transaction
  await session.commitTransaction();
} catch (error) {
  // If an error occurred, abort the transaction
  await session.abortTransaction();
  console.error('Transaction aborted:', error);
} finally {
  // End the session
  session.endSession();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ملاحظات برای معاملات

  • عملکرد: تراکنش ها ممکن است بر عملکرد تأثیر بگذارد، به ویژه برای بارهای کاری سنگین.
  • تایم اوت: تراکنش ها دارای مهلت زمانی پیش فرض 60 ثانیه هستند.
  • مجموعه های ماکت: تراکنش ها نیاز به پیکربندی مجموعه ماکت دارند.
  • خوشه های خرد شده: معاملات روی خوشه های خرد شده ملاحظات و محدودیت های بیشتری دارند.

نمودار پری دریایی

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

مجموعه های ماکت

مجموعه replica گروهی از نمونه های MongoDB است که مجموعه داده های یکسانی را حفظ می کند. مجموعه‌های Replica افزونگی و در دسترس بودن بالا را فراهم می‌کنند.

اجزای یک مجموعه ماکت

  • اولیه: تمام عملیات نوشتن را دریافت می کند.
  • ثانویه: داده های اولیه را تکرار می کند. می تواند برای عملیات خواندن استفاده شود.
  • داور: در انتخابات مقدماتی شرکت می کند اما داده ای ندارد.

نمودار پری دریایی

پیکربندی مجموعه کپی

برای پیکربندی یک مجموعه کپی، از rs.initiate() روش

rs.initiate({
  _id: 'rs0',
  members: [
    { _id: 0, host: 'mongo1:27017' },
    { _id: 1, host: 'mongo2:27017' },
    { _id: 2, host: 'mongo3:27017', arbiterOnly: true }
  ]
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اولویت را بخوانید

اولویت خواندن در MongoDB نحوه توزیع عملیات خواندن در مجموعه replica را تعیین می کند.

نمودار پری دریایی

  • اولیه: از ابتدایی می خواند.
  • ثانویه: از ثانویه می خواند.
  • Primary Preferred: در صورت وجود از اولیه می خواند، در غیر این صورت از ثانویه.
  • ثانویه ترجیح داده شده است: در صورت موجود بودن از ثانویه می خواند، در غیر این صورت از اولیه می خواند.
  • نزدیکترین: از نزدیکترین عضو مجموعه ماکت می خواند.

db.collection('users').find().readPref('secondary');
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نگرانی را بنویسید

نگرانی نوشتن در MongoDB تعیین کننده سطح تایید برای عملیات نوشتن است.

نمودار پری دریایی

  • w: 0: بدون تایید
  • w: 1: قدردانی از ابتدایی.
  • w: اکثریت: تصدیق اکثریت مجموعه ماکت.

db.collection('users').insertOne({ name: 'Alice' }, { writeConcern: { w: 'major

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Failover خودکار

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

  • Primary بر اساس تعداد آرای اعضای مجموعه ماکت انتخاب می شود.
  • Secondary در صورتی که اولیه در دسترس نباشد می توان به ابتدایی ارتقا داد.
  • Arbiter برای شکستن تساوی در انتخابات استفاده می شود.

نمودار پری دریایی

Failover دستی

شما می توانید با وادار کردن یک عضو مجموعه replica برای تبدیل شدن به اصلی، یک Failover دستی را در MongoDB آغاز کنید.

rs.stepDown();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاردینگ

Sharding روشی برای توزیع داده ها در چندین ماشین است. این به شما امکان می دهد با افزودن ماشین های بیشتر به سیستم خود، مقیاس افقی را انجام دهید.

یک مجموعه به تکه‌هایی تقسیم می‌شود و هر تکه روی یک قطعه متفاوت ذخیره می‌شود.

هر Shard زیرمجموعه ای از داده ها در یک خوشه خرد شده است.

نمودار پری دریایی

اجزای شاردینگ

  • شارد: زیر مجموعه ای از داده ها در یک خوشه خرد شده.
  • سرور پیکربندی: فوق داده ها و تنظیمات پیکربندی را برای خوشه ذخیره می کند.
  • کوئری روتر: کوئری ها را به قطعه مناسب هدایت می کند.

نمودار پری دریایی

کلید شاردینگ

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

db.collection.createIndex({ _id: 'hashed' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هنگام انتخاب یک کلید خرد، فاکتورهای زیر را در نظر بگیرید:

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

استراتژی های کلید شارد

  • Hashed Sharding: با استفاده از یک تابع هش، داده ها را به طور یکنواخت در بین خرده ها توزیع می کند.
  • Range Sharding: داده ها را بر اساس محدوده ای از مقادیر در کلید خرده توزیع می کند.
  • شاردینگ مرکب: داده ها را بر اساس چند فیلد در کلید خرده توزیع می کند.
db.collection.createIndex({ _id: 'hashed' });
db.collection.createIndex({ date: 1 });
db.collection.createIndex({ country: 1, city: 1 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مانگوس

Mongoose یک کتابخانه Object Data Modeling (ODM) برای MongoDB و Node.js است. این یک راه حل مبتنی بر طرحواره برای مدل سازی داده های برنامه شما ارائه می دهد.

نمودار پری دریایی

اتصال به MongoDB

برای اتصال به MongoDB با استفاده از Mongoose، از connect() روش

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/myapp', { useNewUrlParser: true, useUnifiedTopology: true });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تعریف طرحواره

طرحواره Mongoose ساختار اسناد را در یک مجموعه تعریف می کند.

const userSchema = new mongoose.Schema({
  name: String,
  age: Number
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد یک مدل

مدل Mongoose کلاسی است که مجموعه ای را در MongoDB نشان می دهد.

const User = mongoose.model('User', userSchema);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

درج اسناد

برای درج یک سند در یک مجموعه، یک نمونه از مدل ایجاد می کنید و آن را فراخوانی می کنید save() روش

const user = new User({ name: 'Alice', age: 25 });

user.save();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استعلام اسناد

برای درخواست اسناد از یک مجموعه، از find() روش

User.find({ name: 'Alice' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با پروجکشن:

User.find({ name: 'Alice' }, { name: 1, age: 1 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به روز رسانی اسناد

برای به روز رسانی اسناد در یک مجموعه، از updateOne() روش

User.updateOne({ name: 'Alice' }, { age: 26 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حذف اسناد

برای حذف اسناد از مجموعه، از deleteOne() روش

User.deleteOne({ name: 'Alice' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

میان افزار

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

userSchema.pre('save', function(next) {
  console.log('Saving user...');
  next();
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مجازی

مجازی‌های Mongoose ویژگی‌های سندی هستند که می‌توانید آنها را دریافت و تنظیم کنید، اما در MongoDB حفظ نمی‌شوند.


userSchema.virtual('fullName').get(function() {
  return this.name + ' ' + this.age;
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پلاگین ها

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

const timestampPlugin = require('./plugins/timestamp');

userSchema.plugin(timestampPlugin);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

معاملات

معاملات Mongoose به شما این امکان را می دهد که چندین عملیات را روی چندین سند در یک تراکنش انجام دهید.


const session = await mongoose.startSession();
session.startTransaction();

try {
  await User.create({ name: 'Alice' }, { session });
  await User.create({ name: 'Bob' }, { session });

  await session.commitTransaction();
} catch (error) {
  await session.abortTransaction();
} finally {
  session.endSession();
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تجمیع

Mongoose یک API روان برای ساخت خطوط لوله تجمیع ارائه می دهد.

const result = await User.aggregate([
  { $match: { name: 'Alice' } },
  { $group: { _id: '$name', total: { $sum: '$age' } }
]);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

شاخص ها

Mongoose به شما این امکان را می دهد که شاخص ها را روی طرحواره های خود تعریف کنید.

userSchema.index({ name: 1 });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

جمعیت

جمعیت Mongoose به شما امکان می دهد به اسناد موجود در مجموعه های دیگر مراجعه کنید.

const userSchema = new mongoose.Schema({
  name: String,
  posts: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Post' }]
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اعتبار سنجی

Mongoose اعتبار سنجی داخلی را برای فیلدهای طرحواره فراهم می کند.

const userSchema = new mongoose.Schema({
  name: { type: String, required: true }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

الگوها

الگو توضیحات استفاده از مورد مزایا معایب
الگوی سطل اسناد مرتبط را در “سطل ها” یا آرایه هایی با اندازه ثابت گروه بندی می کند داده های سری زمانی، خوانش حسگر اینترنت اشیا – تعداد اسناد را کاهش می دهد
– عملکرد پرس و جو را برای اسکن محدوده بهبود می بخشد
– مجتمع برای به روز رسانی آیتم های فردی
– ممکن است منجر به رشد سند شود
الگوی صفت مجموعه ای از فیلدها را با الگوهای دسترسی مشابه به عنوان یک سند جاسازی شده ذخیره می کند محصولات با ویژگی های متفاوت – طرحواره انعطاف پذیر
– پرس و جو کارآمد از ویژگی های رایج
– پرس و جوهای پیچیده تر برای ویژگی های خاص
– پتانسیل برای زمینه های استفاده نشده
الگوی پرت داده های رایج را در یک مجموعه و داده های کمیاب و بزرگ را در مجموعه دیگر ذخیره می کند پست های رسانه های اجتماعی با سطوح تعامل متفاوت – برای عملکرد کیس معمولی بهینه می شود
– از مسائل مربوط به اندازه سند جلوگیری می کند
– برای موارد پرت به دو پرس و جو نیاز دارد
– منطق برنامه پیچیده تر
الگوی زیر مجموعه زیر مجموعه ای از فیلدها را از یک سند در مجموعه ای جداگانه ذخیره می کند نمایه‌های کاربر با فیلدهای پربازدید – عملکرد خواندن را برای پرس و جوهای رایج بهبود می بخشد
– اندازه مجموعه کاری را کاهش می دهد
– تکرار داده ها
– نیاز به همگام نگه داشتن زیر مجموعه ها دارد

پرسش و پاسخ

Exec() در Mongoose چیست؟

را exec() تابع در Mongoose برای اجرای یک پرس و جو و بازگشت یک وعده استفاده می شود. این به شما امکان می دهد تا متدهای پرس و جو را زنجیره ای کنید و سپس در پایان پرس و جو را اجرا کنید.

User.find({ name: 'Alice' }).exec();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

می توانید پرس و جو را بدون آن اجرا کنید exec()، با پاسخ به تماس یا استفاده از async/wait.

User.find({ name: 'Alice' }, (error, users) => {
  console.log(users);
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

const users = await User.find({ name: 'Alice' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چه فرقی با هم دارند findOne() و find() در مونگوس؟

  • find(): آرایه ای از تمام اسنادی را برمی گرداند که با معیارهای پرس و جو مطابقت دارند.
  • findOne(): اولین سندی را برمی گرداند که با معیارهای پرس و جو مطابقت دارد.

تفاوت بین Model.create() و new Model().save() در Mongoose چیست؟

  • Model.create(): یک سند جدید ایجاد می کند و آن را در یک مرحله در پایگاه داده ذخیره می کند.
User.create({ name: 'Alice' });
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  • new Model().save(): نمونه جدیدی از مدل را تکرار می کند اما بلافاصله آن را در پایگاه داده ذخیره نمی کند. قبل از فراخوانی .save() می توانید نمونه را تغییر دهید، اعتبارسنجی انجام دهید یا هر عملیات دیگری را اجرا کنید تا تغییرات ادامه یابد.
const doc = new Model({ name: 'John', age: 30 });
doc.age = 31; // Modify the document
await doc.save(); // Save the document after modification
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

هدف از متد lean() در کوئری های Mongoose چیست و چه زمانی باید از آن استفاده کرد؟

را lean() متد در جستارهای Mongoose، اشیاء جاوا اسکریپت ساده را به جای اسناد Mongoose برمی گرداند که دارای ویژگی های اضافی زیادی هستند، مانند دریافت کننده ها، تنظیم کننده ها و روش هایی که برای کار با سند مفید هستند. زمانی که به ویژگی‌های کامل سند Mongoose نیاز ندارید و می‌خواهید عملکرد پرس و جو را بهبود ببخشید، باید از آن استفاده کنید.

User.find({ name: 'Alice' }).lean();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چگونه حذف های نرم را در Mongoose پیاده سازی کنیم؟

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

const userSchema = new mongoose.Schema({
  name: String,
  deleted: { type: Boolean, default: false }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

از پیش میان افزار برای حذف اسناد حذف شده از نتایج پرس و جو استفاده کنید.

userSchema.pre(/^find/, function(next) {
  this.where({ deleted: false });
  next();
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

یک روش برای “حذف نرم” یک سند اضافه کنید.

userSchema.methods.softDelete = function() {
  this.deleted = true;
  return this.save();
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا