diff --git a/docs/angular/components.md b/docs/angular/components.md new file mode 100644 index 0000000..52a0037 --- /dev/null +++ b/docs/angular/components.md @@ -0,0 +1,80 @@ +# Angular Components + +## What is a Component? +A component controls a patch of screen called a view. It consists of: +- A TypeScript class (component class) +- An HTML template +- Optional CSS styles + +## Creating a Component +```bash +ng generate component my-component +# or shorthand +ng g c my-component +``` + +## Component Structure +```typescript +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-my-component', + templateUrl: './my-component.component.html', + styleUrls: ['./my-component.component.css'] +}) +export class MyComponent { + // Component logic goes here + title = 'My Component'; +} +``` + +## Component Lifecycle Hooks +- `ngOnInit()`: Called after the first ngOnChanges +- `ngOnChanges()`: Called when input properties change +- `ngDoCheck()`: Custom change detection +- `ngAfterViewInit()`: Called after the view is initialized +- `ngOnDestroy()`: Cleanup just before Angular destroys the component + +## Component Communication + +### Parent to Child: @Input() +```typescript +// parent.component.html + + +// child.component.ts +@Input() message: string; +``` + +### Child to Parent: @Output() +```typescript +// child.component.ts +@Output() notify = new EventEmitter(); + +onClick() { + this.notify.emit('Button clicked!'); +} + +// parent.component.html + +``` + +## View Encapsulation +Angular supports three view encapsulation strategies: +- `ViewEncapsulation.Emulated` (default) +- `ViewEncapsulation.None` +- `ViewEncapsulation.ShadowDom` + +## Content Projection +```html + + +

Card Title

+

Card content goes here

+
+ + +
+ +
+``` diff --git a/docs/angular/forms.md b/docs/angular/forms.md new file mode 100644 index 0000000..f0e012f --- /dev/null +++ b/docs/angular/forms.md @@ -0,0 +1,217 @@ +# Angular Forms + +## Template-Driven Forms + +### Basic Setup +```typescript +// app.module.ts +import { FormsModule } from '@angular/forms'; + +@NgModule({ + imports: [ + FormsModule + ] +}) +``` + +### Basic Form +```html +
+
+ + +
+ +
+ + +
+ + +
+``` + +### Two-way Binding +```html + +``` + +## Reactive Forms + +### Setup +```typescript +// app.module.ts +import { ReactiveFormsModule } from '@angular/forms'; + +@NgModule({ + imports: [ + ReactiveFormsModule + ] +}) +``` + +### Basic Form +```typescript +// In component +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +export class LoginComponent { + loginForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.loginForm = this.fb.group({ + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required, Validators.minLength(6)]] + }); + } + + onSubmit() { + if (this.loginForm.valid) { + console.log(this.loginForm.value); + } + } +} +``` + +```html +
+
+ + +
+ Email is required +
+
+ +
+ + +
+ + +
+``` + +### Form Arrays +```typescript +// In component +export class OrderFormComponent { + orderForm: FormGroup; + + constructor(private fb: FormBuilder) { + this.orderForm = this.fb.group({ + items: this.fb.array([this.createItem()]) + }); + } + + createItem(): FormGroup { + return this.fb.group({ + name: ['', Validators.required], + quantity: [1, [Validators.required, Validators.min(1)]] + }); + } + + get items() { + return this.orderForm.get('items') as FormArray; + } + + addItem() { + this.items.push(this.createItem()); + } + + removeItem(index: number) { + this.items.removeAt(index); + } +} +``` + +### Custom Validators +```typescript +export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { + return (control: AbstractControl): {[key: string]: any} | null => { + const forbidden = nameRe.test(control.value); + return forbidden ? {forbiddenName: {value: control.value}} : null; + }; +} + +// Usage +this.heroForm = this.fb.group({ + name: ['', [ + Validators.required, + forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator + ]] +}); +``` + +### Dynamic Forms +```typescript +// In component +export class DynamicFormComponent { + form: FormGroup; + fields = [ + { type: 'text', name: 'firstName', label: 'First Name', required: true }, + { type: 'email', name: 'email', label: 'Email', required: true }, + { type: 'password', name: 'password', label: 'Password', required: true } + ]; + + constructor(private fb: FormBuilder) { + const group = {}; + this.fields.forEach(field => { + group[field.name] = ['', field.required ? Validators.required : []]; + }); + this.form = this.fb.group(group); + } +} +``` + +### Form Submission with HTTP +```typescript +// In component +export class UserFormComponent { + userForm: FormGroup; + + constructor(private fb: FormBuilder, private http: HttpClient) { + this.userForm = this.fb.group({ + name: ['', Validators.required], + email: ['', [Validators.required, Validators.email]] + }); + } + + onSubmit() { + if (this.userForm.valid) { + this.http.post('/api/users', this.userForm.value).subscribe( + response => console.log('Success!', response), + error => console.error('Error!', error) + ); + } + } +} +``` + +### Cross-Field Validation +```typescript +// In component +this.form = this.fb.group({ + password: ['', [Validators.required]], + confirmPassword: ['', [Validators.required]] +}, { validator: this.passwordMatchValidator }); + +passwordMatchValidator(g: FormGroup) { + return g.get('password').value === g.get('confirmPassword').value + ? null : { 'mismatch': true }; +} +``` + +### Form Status Changes +```typescript +// In component +ngOnInit() { + this.form.statusChanges.subscribe(status => { + console.log('Form status:', status); + }); + + this.form.get('email').valueChanges.subscribe(value => { + console.log('Email changed to:', value); + }); +} +``` diff --git a/docs/angular/routing.md b/docs/angular/routing.md new file mode 100644 index 0000000..7466240 --- /dev/null +++ b/docs/angular/routing.md @@ -0,0 +1,167 @@ +# Angular Routing + +## Basic Setup + +```typescript +// app-routing.module.ts +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { HomeComponent } from './home/home.component'; +import { AboutComponent } from './about/about.component'; + +const routes: Routes = [ + { path: '', component: HomeComponent }, + { path: 'about', component: AboutComponent }, + { path: '**', redirectTo: '' } // Wildcard route for 404 +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } +``` + +## Route Parameters + +```typescript +// Route definition +{ path: 'product/:id', component: ProductDetailComponent } + +// Accessing in component +import { ActivatedRoute } from '@angular/router'; + +constructor(private route: ActivatedRoute) { + this.route.params.subscribe(params => { + this.productId = params['id']; + }); +} +``` + +## Child Routes + +```typescript +const routes: Routes = [ + { + path: 'products', + component: ProductsComponent, + children: [ + { path: '', component: ProductListComponent }, + { path: ':id', component: ProductDetailComponent }, + { path: ':id/edit', component: ProductEditComponent } + ] + } +]; +``` + +## Route Guards + +### CanActivate +```typescript +@Injectable({ + providedIn: 'root' +}) +export class AuthGuard implements CanActivate { + constructor(private authService: AuthService, private router: Router) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): boolean { + if (this.authService.isLoggedIn()) { + return true; + } + this.router.navigate(['/login']); + return false; + } +} + +// Usage +{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] } +``` + +### Resolve +```typescript +@Injectable({ providedIn: 'root' }) +export class ProductResolver implements Resolve { + constructor(private productService: ProductService) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + return this.productService.getProduct(route.paramMap.get('id')); + } +} + +// Usage +{ + path: 'product/:id', + component: ProductDetailComponent, + resolve: { + product: ProductResolver + } +} +``` + +## Lazy Loading + +```typescript +const routes: Routes = [ + { + path: 'admin', + loadChildren: () => import('./admin/admin.module') + .then(m => m.AdminModule) + } +]; +``` + +## Navigation + +```typescript +// In component +constructor(private router: Router) {} + +gotoProduct(id: number) { + this.router.navigate(['/product', id]); + // or with query params + this.router.navigate(['/products'], { + queryParams: { page: 1, search: 'angular' } + }); +} +``` + +## Route Events + +```typescript +constructor(private router: Router) { + this.router.events.pipe( + filter(event => event instanceof NavigationEnd) + ).subscribe((event: NavigationEnd) => { + console.log('Navigation ended:', event.url); + }); +} +``` + +## Route Animations + +```typescript +// In component +@Component({ + selector: 'app-root', + template: ` +
+ +
+ `, + animations: [ + trigger('routeAnimations', [ + transition('* <=> *', [ + style({ opacity: 0 }), + animate('300ms', style({ opacity: 1 })) + ]) + ]) + ] +}) +export class AppComponent { + prepareRoute(outlet: RouterOutlet) { + return outlet?.activatedRouteData?.['animation']; + } +} +``` diff --git a/docs/angular/services.md b/docs/angular/services.md new file mode 100644 index 0000000..ed74973 --- /dev/null +++ b/docs/angular/services.md @@ -0,0 +1,119 @@ +# Angular Services + +## What is a Service? +Services are singleton objects that provide specific functionality throughout the application. They are typically used for: +- Data fetching and sharing +- Business logic +- Logging +- Authentication + +## Creating a Service +```bash +ng generate service data +# or shorthand +ng g s data +``` + +## Basic Service Example +```typescript +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' // Makes it a singleton service +}) +export class DataService { + private data: any[] = []; + + constructor() { } + + getData(): any[] { + return this.data; + } + + addData(item: any): void { + this.data.push(item); + } +} +``` + +## Dependency Injection +```typescript +// In a component +constructor(private dataService: DataService) {} + +// In a module (if not using providedIn: 'root') +@NgModule({ + providers: [DataService] +}) +``` + +## HTTP Client +```typescript +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + private apiUrl = 'https://api.example.com'; + + constructor(private http: HttpClient) {} + + getItems(): Observable { + return this.http.get(`${this.apiUrl}/items`); + } + + addItem(item: any): Observable { + return this.http.post(`${this.apiUrl}/items`, item); + } +} +``` + +## HTTP Interceptors +```typescript +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + intercept(req: HttpRequest, next: HttpHandler): Observable> { + const token = localStorage.getItem('token'); + + if (token) { + const cloned = req.clone({ + headers: req.headers.set('Authorization', `Bearer ${token}`) + }); + return next.handle(cloned); + } + + return next.handle(req); + } +} +``` + +## Error Handling +```typescript +import { catchError } from 'rxjs/operators'; +import { throwError } from 'rxjs'; + +getItems(): Observable { + return this.http.get(`${this.apiUrl}/items`).pipe( + catchError(error => { + console.error('Error fetching items:', error); + return throwError('Something went wrong'); + }) + ); +} +``` + +## Service Lifecycle Hooks +- `ngOnInit()`: Not available in services +- `ngOnDestroy()`: Can be used to clean up subscriptions + +## Best Practices +- Keep services focused on a single responsibility +- Use `providedIn: 'root'` for singleton services +- Handle errors appropriately +- Unsubscribe from observables to prevent memory leaks +- Use interfaces for request/response types diff --git a/docs/angular/setup.md b/docs/angular/setup.md new file mode 100644 index 0000000..431c8b7 --- /dev/null +++ b/docs/angular/setup.md @@ -0,0 +1,56 @@ +# Angular Setup Guide + +## Prerequisites +- Node.js (v14 or later) +- npm (v6 or later) +- Angular CLI (latest stable version) + +## Installation + +1. Install Angular CLI globally: +```bash +npm install -g @angular/cli +``` + +2. Verify installation: +```bash +ng version +``` + +## Creating a New Project +```bash +ng new my-angular-app +cd my-angular-app +``` + +## Project Structure +``` +my-angular-app/ +├── src/ +│ ├── app/ # Your application code +│ ├── assets/ # Static assets +│ ├── environments/ # Environment configurations +│ └── index.html # Main HTML file +├── angular.json # Angular CLI configuration +└── package.json # Project dependencies +``` + +## Development Server +```bash +# Start development server +ng serve + +# Open in browser +ng serve --open +``` + +## Building for Production +```bash +ng build --prod +``` + +## Common Commands +- `ng generate component component-name` - Generate a new component +- `ng generate service service-name` - Generate a new service +- `ng test` - Run unit tests +- `ng e2e` - Run end-to-end tests diff --git a/docs/mongodb/aggregation.md b/docs/mongodb/aggregation.md new file mode 100644 index 0000000..a380221 --- /dev/null +++ b/docs/mongodb/aggregation.md @@ -0,0 +1,375 @@ +# MongoDB Aggregation Framework + +## Aggregation Pipeline Stages + +### $match +Filters documents to pass only matching documents to the next stage. + +```javascript +db.orders.aggregate([ + { $match: { status: "completed", total: { $gt: 100 } } } +]); +``` + +### $group +Groups documents by specified identifier expressions. + +```javascript +db.orders.aggregate([ + { + $group: { + _id: "$customerId", + totalSpent: { $sum: "$total" }, + averageOrder: { $avg: "$total" }, + orderCount: { $sum: 1 } + } + } +]); +``` + +### $sort +Sorts all input documents and returns them in sorted order. + +```javascript +db.products.aggregate([ + { $sort: { price: -1, name: 1 } } +]); +``` + +### $project +Reshapes each document by including, excluding, or adding new fields. + +```javascript +db.users.aggregate([ + { + $project: { + name: 1, + email: 1, + yearJoined: { $year: "$joinDate" }, + _id: 0 + } + } +]); +``` + +### $lookup +Performs a left outer join to another collection. + +```javascript +db.orders.aggregate([ + { + $lookup: { + from: "customers", + localField: "customerId", + foreignField: "_id", + as: "customer" + } + }, + { $unwind: "$customer" } +]); +``` + +### $unwind +Deconstructs an array field to output one document per array element. + +```javascript +db.orders.aggregate([ + { $unwind: "$items" }, + { $group: { _id: "$items.productId", total: { $sum: "$items.quantity" } } } +]); +``` + +### $facet +Processes multiple aggregation pipelines within a single stage. + +```javascript +db.products.aggregate([ + { + $facet: { + categories: [ + { $unwind: "$categories" }, + { $group: { _id: "$categories", count: { $sum: 1 } } }, + { $sort: { count: -1 } } + ], + priceStats: [ + { $match: { price: { $exists: true } } }, + { + $group: { + _id: null, + average: { $avg: "$price" }, + min: { $min: "$price" }, + max: { $max: "$price" } + } + } + ] + } + } +]); +``` + +## Aggregation Operators + +### Arithmetic Operators +- `$add`: Adds numbers together +- `$subtract`: Subtracts two numbers +- `$multiply`: Multiplies numbers together +- `$divide`: Divides one number by another +- `$mod`: Returns the remainder of a division + +```javascript +db.orders.aggregate([ + { + $project: { + subtotal: 1, + tax: 1, + total: { + $add: ["$subtotal", "$tax"] + }, + discount: { + $multiply: ["$subtotal", 0.1] // 10% discount + }, + finalTotal: { + $subtract: [ + { $add: ["$subtotal", "$tax"] }, + { $multiply: ["$subtotal", 0.1] } + ] + } + } + } +]); +``` + +### String Operators +- `$concat`: Concatenates strings +- `$substr`: Returns a substring +- `$toLower` / `$toUpper`: Converts to lowercase/uppercase +- `$trim`: Removes whitespace + +```javascript +db.users.aggregate([ + { + $project: { + fullName: { + $concat: ["$firstName", " ", "$lastName"] + }, + emailLower: { $toLower: "$email" }, + username: { $substr: ["$email", 0, { $indexOfBytes: ["$email", "@"] }] } + } + } +]); +``` + +### Date Operators +- `$year` / `$month` / `$dayOfMonth`: Extracts date parts +- `$dateToString`: Formats a date as a string +- `$dateFromString`: Converts a date string to a date object + +```javascript +db.orders.aggregate([ + { + $project: { + orderDate: 1, + year: { $year: "$orderDate" }, + month: { $month: "$orderDate" }, + day: { $dayOfMonth: "$orderDate" }, + formattedDate: { + $dateToString: { + format: "%Y-%m-%d", + date: "$orderDate" + } + } + } + } +]); +``` + +### Conditional Operators +- `$cond`: Ternary operator +- `$ifNull`: Returns a value if null, otherwise returns the field value +- `$switch`: Case/when/default logic + +```javascript +db.products.aggregate([ + { + $project: { + name: 1, + price: 1, + priceCategory: { + $switch: { + branches: [ + { case: { $lt: ["$price", 50] }, then: "Budget" }, + { case: { $lt: ["$price", 200] }, then: "Standard" }, + { case: { $lt: ["$price", 1000] }, then: "Premium" } + ], + default: "Luxury" + } + }, + discountPrice: { + $cond: { + if: { $gt: ["$quantity", 10] }, + then: { $multiply: ["$price", 0.9] }, // 10% discount + else: "$price" + } + }, + description: { $ifNull: ["$description", "No description available"] } + } + } +]); +``` + +## Performance Optimization + +### Index Usage +```javascript +// Create an index for the aggregation +// db.orders.createIndex({ status: 1, orderDate: -1 }); + +db.orders.aggregate([ + { $match: { status: "completed", orderDate: { $gte: new Date("2023-01-01") } } }, + { $sort: { orderDate: -1 } }, + { $limit: 100 } +]); +``` + +### $lookup Optimization +```javascript +// Instead of this (inefficient with large collections): +db.orders.aggregate([ + { $lookup: { from: "products", localField: "productId", foreignField: "_id", as: "product" } }, + { $unwind: "$product" }, + { $match: { "product.category": "Electronics" } } +]); + +// Do this (more efficient): +db.orders.aggregate([ + { + $lookup: { + from: "products", + let: { productId: "$productId" }, + pipeline: [ + { $match: { $expr: { $eq: ["$_id", "$$productId"] }, category: "Electronics" } } + ], + as: "product" + } + }, + { $unwind: "$product" } +]); +``` + +### $redact +Controls document traversal during processing. + +```javascript +db.employees.aggregate([ + { + $redact: { + $cond: { + if: { $eq: ["$department", "HR"] }, + then: "$$DESCEND", + else: "$$PRUNE" + } + } + } +]); +``` + +## Aggregation with $graphLookup + +### Recursive Lookup +```javascript +db.employees.aggregate([ + { + $match: { name: "John Doe" } + }, + { + $graphLookup: { + from: "employees", + startWith: "$reportsTo", + connectFromField: "reportsTo", + connectToField: "_id", + as: "managementChain", + maxDepth: 10, + depthField: "level" + } + } +]); +``` + +## Aggregation with $merge + +### Output to a Collection +```javascript +db.orders.aggregate([ + { $match: { status: "completed" } }, + { + $group: { + _id: { $dateToString: { format: "%Y-%m", date: "$orderDate" } }, + totalSales: { $sum: "$total" }, + orderCount: { $sum: 1 } + } + }, + { $sort: { _id: 1 } }, + { + $merge: { + into: "monthlySales", + on: "_id", + whenMatched: "replace", + whenNotMatched: "insert" + } + } +]); +``` + +## Aggregation with $setWindowFields (MongoDB 5.0+) + +### Moving Averages and Rankings +```javascript +db.sales.aggregate([ + { $match: { date: { $gte: new Date("2023-01-01") } } }, + { $sort: { productId: 1, date: 1 } }, + { + $setWindowFields: { + partitionBy: "$productId", + sortBy: { date: 1 }, + output: { + movingAvg: { + $avg: "$amount", + window: { + documents: ["unbounded", "current"] + } + }, + salesRank: { + $denseRank: {} + } + } + } + } +]); +``` + +## Aggregation with $densify + +### Filling Gaps in Time Series Data +```javascript +db.sensorReadings.aggregate([ + { $match: { sensorId: "A1", timestamp: { $gte: new Date("2023-01-01") } } }, + { + $densify: { + field: "timestamp", + range: { + step: 1, + unit: "hour", + bounds: [new Date("2023-01-01"), new Date("2023-01-02")] + } + } + }, + { + $fill: { + output: { + value: { method: "linear" }, + status: { value: "interpolated" } + } + } + } +]); +``` diff --git a/docs/mongodb/crud.md b/docs/mongodb/crud.md new file mode 100644 index 0000000..605d144 --- /dev/null +++ b/docs/mongodb/crud.md @@ -0,0 +1,262 @@ +# MongoDB CRUD Operations + +## Basic CRUD Operations + +### 1. Create Operations + +#### Insert a Single Document +```javascript +// Using insertOne() +db.collection('users').insertOne({ + name: 'John Doe', + email: 'john@example.com', + age: 30, + status: 'active' +}); +``` + +#### Insert Multiple Documents +```javascript +// Using insertMany() +db.collection('users').insertMany([ + { name: 'Alice', age: 25, status: 'active' }, + { name: 'Bob', age: 35, status: 'inactive' }, + { name: 'Charlie', age: 40, status: 'pending' } +]); +``` + +### 2. Read Operations + +#### Find All Documents +```javascript +// Using find() with no query +const allUsers = await db.collection('users').find({}).toArray(); +``` + +#### Find with Query +```javascript +// Find with equality condition +const activeUsers = await db.collection('users') + .find({ status: 'active' }) + .toArray(); + +// Using comparison operators +const adultUsers = await db.collection('users') + .find({ age: { $gt: 18 } }) + .toArray(); +``` + +#### Find One Document +```javascript +const user = await db.collection('users').findOne({ email: 'john@example.com' }); +``` + +#### Projection (Selecting Fields) +```javascript +// Include only name and email fields +const users = await db.collection('users') + .find({}, { projection: { name: 1, email: 1, _id: 0 } }) + .toArray(); +``` + +#### Sorting +```javascript +// Sort by age in descending order +const sortedUsers = await db.collection('users') + .find() + .sort({ age: -1 }) + .toArray(); +``` + +#### Limiting and Skipping +```javascript +// Pagination: Get second page with 10 items per page +const page = 2; +const limit = 10; +const users = await db.collection('users') + .find() + .skip((page - 1) * limit) + .limit(limit) + .toArray(); +``` + +### 3. Update Operations + +#### Update a Single Document +```javascript +// Using updateOne() +const result = await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $set: { status: 'inactive', updatedAt: new Date() } } +); + +console.log(`${result.matchedCount} document(s) matched the filter`); +console.log(`${result.modifiedCount} document(s) was/were updated`); +``` + +#### Update Multiple Documents +```javascript +// Using updateMany() +const result = await db.collection('users').updateMany( + { status: 'pending' }, + { $set: { status: 'active', updatedAt: new Date() } } +); +``` + +#### Increment a Value +```javascript +// Increment age by 1 +await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $inc: { age: 1 } } +); +``` + +#### Add to Array +```javascript +// Add to array field +await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $push: { tags: 'premium' } } +); + +// Add to array if not exists +await db.collection('users').updateOne( + { email: 'john@example.com' }, + { $addToSet: { tags: 'premium' } } +); +``` + +### 4. Delete Operations + +#### Delete a Single Document +```javascript +const result = await db.collection('users').deleteOne({ email: 'john@example.com' }); +console.log(`${result.deletedCount} document(s) was/were deleted`); +``` + +#### Delete Multiple Documents +```javascript +const result = await db.collection('users').deleteMany({ status: 'inactive' }); +``` + +#### Delete All Documents (but keep the collection) +```javascript +const result = await db.collection('users').deleteMany({}); +``` + +## Bulk Operations + +### Ordered Bulk Operations +```javascript +const bulk = db.collection('users').initializeOrderedBulkOp(); +bulk.insert({ name: 'User 1' }); +bulk.insert({ name: 'User 2' }); +bulk.find({ name: 'User 1' }).update({ $set: { status: 'active' } }); +const result = await bulk.execute(); +``` + +### Unordered Bulk Operations +```javascript +const bulk = db.collection('users').initializeUnorderedBulkOp(); +bulk.insert({ name: 'User 3' }); +bulk.insert({ name: 'User 4' }); +const result = await bulk.execute(); +``` + +## Find and Modify + +### Find One and Update +```javascript +const result = await db.collection('users').findOneAndUpdate( + { email: 'john@example.com' }, + { $set: { lastLogin: new Date() } }, + { returnDocument: 'after' } // Return the updated document +); +``` + +### Find One and Delete +```javascript +const result = await db.collection('users').findOneAndDelete( + { email: 'john@example.com' } +); +``` + +## Indexes + +### Create Index +```javascript +// Single field index +await db.collection('users').createIndex({ email: 1 }); + +// Compound index +await db.collection('users').createIndex({ status: 1, createdAt: -1 }); + +// Unique index +await db.collection('users').createIndex({ email: 1 }, { unique: true }); + +// Text index for text search +await db.collection('articles').createIndex({ content: 'text' }); +``` + +### List Indexes +```javascript +const indexes = await db.collection('users').indexes(); +console.log(indexes); +``` + +### Drop Index +```javascript +await db.collection('users').dropIndex('email_1'); +``` + +## Transactions (MongoDB 4.0+) + +```javascript +const session = client.startSession(); + +try { + await session.withTransaction(async () => { + // Perform operations in the transaction + const user = await db.collection('users').findOne( + { email: 'john@example.com' }, + { session } + ); + + if (!user) { + throw new Error('User not found'); + } + + await db.collection('orders').insertOne( + { + userId: user._id, + items: ['item1', 'item2'], + total: 100, + status: 'pending' + }, + { session } + ); + + await db.collection('users').updateOne( + { _id: user._id }, + { $inc: { orderCount: 1 } }, + { session } + ); + }); +} finally { + await session.endSession(); +} +``` + +## Best Practices + +1. **Use Projections** to retrieve only the fields you need +2. **Create Indexes** for frequently queried fields +3. **Use $lookup** instead of multiple queries when possible +4. **Batch Operations** for bulk inserts/updates +5. **Use Cursors** for large result sets +6. **Monitor Performance** using explain() +7. **Use Transactions** for multi-document operations that need to be atomic +8. **Set Write Concerns** appropriately for your use case +9. **Use TTL Indexes** for expiring data +10. **Regularly Compact** collections with high churn diff --git a/docs/mongodb/mongoose.md b/docs/mongodb/mongoose.md new file mode 100644 index 0000000..aaa0e5f --- /dev/null +++ b/docs/mongodb/mongoose.md @@ -0,0 +1,536 @@ +# Mongoose ODM + +## Installation +```bash +npm install mongoose +``` + +## Basic Setup +```javascript +const mongoose = require('mongoose'); + +// Connect to MongoDB +mongoose.connect('mongodb://localhost:27017/mydatabase', { + useNewUrlParser: true, + useUnifiedTopology: true +}); + +// Get the default connection +const db = mongoose.connection; + +// Event listeners +db.on('error', console.error.bind(console, 'MongoDB connection error:')); +db.once('open', () => { + console.log('Connected to MongoDB'); +}); +``` + +## Defining a Schema +```javascript +const { Schema } = mongoose; + +const userSchema = new Schema({ + username: { + type: String, + required: true, + unique: true, + trim: true, + minlength: 3 + }, + email: { + type: String, + required: true, + unique: true, + trim: true, + lowercase: true, + match: [/^\S+@\S+\.\S+$/, 'Please use a valid email address'] + }, + password: { + type: String, + required: true, + minlength: 6, + select: false // Don't return password by default + }, + role: { + type: String, + enum: ['user', 'admin'], + default: 'user' + }, + createdAt: { + type: Date, + default: Date.now + }, + updatedAt: { + type: Date, + default: Date.now + }, + profile: { + firstName: String, + lastName: String, + bio: String, + avatar: String + }, + tags: [String], + isActive: { + type: Boolean, + default: true + } +}, { + timestamps: true, // Adds createdAt and updatedAt fields + toJSON: { virtuals: true }, // Include virtuals when converting to JSON + toObject: { virtuals: true } // Include virtuals when converting to objects +}); + +// Virtual for full name +userSchema.virtual('fullName').get(function() { + return `${this.profile?.firstName || ''} ${this.profile?.lastName || ''}`.trim(); +}); + +// Indexes +userSchema.index({ email: 1 }, { unique: true }); +userSchema.index({ 'profile.firstName': 'text', 'profile.lastName': 'text' }); + +// Pre-save hook to hash password +userSchema.pre('save', async function(next) { + if (!this.isModified('password')) return next(); + + try { + const salt = await bcrypt.genSalt(10); + this.password = await bcrypt.hash(this.password, salt); + next(); + } catch (error) { + next(error); + } +}); + +// Instance method +userSchema.methods.comparePassword = async function(candidatePassword) { + return await bcrypt.compare(candidatePassword, this.password); +}; + +// Static method +userSchema.statics.findByEmail = function(email) { + return this.findOne({ email }); +}; + +// Query helper +userSchema.query.byName = function(name) { + return this.where({ name: new RegExp(name, 'i') }); +}; + +const User = mongoose.model('User', userSchema); +module.exports = User; +``` + +## CRUD Operations + +### Create +```javascript +// Create and save +const user = new User({ + username: 'johndoe', + email: 'john@example.com', + password: 'securepassword123', + role: 'user' +}); + +await user.save(); + +// Create in one step +const newUser = await User.create({ + username: 'janedoe', + email: 'jane@example.com', + password: 'securepassword456' +}); +``` + +### Read +```javascript +// Find all users +const users = await User.find({}); + +// Find one user +const user = await User.findOne({ email: 'john@example.com' }); + +// Find by ID +const user = await User.findById('507f1f77bcf86cd799439011'); + +// Find with conditions +const activeAdmins = await User.find({ + role: 'admin', + isActive: true +}).sort({ createdAt: -1 }); + +// Using query builder +const users = await User + .find({ role: 'user' }) + .where('createdAt').gt(oneYearAgo) + .sort('-createdAt') + .limit(10) + .select('username email createdAt') + .lean(); +``` + +### Update +```javascript +// Update by ID +const user = await User.findByIdAndUpdate( + '507f1f77bcf86cd799439011', + { $set: { isActive: false } }, + { new: true, runValidators: true } +); + +// Update one +await User.updateOne( + { email: 'john@example.com' }, + { $inc: { loginCount: 1 } } +); + +// Update many +await User.updateMany( + { role: 'user' }, + { $set: { lastNotified: new Date() } } +); +``` + +### Delete +```javascript +// Delete one +await User.deleteOne({ email: 'john@example.com' }); + +// Delete by ID +await User.findByIdAndDelete('507f1f77bcf86cd799439011'); + +// Delete many +await User.deleteMany({ isActive: false }); +``` + +## Middleware (Hooks) + +### Document Middleware +```javascript +userSchema.pre('save', function(next) { + this.updatedAt = new Date(); + next(); +}); + +userSchema.post('save', function(doc, next) { + console.log(`User ${doc._id} was saved`); + next(); +}); +``` + +### Query Middleware +```javascript +userSchema.pre('find', function() { + this.start = Date.now(); +}); + +userSchema.post('find', function(docs) { + console.log(`Query took ${Date.now() - this.start} ms`); +}); +``` + +### Aggregation Middleware +```javascript +userSchema.pre('aggregate', function() { + this.pipeline().unshift({ $match: { isActive: true } }); +}); +``` + +## Population (Joins) + +### Basic Population +```javascript +const postSchema = new Schema({ + title: String, + content: String, + author: { type: Schema.Types.ObjectId, ref: 'User' }, + comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }] +}); + +const Post = mongoose.model('Post', postSchema); + +// Populate author +const post = await Post.findById(postId).populate('author'); + +// Populate multiple fields +const post = await Post.findById(postId) + .populate('author') + .populate('comments'); + +// Populate with selected fields +const post = await Post.findById(postId).populate({ + path: 'author', + select: 'username email profile.firstName profile.lastName' +}); + +// Populate with conditions +const post = await Post.findById(postId).populate({ + path: 'comments', + match: { isApproved: true }, + options: { sort: { createdAt: -1 }, limit: 10 }, + select: 'content createdAt', + populate: { + path: 'author', + select: 'username' + } +}); +``` + +## Aggregation + +### Basic Aggregation +```javascript +const stats = await User.aggregate([ + { $match: { isActive: true } }, + { + $group: { + _id: '$role', + count: { $sum: 1 }, + averageAge: { $avg: '$age' } + } + }, + { $sort: { count: -1 } } +]); +``` + +### Text Search +```javascript +const results = await User.find( + { $text: { $search: 'john' } }, + { score: { $meta: 'textScore' } } +).sort({ score: { $meta: 'textScore' } }); +``` + +## Transactions + +### Basic Transaction +```javascript +const session = await mongoose.startSession(); +session.startTransaction(); + +try { + const user = await User.create([{ + username: 'newuser', + email: 'new@example.com', + password: 'password123' + }], { session }); + + await Profile.create([{ + userId: user[0]._id, + firstName: 'New', + lastName: 'User' + }], { session }); + + await session.commitTransaction(); + console.log('Transaction completed'); +} catch (error) { + await session.abortTransaction(); + console.error('Transaction aborted:', error); +} finally { + session.endSession(); +} +``` + +## Plugins + +### Creating a Plugin +```javascript +// plugins/timestamps.js +module.exports = function timestampsPlugin(schema) { + schema.add({ + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now } + }); + + schema.pre('save', function(next) { + this.updatedAt = new Date(); + next(); + }); +}; + +// Using the plugin +const timestamps = require('./plugins/timestamps'); +userSchema.plugin(timestamps); +``` + +## Validation + +### Custom Validators +```javascript +const userSchema = new Schema({ + email: { + type: String, + required: true, + validate: { + validator: function(v) { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); + }, + message: props => `${props.value} is not a valid email!` + } + }, + age: { + type: Number, + min: [18, 'Must be at least 18, got {VALUE}'], + max: 120 + }, + role: { + type: String, + enum: { + values: ['user', 'admin'], + message: '{VALUE} is not a valid role' + } + } +}); + +// Async validator +userSchema.path('username').validate({ + isAsync: true, + validator: async function(value) { + const user = await this.constructor.findOne({ username: value }); + return !user || this._id.equals(user._id); + }, + message: 'Username already exists' +}); +``` + +## Error Handling + +### Handling Validation Errors +```javascript +try { + const user = await User.create({ email: 'invalid-email' }); +} catch (error) { + if (error.name === 'ValidationError') { + const errors = {}; + Object.keys(error.errors).forEach(key => { + errors[key] = error.errors[key].message; + }); + return { errors }; + } + throw error; +} +``` + +### Handling Duplicate Key Errors +```javascript +try { + const user = await User.create({ email: 'duplicate@example.com' }); +} catch (error) { + if (error.code === 11000) { + return { error: 'Email already exists' }; + } + throw error; +} +``` + +## Performance Optimization + +### Lean Queries +```javascript +// Returns plain JavaScript objects instead of Mongoose documents +const users = await User.find({}).lean(); +``` + +### Select Only Required Fields +```javascript +// Only fetch username and email +const users = await User.find({}, 'username email'); +``` + +### Use Indexes +```javascript +// Create index +userSchema.index({ email: 1 }, { unique: true }); +userSchema.index({ 'profile.location': '2dsphere' }); +``` + +### Caching +```javascript +// Simple in-memory cache +const cache = {}; + +async function getUser(id) { + if (cache[id]) { + return cache[id]; + } + + const user = await User.findById(id).cache({ key: id }); + cache[id] = user; + return user; +} +``` + +## Testing + +### Using Jest +```javascript +// __tests__/user.test.js +const mongoose = require('mongoose'); +const User = require('../models/User'); + +beforeAll(async () => { + await mongoose.connect('mongodb://localhost:27017/testdb', { + useNewUrlParser: true, + useUnifiedTopology: true + }); +}); + +afterAll(async () => { + await mongoose.connection.dropDatabase(); + await mongoose.connection.close(); +}); + +describe('User Model', () => { + it('should create a new user', async () => { + const user = await User.create({ + username: 'testuser', + email: 'test@example.com', + password: 'password123' + }); + + expect(user).toHaveProperty('_id'); + expect(user.email).toBe('test@example.com'); + }); + + it('should not create user with duplicate email', async () => { + await User.create({ + username: 'testuser1', + email: 'duplicate@example.com', + password: 'password123' + }); + + await expect( + User.create({ + username: 'testuser2', + email: 'duplicate@example.com', + password: 'password456' + }) + ).rejects.toThrow('duplicate key error'); + }); +}); +``` + +## Best Practices + +1. **Use Schemas**: Always define schemas for your models +2. **Use Middleware**: For pre/post hooks and business logic +3. **Use Virtuals**: For computed properties +4. **Use Statics**: For model-level functions +5. **Use Methods**: For document-level functions +6. **Use Plugins**: For reusable functionality +7. **Use Lean**: When you don't need Mongoose documents +8. **Use Projections**: Only fetch the fields you need +9. **Use Indexes**: For better query performance +10. **Handle Errors**: Proper error handling and validation +11. **Use Transactions**: For multi-document operations +12. **Use Environment Variables**: For configuration +13. **Use Connection Pooling**: For better performance +14. **Monitor Performance**: Use MongoDB Atlas or similar tools +15. **Keep Documents Small**: Avoid large documents (>16MB) +16. **Use Caching**: For frequently accessed data +17. **Use Aggregation**: For complex queries +18. **Use Text Search**: For full-text search capabilities +19. **Use Geospatial Queries**: For location-based data +20. **Keep Mongoose Updated**: For security and performance improvements diff --git a/docs/mongodb/schema-design.md b/docs/mongodb/schema-design.md new file mode 100644 index 0000000..95ca163 --- /dev/null +++ b/docs/mongodb/schema-design.md @@ -0,0 +1,318 @@ +# MongoDB Schema Design + +## Data Modeling Concepts + +### 1. Document Structure + +#### Embedded Documents +```javascript +// Users with embedded addresses +{ + _id: ObjectId("507f1f77bcf86cd799439011"), + name: "John Doe", + email: "john@example.com", + addresses: [ + { + type: "home", + street: "123 Main St", + city: "Springfield", + zip: "12345", + isPrimary: true + }, + { + type: "work", + street: "456 Work Ave", + city: "Springfield", + zip: "12345" + } + ] +} +``` + +#### Referenced Documents +```javascript +// Users collection +{ + _id: ObjectId("507f1f77bcf86cd799439011"), + name: "John Doe", + email: "john@example.com" +} + +// Addresses collection +{ + _id: ObjectId("5a934e000102030405000000"), + userId: ObjectId("507f1f77bcf86cd799439011"), + type: "home", + street: "123 Main St", + city: "Springfield", + zip: "12345", + isPrimary: true +} +``` + +## Schema Design Patterns + +### 1. Embedding (Denormalization) +**When to use:** +- One-to-few relationships +- Data that's always accessed together +- Data that changes together + +**Example: Blog Post with Comments** +```javascript +{ + _id: ObjectId("5a934e000102030405000001"), + title: "MongoDB Schema Design", + content: "...", + author: "John Doe", + publishedAt: ISODate("2023-01-15T10:00:00Z"), + comments: [ + { + _id: ObjectId("5a934e000102030405000002"), + author: "Alice", + text: "Great post!", + createdAt: ISODate("2023-01-15T11:30:00Z") + }, + { + _id: ObjectId("5a934e000102030405000003"), + author: "Bob", + text: "Thanks for sharing!", + createdAt: ISODate("2023-01-16T09:15:00Z") + } + ] +} +``` + +### 2. Referencing (Normalization) +**When to use:** +- One-to-many or many-to-many relationships +- Large documents that exceed 16MB +- Frequently updated data + +**Example: Products and Categories** +```javascript +// Products collection +{ + _id: ObjectId("5a934e000102030405000100"), + name: "Laptop", + price: 999.99, + categoryIds: [ + ObjectId("5a934e000102030405000200"), // Electronics + ObjectId("5a934e000102030405000201") // Computers + ] +} + +// Categories collection +{ + _id: ObjectId("5a934e000102030405000200"), + name: "Electronics", + description: "Electronic devices" +} + +{ + _id: ObjectId("5a934e000102030405000201"), + name: "Computers", + description: "Computers and laptops" +} +``` + +### 3. Bucket Pattern +**When to use:** +- Time-series data +- IoT sensor data +- Logging + +**Example: Sensor Data** +```javascript +{ + _id: "sensor-1-2023-01", + sensorId: "sensor-1", + type: "temperature", + unit: "Celsius", + measurements: [ + { timestamp: ISODate("2023-01-01T00:00:00Z"), value: 21.5 }, + { timestamp: ISODate("2023-01-01T00:05:00Z"), value: 21.7 }, + // ... more measurements for the month + ], + metadata: { + location: "Room 101", + min: 18.0, + max: 25.0 + } +} +``` + +### 4. Attribute Pattern +**When to use:** +- Heterogeneous document attributes +- Sparse fields +- Polymorphic schemas + +**Example: E-commerce Product Catalog** +```javascript +{ + _id: ObjectId("5a934e000102030405000300"), + name: "Smartphone X", + category: "electronics", + attributes: [ + { name: "color", value: "black" }, + { name: "storage", value: "128GB" }, + { name: "ram", value: "8GB" } + ] +} + +{ + _id: ObjectId("5a934e000102030405000301"), + name: "T-Shirt", + category: "clothing", + attributes: [ + { name: "color", value: "blue" }, + { name: "size", value: "L" }, + { name: "material", value: "cotton" } + ] +} +``` + +## Schema Validation + +### JSON Schema Validation +```javascript +db.createCollection("users", { + validator: { + $jsonSchema: { + bsonType: "object", + required: ["name", "email", "status"], + properties: { + name: { + bsonType: "string", + description: "must be a string and is required" + }, + email: { + bsonType: "string", + pattern: "^.+@.+\\.", + description: "must be a valid email and is required" + }, + status: { + enum: ["active", "inactive", "pending"], + description: "must be a valid status and is required" + }, + age: { + bsonType: ["int", "null"], + minimum: 0, + maximum: 120, + description: "must be an integer between 0 and 120" + } + } + } + } +}); +``` + +## Data Types + +### Common Data Types +- String (`string`) +- Number (`int32`, `int64`, `double`) +- Boolean (`bool`) +- Date (`date`) +- Array (`array`) +- Object (`object`) +- ObjectId (`objectId`) +- Binary Data (`binData`) +- Null (`null`) + +### Special Types +- Decimal128 (for precise decimal arithmetic) +- Timestamp (for internal MongoDB use) +- Regular Expression (`regex`) +- JavaScript (`javascript`) +- MinKey/MaxKey (for comparison operations) + +## Indexing Strategies + +### Single Field Index +```javascript +db.users.createIndex({ email: 1 }); +``` + +### Compound Index +```javascript +db.orders.createIndex({ customerId: 1, orderDate: -1 }); +``` + +### Multikey Index (for arrays) +```javascript +db.products.createIndex({ tags: 1 }); +``` + +### Text Index +```javascript +db.articles.createIndex({ title: "text", content: "text" }); +``` + +### Wildcard Index +```javascript +db.users.createIndex({ "metadata.$**": 1 }); +``` + +## Sharding Strategies + +### Hashed Sharding +```javascript +sh.shardCollection("mydb.users", { _id: "hashed" }); +``` + +### Ranged Sharding +```javascript +sh.shardCollection("mydb.orders", { customerId: 1 }); +``` + +### Zoned Sharding +```javascript +sh.addShardTag("shard0000", "US"); +sh.addShardTag("shard0001", "EU"); +sh.addTagRange("mydb.users", { country: "US" }, { country: "US" }, "US"); +sh.addTagRange("mydb.users", { country: "UK" }, { country: "FR" }, "EU"); +``` + +## Data Lifecycle Management + +### TTL Indexes +```javascript +// Documents will be automatically deleted after 30 days +db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 2592000 }); +``` + +### Capped Collections +```javascript +// Create a capped collection of 1MB +db.createCollection("recent_activities", { capped: true, size: 1048576 }); +``` + +## Schema Evolution + +### Adding Fields +```javascript +db.users.updateMany( + { newField: { $exists: false } }, + { $set: { newField: "default value" } } +); +``` + +### Migrating Data +```javascript +// Add new structure +db.products.updateMany( + {}, + { $set: { "metadata.tags": [] } } +); + +// Migrate old tags to new structure +db.products.updateMany( + { tags: { $exists: true } }, + [ + { $set: { "metadata.tags": "$tags" } }, + { $unset: "tags" } + ] +); +``` diff --git a/docs/mongodb/setup.md b/docs/mongodb/setup.md new file mode 100644 index 0000000..f098700 --- /dev/null +++ b/docs/mongodb/setup.md @@ -0,0 +1,211 @@ +# MongoDB Setup Guide + +## Installation + +### macOS (using Homebrew) +```bash +# Install MongoDB Community Edition +brew tap mongodb/brew +brew install mongodb-community + +# Start MongoDB +brew services start mongodb-community + +# Verify installation +mongod --version +mongo --version +``` + +### Windows +1. Download MongoDB Community Server from [MongoDB Download Center](https://www.mongodb.com/try/download/community) +2. Run the installer and follow the prompts +3. Add MongoDB's `bin` directory to your system's PATH +4. Create a data directory: `C:\data\db` +5. Start MongoDB: `mongod` + +### Linux (Ubuntu/Debian) +```bash +# Import the public key +wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add - + +# Create list file +echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list + +# Reload local package database +sudo apt-get update + +# Install MongoDB +sudo apt-get install -y mongodb-org + +# Start MongoDB +sudo systemctl start mongod + +# Enable automatic startup +sudo systemctl enable mongod +``` + +## MongoDB Compass (GUI Tool) +Download and install MongoDB Compass from: https://www.mongodb.com/try/download/compass + +## Basic Commands + +### Start MongoDB Shell +```bash +mongosh # For MongoDB 6.0+ +# or +mongo # For older versions +``` + +### Show Databases +```javascript +show dbs +``` + +### Switch/Create Database +```javascript +use mydb +``` + +### Show Collections +```javascript +show collections +``` + +### Get Help +```javascript +db.help() +``` + +## Connecting with Node.js + +### Install MongoDB Driver +```bash +npm install mongodb +``` + +### Basic Connection +```javascript +const { MongoClient } = require('mongodb'); + +// Connection URI +const uri = 'mongodb://localhost:27017'; + +// Create a new MongoClient +const client = new MongoClient(uri); + +async function run() { + try { + // Connect the client to the server + await client.connect(); + console.log('Connected successfully to MongoDB'); + + // Get the database + const db = client.db('mydb'); + + // Get the collection + const collection = db.collection('documents'); + + // Insert a document + await collection.insertOne({ name: 'Test', value: 1 }); + + // Find documents + const docs = await collection.find({}).toArray(); + console.log('Found documents:', docs); + + } finally { + // Close the connection + await client.close(); + } +} + +run().catch(console.dir); +``` + +## Configuration + +### MongoDB Configuration File (mongod.conf) +Typical location: +- Linux: `/etc/mongod.conf` +- macOS: `/usr/local/etc/mongod.conf` +- Windows: `C:\Program Files\MongoDB\Server\\bin\mongod.cfg` + +### Common Configuration Options +```yaml +# Network interfaces +net: + port: 27017 + bindIp: 127.0.0.1 # Only localhost + +# Storage +dbPath: /data/db + +# Security +security: + authorization: enabled # Enable authentication + +# Replication (for replica sets) +replication: + replSetName: "rs0" + +# Performance +storage: + journal: + enabled: true + wiredTiger: + engineConfig: + cacheSizeGB: 1 # Adjust based on available RAM +``` + +## Authentication + +### Create Admin User +```javascript +use admin +db.createUser({ + user: 'admin', + pwd: 'your-secure-password', + roles: [ { role: 'userAdminAnyDatabase', db: 'admin' } ] +}) +``` + +### Connect with Authentication +```javascript +const uri = 'mongodb://admin:your-secure-password@localhost:27017/admin'; +const client = new MongoClient(uri); +``` + +## Backup and Restore + +### Create Backup +```bash +mongodump --uri="mongodb://localhost:27017" --out=/backup/$(date +%Y%m%d) +``` + +### Restore from Backup +```bash +mongorestore --uri="mongodb://localhost:27017" /backup/20231015/mydb +``` + +## Monitoring + +### Basic Stats +```javascript +db.serverStatus() + +db.stats() + +// Collection stats +db.collection.stats() + +// Current operations +db.currentOp() +``` + +### Enable Profiling +```javascript +// Set profiling level (0=off, 1=slow, 2=all) +db.setProfilingLevel(1, { slowms: 100 }) + +// View profile data +db.system.profile.find().pretty() +``` diff --git a/docs/ui/css-tricks.md b/docs/ui/css-tricks.md new file mode 100644 index 0000000..4be1bf7 --- /dev/null +++ b/docs/ui/css-tricks.md @@ -0,0 +1,581 @@ +# CSS Tricks and Tips + +## Layout + +### Flexbox Centering +Center elements both horizontally and vertically: +```css +.container { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; /* Optional: for full viewport height */ +} +``` + +### CSS Grid Layout +Create a responsive grid with auto-fill: +```css +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 1rem; +} +``` + +### Sticky Footer +Keep footer at the bottom of the page: +```css +body { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +main { + flex: 1; +} +``` + +## Effects + +### Smooth Scrolling +```css +html { + scroll-behavior: smooth; +} +``` + +### Custom Scrollbar +```css +/* WebKit (Chrome, Safari, newer Edge) */ +::-webkit-scrollbar { + width: 10px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; +} + +::-webkit-scrollbar-thumb { + background: #888; + border-radius: 5px; +} + +/* Firefox */ +html { + scrollbar-width: thin; + scrollbar-color: #888 #f1f1f1; +} +``` + +### Hover Effects +```css +.button { + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.button:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} +``` + +## Typography + +### Text Overflow Ellipsis +```css +.ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; +} + +/* Multi-line ellipsis (3 lines) */ +.multi-line-ellipsis { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} +``` + +### System Font Stack +```css +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; +} +``` + +## Responsive Design + +### Mobile-First Media Queries +```css +/* Base styles (mobile) */ +.container { + padding: 1rem; +} + +/* Tablet */ +@media (min-width: 768px) { + .container { + padding: 2rem; + } +} + +/* Desktop */ +@media (min-width: 1024px) { + .container { + max-width: 1200px; + margin: 0 auto; + } +} +``` + +### Responsive Images +```css +.responsive-img { + max-width: 100%; + height: auto; + display: block; +} + +/* Art direction with picture element */ + + + + Description + +``` + +## Animations + +### Keyframe Animation +```css +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.animate-fade-in { + animation: fadeIn 0.6s ease-out forwards; +} +``` + +### Hover Underline Animation +```css +.hover-underline { + position: relative; + text-decoration: none; +} + +.hover-underline::after { + content: ''; + position: absolute; + width: 0; + height: 2px; + bottom: -2px; + left: 0; + background-color: currentColor; + transition: width 0.3s ease; +} + +.hover-underline:hover::after { + width: 100%; +} +``` + +## Forms + +### Custom Checkbox/Radio +```css +.custom-checkbox { + position: relative; + padding-left: 30px; + cursor: pointer; +} + +.custom-checkbox input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 20px; + width: 20px; + background-color: #eee; + border: 2px solid #ddd; + border-radius: 4px; +} + +.custom-checkbox:hover input ~ .checkmark { + background-color: #ccc; +} + +.custom-checkbox input:checked ~ .checkmark { + background-color: #2196F3; + border-color: #2196F3; +} + +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +.custom-checkbox input:checked ~ .checkmark:after { + display: block; +} + +.custom-checkbox .checkmark:after { + left: 6px; + top: 2px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} +``` + +## Variables and Theming + +### CSS Custom Properties +```css +:root { + --primary-color: #3498db; + --secondary-color: #2ecc71; + --spacing-unit: 1rem; + --border-radius: 4px; + --transition: all 0.3s ease; +} + +.button { + background-color: var(--primary-color); + padding: calc(var(--spacing-unit) * 2); + border-radius: var(--border-radius); + transition: var(--transition); +} + +/* Dark mode theming */ +@media (prefers-color-scheme: dark) { + :root { + --primary-color: #2980b9; + --secondary-color: #27ae60; + } +} +``` + +## Performance + +### Will-Change Property +```css +.optimize { + will-change: transform, opacity; +} +``` + +### Content Visibility +```css +.long-content { + content-visibility: auto; + contain-intrinsic-size: 0 500px; /* Estimated height */ +} +``` + +## Browser Compatibility + +### Feature Queries +```css +@supports (display: grid) { + .container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + } +} + +@supports not (display: grid) { + .container { + display: flex; + flex-wrap: wrap; + } + + .container > * { + flex: 1 1 250px; + margin: 0.5rem; + } +} +``` + +### Vendor Prefixes +```css +.gradient-bg { + background: #1e5799; /* Fallback */ + background: -moz-linear-gradient(top, #1e5799 0%, #2989d8 100%); + background: -webkit-linear-gradient(top, #1e5799 0%,#2989d8 100%); + background: linear-gradient(to bottom, #1e5799 0%,#2989d8 100%); + filter: progid:DXImageTransform.Microsoft.gradient( + startColorstr='#1e5799', + endColorstr='#2989d8', + GradientType=0 + ); +} +``` + +## Debugging + +### Debugging Layout +```css +/* Add to any element to debug */ +.debug { + outline: 1px solid red; + background: rgba(255, 0, 0, 0.1); +} + +/* Debug all elements */ +* { outline: 1px solid rgba(255, 0, 0, 0.2); } +* * { outline: 1px solid rgba(0, 255, 0, 0.2); } +* * * { outline: 1px solid rgba(0, 0, 255, 0.2); } +* * * * { outline: 1px solid rgba(255, 0, 255, 0.2); } +* * * * * { outline: 1px solid rgba(0, 255, 255, 0.2); } +``` + +### Print Styles +```css +@media print { + /* Hide elements when printing */ + .no-print { + display: none !important; + } + + /* Page breaks */ + .page-break { + page-break-before: always; + } + + /* Prevent text from breaking across pages */ + p, h1, h2, h3, h4, h5, h6 { + page-break-inside: avoid; + } + + /* Set page margins */ + @page { + margin: 2cm; + } +} +``` + +## Modern CSS + +### Aspect Ratio +```css +.aspect-ratio-box { + aspect-ratio: 16 / 9; + background: #f0f0f0; +} +``` + +### CSS Grid Subgrid +```css +.grid-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; +} + +.grid-item { + display: grid; + grid-template-rows: subgrid; + grid-row: span 3; /* Number of rows this item spans */ +} +``` + +### Container Queries +```css +.component { + container-type: inline-size; +} + +@container (min-width: 400px) { + .component .content { + display: flex; + gap: 1rem; + } +} +``` + +## Accessibility + +### Screen Reader Only +```css +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +/* For interactive elements that need to be visible on focus */ +.sr-only.focusable:focus { + position: static; + width: auto; + height: auto; + padding: 0.5em; + margin: 0; + overflow: visible; + clip: auto; + white-space: normal; +} +``` + +### Reduced Motion +```css +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} +``` + +## Bonus: CSS Reset +```css +/* Box sizing rules */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margin and padding */ +html, +body, +h1, h2, h3, h4, h5, h6, +p, blockquote, pre, +dl, dd, ol, ul, +figure, +fieldset, legend, +textarea, +pre, iframe, +hr { + margin: 0; + padding: 0; +} + +/* Set core body defaults */ +body { + min-height: 100vh; + scroll-behavior: smooth; + text-rendering: optimizeSpeed; + line-height: 1.5; +} + +/* Remove list styles on ul, ol elements */ +ul[class], +ol[class] { + list-style: none; +} + +/* Make images easier to work with */ +img, +picture, +video, +canvas, +svg { + max-width: 100%; + display: block; +} + +/* Inherit fonts for inputs and buttons */ +input, +button, +textarea, +select { + font: inherit; +} + +/* Remove all animations and transitions for people that prefer not to see them */ +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} +``` + +## CSS Variables for Theming +```css +:root { + /* Colors */ + --color-primary: #2563eb; + --color-secondary: #7c3aed; + --color-success: #10b981; + --color-warning: #f59e0b; + --color-danger: #ef4444; + + /* Typography */ + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'Fira Code', 'Courier New', monospace; + + /* Spacing */ + --space-xs: 0.25rem; + --space-sm: 0.5rem; + --space-md: 1rem; + --space-lg: 2rem; + --space-xl: 4rem; + + /* Border radius */ + --radius-sm: 0.25rem; + --radius-md: 0.5rem; + --radius-lg: 1rem; + --radius-full: 9999px; + + /* Shadows */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + + /* Transitions */ + --transition-fast: 150ms ease; + --transition-normal: 300ms ease; + --transition-slow: 500ms ease; +} + +/* Dark mode */ +@media (prefers-color-scheme: dark) { + :root { + --color-primary: #3b82f6; + --color-secondary: #8b5cf6; + --color-success: #10b981; + --color-warning: #f59e0b; + --color-danger: #ef4444; + + --shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.3); + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2); + --shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); + --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2); + } +} +``` + +This CSS tricks guide covers a wide range of techniques from basic to advanced, including layout, animations, responsive design, accessibility, and modern CSS features. Each example is ready to use and includes comments for better understanding.