Claude Agent Skill · by Hoodini

Mongodb

Install Mongodb skill for Claude Code from hoodini/ai-agents-skills.

Install
Terminal · npx
$npx skills add https://github.com/hoodini/ai-agents-skills --skill mongodb
Works with Paperclip

How Mongodb fits into a Paperclip company.

Mongodb drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md572 lines
Expand
---name: mongodbdescription: Work with MongoDB databases using best practices. Use when designing schemas, writing queries, building aggregation pipelines, or optimizing performance. Triggers on MongoDB, Mongoose, NoSQL, aggregation pipeline, document database, MongoDB Atlas.--- # MongoDB & Mongoose Build and query MongoDB databases with best practices. ## Quick Start ```bashnpm install mongodb mongoose``` ### Native Driver```typescriptimport { MongoClient, ObjectId } from 'mongodb'; const client = new MongoClient(process.env.MONGODB_URI!);const db = client.db('myapp');const users = db.collection('users'); // Connectawait client.connect(); // CRUD Operationsawait users.insertOne({ name: 'Alice', email: 'alice@example.com' });const user = await users.findOne({ email: 'alice@example.com' });await users.updateOne({ _id: user._id }, { $set: { name: 'Alice Smith' } });await users.deleteOne({ _id: user._id });``` ### Mongoose Setup```typescriptimport mongoose from 'mongoose'; await mongoose.connect(process.env.MONGODB_URI!, {  maxPoolSize: 10,  serverSelectionTimeoutMS: 5000,  socketTimeoutMS: 45000,}); // Connection eventsmongoose.connection.on('connected', () => console.log('MongoDB connected'));mongoose.connection.on('error', (err) => console.error('MongoDB error:', err));mongoose.connection.on('disconnected', () => console.log('MongoDB disconnected')); // Graceful shutdownprocess.on('SIGINT', async () => {  await mongoose.connection.close();  process.exit(0);});``` ## Schema Design ### Basic Schema```typescriptimport mongoose, { Schema, Document, Model } from 'mongoose'; interface IUser extends Document {  email: string;  name: string;  password: string;  role: 'user' | 'admin';  profile: {    avatar?: string;    bio?: string;  };  createdAt: Date;  updatedAt: Date;} const userSchema = new Schema<IUser>({  email: {    type: String,    required: [true, 'Email is required'],    unique: true,    lowercase: true,    trim: true,    match: [/^\S+@\S+\.\S+$/, 'Invalid email format'],  },  name: {    type: String,    required: true,    trim: true,    minlength: 2,    maxlength: 100,  },  password: {    type: String,    required: true,    select: false,  // Never return password by default  },  role: {    type: String,    enum: ['user', 'admin'],    default: 'user',  },  profile: {    avatar: String,    bio: { type: String, maxlength: 500 },  },}, {  timestamps: true,  // Adds createdAt, updatedAt  toJSON: {    transform(doc, ret) {      delete ret.password;      delete ret.__v;      return ret;    },  },}); // IndexesuserSchema.index({ email: 1 });userSchema.index({ createdAt: -1 });userSchema.index({ name: 'text', 'profile.bio': 'text' });  // Text search const User: Model<IUser> = mongoose.model('User', userSchema);``` ### Embedded Documents vs References ```typescript// ✅ Embed when: Data is read together, doesn't grow unboundedconst orderSchema = new Schema({  customer: {    name: String,    email: String,    address: {      street: String,      city: String,      country: String,    },  },  items: [{    product: String,    quantity: Number,    price: Number,  }],  total: Number,}); // ✅ Reference when: Data is large, shared, or changes independentlyconst postSchema = new Schema({  title: String,  content: String,  author: {    type: Schema.Types.ObjectId,    ref: 'User',    required: true,  },  comments: [{    type: Schema.Types.ObjectId,    ref: 'Comment',  }],}); // Populate referencesconst post = await Post.findById(id)  .populate('author', 'name email')  // Select specific fields  .populate({    path: 'comments',    populate: { path: 'author', select: 'name' },  // Nested populate  });``` ### Virtuals```typescriptconst userSchema = new Schema({  firstName: String,  lastName: String,}); // Virtual field (not stored in DB)userSchema.virtual('fullName').get(function() {  return `${this.firstName} ${this.lastName}`;}); // Virtual populate (for reverse references)userSchema.virtual('posts', {  ref: 'Post',  localField: '_id',  foreignField: 'author',}); // Enable virtuals in JSONuserSchema.set('toJSON', { virtuals: true });userSchema.set('toObject', { virtuals: true });``` ## Query Operations ### Find Operations```typescript// Find with filtersconst users = await User.find({  role: 'user',  createdAt: { $gte: new Date('2024-01-01') },}); // Query builderconst results = await User.find()  .where('role').equals('user')  .where('createdAt').gte(new Date('2024-01-01'))  .select('name email')  .sort({ createdAt: -1 })  .limit(10)  .skip(20)  .lean();  // Return plain objects (faster) // Find oneconst user = await User.findOne({ email: 'alice@example.com' });const userById = await User.findById(id); // Exists checkconst exists = await User.exists({ email: 'alice@example.com' }); // Countconst count = await User.countDocuments({ role: 'admin' });``` ### Query Operators```typescript// Comparisonawait User.find({ age: { $eq: 25 } });      // Equalawait User.find({ age: { $ne: 25 } });      // Not equalawait User.find({ age: { $gt: 25 } });      // Greater thanawait User.find({ age: { $gte: 25 } });     // Greater or equalawait User.find({ age: { $lt: 25 } });      // Less thanawait User.find({ age: { $lte: 25 } });     // Less or equalawait User.find({ age: { $in: [20, 25, 30] } });   // In arrayawait User.find({ age: { $nin: [20, 25] } });      // Not in array // Logicalawait User.find({  $and: [{ age: { $gte: 18 } }, { role: 'user' }],});await User.find({  $or: [{ role: 'admin' }, { isVerified: true }],});await User.find({ age: { $not: { $lt: 18 } } }); // Elementawait User.find({ avatar: { $exists: true } });await User.find({ score: { $type: 'number' } }); // Arrayawait User.find({ tags: 'nodejs' });  // Array contains valueawait User.find({ tags: { $all: ['nodejs', 'mongodb'] } });  // Contains allawait User.find({ tags: { $size: 3 } });  // Array lengthawait User.find({ 'items.0.price': { $gt: 100 } });  // Array index // Text searchawait User.find({ $text: { $search: 'mongodb developer' } }); // Regexawait User.find({ name: { $regex: /^john/i } });``` ### Update Operations```typescript// Update oneawait User.updateOne(  { _id: userId },  { $set: { name: 'New Name' } }); // Update manyawait User.updateMany(  { role: 'user' },  { $set: { isVerified: true } }); // Find and update (returns document)const updated = await User.findByIdAndUpdate(  userId,  { $set: { name: 'New Name' } },  { new: true, runValidators: true }  // Return updated doc, run validators); // Update operatorsawait User.updateOne({ _id: userId }, {  $set: { name: 'New Name' },          // Set field  $unset: { tempField: '' },           // Remove field  $inc: { loginCount: 1 },             // Increment  $mul: { score: 1.5 },                // Multiply  $min: { lowScore: 50 },              // Set if less than  $max: { highScore: 100 },            // Set if greater than  $push: { tags: 'new-tag' },          // Add to array  $pull: { tags: 'old-tag' },          // Remove from array  $addToSet: { tags: 'unique-tag' },   // Add if not exists}); // Upsert (insert if not exists)await User.updateOne(  { email: 'new@example.com' },  { $set: { name: 'New User' } },  { upsert: true });``` ## Aggregation Pipeline ### Basic Aggregation```typescriptconst results = await Order.aggregate([  // Stage 1: Match  { $match: { status: 'completed' } },    // Stage 2: Group  { $group: {    _id: '$customerId',    totalOrders: { $sum: 1 },    totalSpent: { $sum: '$total' },    avgOrder: { $avg: '$total' },  }},    // Stage 3: Sort  { $sort: { totalSpent: -1 } },    // Stage 4: Limit  { $limit: 10 },]);``` ### Pipeline Stages```typescriptconst pipeline = [  // $match - Filter documents  { $match: { createdAt: { $gte: new Date('2024-01-01') } } },    // $project - Shape output  { $project: {    name: 1,    email: 1,    yearJoined: { $year: '$createdAt' },    fullName: { $concat: ['$firstName', ' ', '$lastName'] },  }},    // $lookup - Join collections  { $lookup: {    from: 'orders',    localField: '_id',    foreignField: 'userId',    as: 'orders',  }},    // $unwind - Flatten arrays  { $unwind: { path: '$orders', preserveNullAndEmptyArrays: true } },    // $group - Aggregate  { $group: {    _id: '$_id',    name: { $first: '$name' },    orderCount: { $sum: 1 },    orders: { $push: '$orders' },  }},    // $addFields - Add computed fields  { $addFields: {    hasOrders: { $gt: ['$orderCount', 0] },  }},    // $facet - Multiple pipelines  { $facet: {    topCustomers: [{ $sort: { orderCount: -1 } }, { $limit: 5 }],    stats: [{ $group: { _id: null, avgOrders: { $avg: '$orderCount' } } }],  }},];``` ### Analytics Examples```typescript// Sales by monthconst salesByMonth = await Order.aggregate([  { $match: { status: 'completed' } },  { $group: {    _id: {      year: { $year: '$createdAt' },      month: { $month: '$createdAt' },    },    totalSales: { $sum: '$total' },    orderCount: { $sum: 1 },  }},  { $sort: { '_id.year': -1, '_id.month': -1 } },]); // Top productsconst topProducts = await Order.aggregate([  { $unwind: '$items' },  { $group: {    _id: '$items.productId',    totalQuantity: { $sum: '$items.quantity' },    totalRevenue: { $sum: { $multiply: ['$items.price', '$items.quantity'] } },  }},  { $lookup: {    from: 'products',    localField: '_id',    foreignField: '_id',    as: 'product',  }},  { $unwind: '$product' },  { $project: {    name: '$product.name',    totalQuantity: 1,    totalRevenue: 1,  }},  { $sort: { totalRevenue: -1 } },  { $limit: 10 },]);``` ## Middleware (Hooks) ```typescript// Pre-save middlewareuserSchema.pre('save', async function(next) {  if (this.isModified('password')) {    this.password = await bcrypt.hash(this.password, 12);  }  next();}); // Post-save middlewareuserSchema.post('save', function(doc) {  console.log('User saved:', doc._id);}); // Pre-find middlewareuserSchema.pre(/^find/, function(next) {  // Exclude deleted users by default  this.find({ isDeleted: { $ne: true } });  next();}); // Pre-aggregate middlewareuserSchema.pre('aggregate', function(next) {  // Add match stage to all aggregations  this.pipeline().unshift({ $match: { isDeleted: { $ne: true } } });  next();});``` ## Transactions ```typescriptconst session = await mongoose.startSession(); try {  session.startTransaction();    // All operations in the transaction  const user = await User.create([{ name: 'Alice' }], { session });  await Account.create([{ userId: user[0]._id, balance: 0 }], { session });  await Order.updateOne({ _id: orderId }, { $set: { status: 'paid' } }, { session });    await session.commitTransaction();} catch (error) {  await session.abortTransaction();  throw error;} finally {  session.endSession();} // With callbackawait mongoose.connection.transaction(async (session) => {  await User.create([{ name: 'Alice' }], { session });  await Account.create([{ userId: user._id }], { session });});``` ## Indexing ```typescript// Single field indexuserSchema.index({ email: 1 }); // Compound indexuserSchema.index({ role: 1, createdAt: -1 }); // Unique indexuserSchema.index({ email: 1 }, { unique: true }); // Partial indexuserSchema.index(  { email: 1 },  { partialFilterExpression: { isActive: true } }); // TTL index (auto-delete after time)sessionSchema.index({ createdAt: 1 }, { expireAfterSeconds: 3600 }); // Text index for searchpostSchema.index({ title: 'text', content: 'text' }); // Geospatial indexlocationSchema.index({ coordinates: '2dsphere' }); // Check indexesconst indexes = await User.collection.getIndexes();``` ## Performance Tips ```typescript// Use lean() for read-only queriesconst users = await User.find().lean(); // Select only needed fieldsconst users = await User.find().select('name email'); // Use cursor for large datasetsconst cursor = User.find().cursor();for await (const user of cursor) {  // Process one at a time} // Bulk operationsconst bulkOps = [  { insertOne: { document: { name: 'User 1' } } },  { updateOne: { filter: { _id: id1 }, update: { $set: { name: 'Updated' } } } },  { deleteOne: { filter: { _id: id2 } } },];await User.bulkWrite(bulkOps); // Explain queryconst explanation = await User.find({ role: 'admin' }).explain('executionStats');``` ## MongoDB Atlas ```typescript// Atlas connection stringconst uri = 'mongodb+srv://user:password@cluster.mongodb.net/dbname?retryWrites=true&w=majority'; // Atlas Search (full-text search)const results = await Product.aggregate([  { $search: {    index: 'default',    text: {      query: 'wireless headphones',      path: ['name', 'description'],      fuzzy: { maxEdits: 1 },    },  }},  { $project: {    name: 1,    score: { $meta: 'searchScore' },  }},]); // Atlas Vector Searchconst results = await Product.aggregate([  { $vectorSearch: {    index: 'vector_index',    path: 'embedding',    queryVector: [0.1, 0.2, ...],    numCandidates: 100,    limit: 10,  }},]);``` ## Resources - **MongoDB Docs**: https://www.mongodb.com/docs/- **Mongoose Docs**: https://mongoosejs.com/docs/- **MongoDB University**: https://learn.mongodb.com/- **Atlas Docs**: https://www.mongodb.com/docs/atlas/