Complete MongoDB Tutorial: NoSQL Database for Modern Applications

# Tutorial Lengkap MongoDB: Database NoSQL untuk Aplikasi Modern MongoDB adalah database NoSQL document-oriented yang sangat populer untuk aplikasi modern. Dengan schema yang fleksibel dan kemampuan...

By Ruby Abdullah · · tutorial
MongoDBNoSQLDatabasePythonPyMongoFastAPI

Complete MongoDB Tutorial: NoSQL Database for Modern Applications

MongoDB is a highly popular document-oriented NoSQL database for modern applications. With flexible schema and horizontal scaling capabilities, MongoDB is suitable for various use cases from web applications to big data analytics.

What is MongoDB?

MongoDB stores data in JSON-like documents (BSON) providing:

  • Schema flexibility: No need to define data structure beforehand
  • Horizontal scaling: Easy to scale with sharding
  • High performance: Optimized for read/write operations
  • Rich query language: Support for complex queries and aggregations
  • Native replication: Built-in high availability

Use Cases:
  • Content management systems
  • Real-time analytics
  • Mobile applications
  • IoT data storage
  • E-commerce platforms
  • Social media applications

Installing MongoDB

1. Install on Ubuntu

# Import MongoDB public GPG key

curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \

sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor

Add MongoDB repository

echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \

sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list

Update and install

sudo apt update

sudo apt install -y mongodb-org

Start MongoDB

sudo systemctl start mongod

sudo systemctl enable mongod

Verify installation

mongosh --eval 'db.runCommand({ connectionStatus: 1 })'

2. Install via Docker

# Pull MongoDB image

docker pull mongo:7.0

Run MongoDB container

docker run -d \

--name mongodb \

-p 27017:27017 \

-e MONGOINITDBROOTUSERNAME=admin \

-e MONGOINITDBROOTPASSWORD=password123 \

-v mongodbdata:/data/db \

mongo:7.0

Connect with mongosh

docker exec -it mongodb mongosh -u admin -p password123

3. MongoDB Atlas (Cloud)

For production, MongoDB Atlas provides a managed service:

  • Create account at MongoDB Atlas
  • Create cluster (free tier available)
  • Configure network access
  • Get connection string
  • # Connection string format
    

    connectionstring = "mongodb+srv://username:password@cluster.mongodb.net/dbname"

    MongoDB Shell (mongosh)

    1. Basic Commands

    // Connect to MongoDB
    

    mongosh

    // Show databases

    show dbs

    // Use/create database

    use myapp

    // Show collections

    show collections

    // Current database

    db

    // Database stats

    db.stats()

    // Drop database

    db.dropDatabase()

    2. CRUD Operations

    Create (Insert):
    // Insert single document
    

    db.users.insertOne({

    name: "Ruby Abdullah",

    email: "ruby@example.com",

    age: 30,

    skills: ["Python", "JavaScript", "MongoDB"],

    address: {

    city: "Jakarta",

    country: "Indonesia"

    },

    createdAt: new Date()

    })

    // Insert multiple documents

    db.users.insertMany([

    { name: "Alice", email: "alice@example.com", age: 25 },

    { name: "Bob", email: "bob@example.com", age: 35 },

    { name: "Charlie", email: "charlie@example.com", age: 28 }

    ])

    Read (Query):
    // Find all documents
    

    db.users.find()

    // Find with filter

    db.users.find({ age: { $gt: 25 } })

    // Find one document

    db.users.findOne({ email: "ruby@example.com" })

    // Projection (select fields)

    db.users.find({}, { name: 1, email: 1, id: 0 })

    // Sort

    db.users.find().sort({ age: -1 }) // Descending

    // Limit and skip (pagination)

    db.users.find().skip(10).limit(5)

    // Count

    db.users.countDocuments({ age: { $gt: 25 } })

    Update:
    // Update one document
    

    db.users.updateOne(

    { email: "ruby@example.com" },

    { $set: { age: 31 } }

    )

    // Update many documents

    db.users.updateMany(

    { age: { $lt: 30 } },

    { $set: { status: "young" } }

    )

    // Replace entire document

    db.users.replaceOne(

    { email: "bob@example.com" },

    { name: "Bob Smith", email: "bob@example.com", age: 36 }

    )

    // Upsert (update or insert)

    db.users.updateOne(

    { email: "new@example.com" },

    { $set: { name: "New User", age: 20 } },

    { upsert: true }

    )

    Delete:
    // Delete one document
    

    db.users.deleteOne({ email: "alice@example.com" })

    // Delete many documents

    db.users.deleteMany({ age: { $lt: 25 } })

    // Delete all documents in collection

    db.users.deleteMany({})

    3. Query Operators

    Comparison:
    // Equal
    

    db.users.find({ age: 30 })

    // Not equal

    db.users.find({ age: { $ne: 30 } })

    // Greater than / less than

    db.users.find({ age: { $gt: 25, $lt: 40 } })

    // Greater/less than or equal

    db.users.find({ age: { $gte: 25, $lte: 40 } })

    // In array of values

    db.users.find({ age: { $in: [25, 30, 35] } })

    // Not in array

    db.users.find({ age: { $nin: [25, 30] } })

    Logical:
    // AND (implicit)
    

    db.users.find({ age: { $gt: 25 }, status: "active" })

    // AND (explicit)

    db.users.find({

    $and: [

    { age: { $gt: 25 } },

    { status: "active" }

    ]

    })

    // OR

    db.users.find({

    $or: [

    { age: { $lt: 25 } },

    { status: "premium" }

    ]

    })

    // NOT

    db.users.find({

    age: { $not: { $gt: 30 } }

    })

    // NOR (neither)

    db.users.find({

    $nor: [

    { age: { $lt: 25 } },

    { status: "inactive" }

    ]

    })

    Array:
    // Match array element
    

    db.users.find({ skills: "Python" })

    // Match all elements

    db.users.find({ skills: { $all: ["Python", "MongoDB"] } })

    // Array size

    db.users.find({ skills: { $size: 3 } })

    // Element match (for array of objects)

    db.orders.find({

    items: {

    $elemMatch: {

    product: "Laptop",

    quantity: { $gt: 1 }

    }

    }

    })

    Element:
    // Field exists
    

    db.users.find({ phone: { $exists: true } })

    // Field type

    db.users.find({ age: { $type: "int" } })

    Text Search:
    // Create text index first
    

    db.articles.createIndex({ title: "text", content: "text" })

    // Text search

    db.articles.find({ $text: { $search: "mongodb tutorial" } })

    // Text search with score

    db.articles.find(

    { $text: { $search: "mongodb" } },

    { score: { $meta: "textScore" } }

    ).sort({ score: { $meta: "textScore" } })

    Indexes

    1. Index Types

    // Single field index
    

    db.users.createIndex({ email: 1 }) // 1 = ascending, -1 = descending

    // Compound index

    db.users.createIndex({ lastName: 1, firstName: 1 })

    // Unique index

    db.users.createIndex({ email: 1 }, { unique: true })

    // Text index

    db.articles.createIndex({ content: "text" })

    // Geospatial index

    db.places.createIndex({ location: "2dsphere" })

    // TTL index (auto-delete after time)

    db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })

    2. Index Management

    // List indexes
    

    db.users.getIndexes()

    // Drop index

    db.users.dropIndex("email1")

    // Drop all indexes (except id)

    db.users.dropIndexes()

    // Explain query (check index usage)

    db.users.find({ email: "ruby@example.com" }).explain("executionStats")

    Aggregation Framework

    1. Basic Aggregation

    // Count by field
    

    db.users.aggregate([

    { $group: { id: "$status", count: { $sum: 1 } } }

    ])

    // Average

    db.users.aggregate([

    { $group: { id: null, avgAge: { $avg: "$age" } } }

    ])

    // Multiple aggregations

    db.orders.aggregate([

    { $match: { status: "completed" } },

    {

    $group: {

    id: "$customerId",

    totalOrders: { $sum: 1 },

    totalSpent: { $sum: "$total" },

    avgOrderValue: { $avg: "$total" }

    }

    },

    { $sort: { totalSpent: -1 } },

    { $limit: 10 }

    ])

    2. Aggregation Stages

    // $match - Filter documents
    

    { $match: { status: "active" } }

    // $group - Group and aggregate

    {

    $group: {

    id: "$category",

    total: { $sum: "$price" },

    avg: { $avg: "$price" },

    min: { $min: "$price" },

    max: { $max: "$price" },

    count: { $sum: 1 }

    }

    }

    // $project - Reshape documents

    {

    $project: {

    name: 1,

    fullName: { $concat: ["$firstName", " ", "$lastName"] },

    yearJoined: { $year: "$createdAt" }

    }

    }

    // $sort - Sort results

    { $sort: { total: -1, name: 1 } }

    // $limit and $skip

    { $skip: 10 }

    { $limit: 5 }

    // $unwind - Deconstruct array

    { $unwind: "$items" }

    // $lookup - Join collections

    {

    $lookup: {

    from: "products",

    localField: "productId",

    foreignField: "id",

    as: "productDetails"

    }

    }

    // $addFields - Add new fields

    {

    $addFields: {

    totalWithTax: { $multiply: ["$total", 1.1] }

    }

    }

    // $facet - Multiple pipelines

    {

    $facet: {

    byCategory: [

    { $group: { id: "$category", count: { $sum: 1 } } }

    ],

    byStatus: [

    { $group: { id: "$status", count: { $sum: 1 } } }

    ],

    totalCount: [

    { $count: "total" }

    ]

    }

    }

    3. Real-world Aggregation Examples

    // E-commerce: Top selling products
    

    db.orders.aggregate([

    { $unwind: "$items" },

    {

    $group: {

    id: "$items.productId",

    totalSold: { $sum: "$items.quantity" },

    revenue: { $sum: { $multiply: ["$items.price", "$items.quantity"] } }

    }

    },

    {

    $lookup: {

    from: "products",

    localField: "id",

    foreignField: "id",

    as: "product"

    }

    },

    { $unwind: "$product" },

    {

    $project: {

    productName: "$product.name",

    totalSold: 1,

    revenue: 1

    }

    },

    { $sort: { revenue: -1 } },

    { $limit: 10 }

    ])

    // User activity report

    db.events.aggregate([

    {

    $match: {

    timestamp: {

    $gte: ISODate("2024-01-01"),

    $lt: ISODate("2024-02-01")

    }

    }

    },

    {

    $group: {

    id: {

    userId: "$userId",

    day: { $dateToString: { format: "%Y-%m-%d", date: "$timestamp" } }

    },

    eventCount: { $sum: 1 },

    uniqueActions: { $addToSet: "$action" }

    }

    },

    {

    $group: {

    id: "$id.userId",

    activeDays: { $sum: 1 },

    totalEvents: { $sum: "$eventCount" },

    avgEventsPerDay: { $avg: "$eventCount" }

    }

    },

    { $sort: { totalEvents: -1 } }

    ])

    Python with PyMongo

    1. Setup and Connection

    from pymongo import MongoClient
    

    from pymongo.errors import ConnectionFailure

    from bson.objectid import ObjectId

    from datetime import datetime

    Basic connection

    client = MongoClient("mongodb://localhost:27017/")

    Connection with authentication

    client = MongoClient(

    "mongodb://admin:password123@localhost:27017/",

    authSource="admin"

    )

    MongoDB Atlas connection

    client = MongoClient(

    "mongodb+srv://user:password@cluster.mongodb.net/?retryWrites=true&w=majority"

    )

    Test connection

    try:

    client.admin.command('ping')

    print("Connected successfully!")

    except ConnectionFailure:

    print("Connection failed!")

    Select database and collection

    db = client["myapp"]

    users = db["users"]

    2. CRUD with PyMongo

    from pymongo import MongoClient
    

    from bson.objectid import ObjectId

    from datetime import datetime

    client = MongoClient("mongodb://localhost:27017/")

    db = client["myapp"]

    users = db["users"]

    Insert

    def createuser(name, email, age):

    user = {

    "name": name,

    "email": email,

    "age": age,

    "createdat": datetime.utcnow()

    }

    result = users.insertone(user)

    return result.insertedid

    Insert many

    def createusers(userlist):

    result = users.insertmany(userlist)

    return result.insertedids

    Read

    def getuserbyemail(email):

    return users.findone({"email": email})

    def getusers(filters=None, limit=10, skip=0):

    query = filters or {}

    return list(users.find(query).skip(skip).limit(limit))

    def getuserbyid(userid):

    return users.findone({"id": ObjectId(userid)})

    Update

    def updateuser(userid, updates):

    result = users.updateone(

    {"id": ObjectId(userid)},

    {"$set": updates}

    )

    return result.modifiedcount

    def incrementage(userid):

    result = users.updateone(

    {"id": ObjectId(userid)},

    {"$inc": {"age": 1}}

    )

    return result.modifiedcount

    Delete

    def deleteuser(userid):

    result = users.deleteone({"id": ObjectId(userid)})

    return result.deletedcount

    Usage

    userid = createuser("Ruby", "ruby@example.com", 30)

    print(f"Created user: {userid}")

    user = getuserbyemail("ruby@example.com")

    print(f"Found user: {user}")

    updateuser(userid, {"age": 31, "status": "active"})

    3. Aggregation with PyMongo

    from pymongo import MongoClient
    
    

    client = MongoClient("mongodb://localhost:27017/")

    db = client["ecommerce"]

    orders = db["orders"]

    Aggregation pipeline

    def getsalesbycategory():

    pipeline = [

    {"$unwind": "$items"},

    {

    "$group": {

    "id": "$items.category",

    "totalsales": {"$sum": {"$multiply": ["$items.price", "$items.quantity"]}},

    "totalitems": {"$sum": "$items.quantity"},

    "ordercount": {"$sum": 1}

    }

    },

    {"$sort": {"totalsales": -1}}

    ]

    return list(orders.aggregate(pipeline))

    Customer analytics

    def gettopcustomers(limit=10):

    pipeline = [

    {"$match": {"status": "completed"}},

    {

    "$group": {

    "id": "$customerid",

    "totalspent": {"$sum": "$total"},

    "ordercount": {"$sum": 1},

    "avgordervalue": {"$avg": "$total"}

    }

    },

    {"$sort": {"totalspent": -1}},

    {"$limit": limit},

    {

    "$lookup": {

    "from": "customers",

    "localField": "id",

    "foreignField": "id",

    "as": "customerinfo"

    }

    },

    {"$unwind": "$customerinfo"},

    {

    "$project": {

    "customername": "$customerinfo.name",

    "email": "$customerinfo.email",

    "totalspent": 1,

    "ordercount": 1,

    "avgordervalue": {"$round": ["$avgordervalue", 2]}

    }

    }

    ]

    return list(orders.aggregate(pipeline))

    Time series analysis

    def getdailyrevenue(startdate, enddate):

    pipeline = [

    {

    "$match": {

    "createdat": {"$gte": startdate, "$lt": enddate},

    "status": "completed"

    }

    },

    {

    "$group": {

    "id": {

    "$dateToString": {"format": "%Y-%m-%d", "date": "$createdat"}

    },

    "revenue": {"$sum": "$total"},

    "orders": {"$sum": 1}

    }

    },

    {"$sort": {"id": 1}}

    ]

    return list(orders.aggregate(pipeline))

    4. Index Management

    from pymongo import MongoClient, ASCENDING, DESCENDING, TEXT
    
    

    client = MongoClient("mongodb://localhost:27017/")

    db = client["myapp"]

    users = db["users"]

    Create indexes

    users.createindex("email", unique=True)

    users.createindex([("lastname", ASCENDING), ("firstname", ASCENDING)])

    users.createindex([("location", "2dsphere")])

    users.createindex([("bio", TEXT), ("interests", TEXT)])

    Create TTL index

    sessions = db["sessions"]

    sessions.createindex("createdat", expireAfterSeconds=3600)

    List indexes

    for index in users.listindexes():

    print(index)

    Drop index

    users.dropindex("email1")

    Motor: Async MongoDB Driver

    import asyncio
    

    from motor.motorasyncio import AsyncIOMotorClient

    async def main():

    # Connect

    client = AsyncIOMotorClient("mongodb://localhost:27017/")

    db = client["myapp"]

    users = db["users"]

    # Insert

    result = await users.insertone({

    "name": "Async User",

    "email": "async@example.com"

    })

    print(f"Inserted: {result.insertedid}")

    # Find

    user = await users.findone({"email": "async@example.com"})

    print(f"Found: {user}")

    # Find many

    cursor = users.find({"status": "active"})

    async for doc in cursor:

    print(doc)

    # Aggregation

    pipeline = [

    {"$group": {"id": "$status", "count": {"$sum": 1}}}

    ]

    cursor = users.aggregate(pipeline)

    async for doc in cursor:

    print(doc)

    asyncio.run(main())

    FastAPI with MongoDB

    from fastapi import FastAPI, HTTPException
    

    from pydantic import BaseModel, Field

    from typing import Optional, List

    from motor.motorasyncio import AsyncIOMotorClient

    from bson import ObjectId

    from datetime import datetime

    app = FastAPI()

    MongoDB connection

    client = AsyncIOMotorClient("mongodb://localhost:27017/")

    db = client["myapp"]

    Pydantic models

    class PyObjectId(ObjectId):

    @classmethod

    def _getvalidators_(cls):

    yield cls.validate

    @classmethod

    def validate(cls, v, handler):

    if not ObjectId.isvalid(v):

    raise ValueError("Invalid ObjectId")

    return ObjectId(v)

    class UserCreate(BaseModel):

    name: str

    email: str

    age: int

    class UserResponse(BaseModel):

    id: str = Field(alias="id")

    name: str

    email: str

    age: int

    createdat: datetime

    class Config:

    populatebyname = True

    jsonencoders = {ObjectId: str}

    Routes

    @app.post("/users", responsemodel=UserResponse)

    async def createuser(user: UserCreate):

    userdict = user.modeldump()

    userdict["createdat"] = datetime.utcnow()

    result = await db.users.insertone(userdict)

    created = await db.users.findone({"id": result.insertedid})

    created["id"] = str(created["id"])

    return created

    @app.get("/users", responsemodel=List[UserResponse])

    async def getusers(skip: int = 0, limit: int = 10):

    cursor = db.users.find().skip(skip).limit(limit)

    users = await cursor.tolist(length=limit)

    for user in users:

    user["id"] = str(user["id"])

    return users

    @app.get("/users/{userid}", responsemodel=UserResponse)

    async def getuser(userid: str):

    user = await db.users.findone({"id": ObjectId(userid)})

    if not user:

    raise HTTPException(statuscode=404, detail="User not found")

    user["id"] = str(user["id"])

    return user

    @app.put("/users/{userid}", responsemodel=UserResponse)

    async def updateuser(userid: str, user: UserCreate):

    result = await db.users.updateone(

    {"id": ObjectId(userid)},

    {"$set": user.modeldump()}

    )

    if result.modifiedcount == 0:

    raise HTTPException(statuscode=404, detail="User not found")

    updated = await db.users.findone({"id": ObjectId(userid)})

    updated["id"] = str(updated["id"])

    return updated

    @app.delete("/users/{userid}")

    async def deleteuser(userid: str):

    result = await db.users.deleteone({"id": ObjectId(userid)})

    if result.deletedcount == 0:

    raise HTTPException(statuscode=404, detail="User not found")

    return {"message": "User deleted"}

    Run: uvicorn main:app --reload

    Best Practices

    1. Schema Design

    # Embedding vs Referencing
    
    

    Embedding (for data always accessed together)

    userwithaddress = {

    "name": "Ruby",

    "email": "ruby@example.com",

    "addresses": [

    {"type": "home", "city": "Jakarta", "country": "Indonesia"},

    {"type": "work", "city": "Bandung", "country": "Indonesia"}

    ]

    }

    Referencing (for large or frequently updated data)

    user = {

    "name": "Ruby",

    "email": "ruby@example.com"

    }

    order = {

    "userid": ObjectId("..."), # Reference to user

    "items": [...],

    "total": 100

    }

    2. Connection Management

    from pymongo import MongoClient
    

    from contextlib import contextmanager

    Connection pooling

    client = MongoClient(

    "mongodb://localhost:27017/",

    maxPoolSize=50,

    minPoolSize=10,

    maxIdleTimeMS=30000

    )

    Context manager for transactions

    @contextmanager

    def getsession():

    with client.startsession() as session:

    yield session

    Usage with transaction

    with getsession() as session:

    with session.starttransaction():

    db.users.insertone({"name": "Test"}, session=session)

    db.logs.insertone({"action": "usercreated"}, session=session)

    3. Error Handling

    from pymongo.errors import (
    

    ConnectionFailure,

    DuplicateKeyError,

    WriteError

    )

    def safeinsert(collection, document):

    try:

    result = collection.insertone(document)

    return result.insertedid

    except DuplicateKeyError:

    print("Document already exists")

    return None

    except WriteError as e:

    print(f"Write error: {e}")

    return None

    except ConnectionFailure:

    print("Connection failed")

    return None

    4. Performance Tips

    # Use projection to reduce data transfer
    

    users.find({}, {"name": 1, "email": 1})

    Batch operations

    from pymongo import InsertOne, UpdateOne

    operations = [

    InsertOne({"name": "User 1"}),

    InsertOne({"name": "User 2"}),

    UpdateOne({"name": "User 1"}, {"$set": {"status": "active"}})

    ]

    result = users.bulkwrite(operations)

    Create appropriate indexes

    users.createindex([("email", 1)], unique=True)

    users.createindex([("status", 1), ("created_at", -1)])

    Conclusion

    MongoDB is a powerful and flexible NoSQL database for modern applications. Key takeaways:

  • Document-oriented: Data stored as JSON-like documents
  • Schema flexibility: No fixed schema required
  • Rich queries: Support for complex queries and aggregations
  • Scalable: Horizontal scaling with sharding
  • Python integration: PyMongo and Motor for sync/async
  • Usage tips:

    • Design schema based on access patterns
    • Use indexes for frequently used queries
    • Leverage aggregation framework for analytics
    • Implement proper connection pooling
    • Handle errors gracefully

    Related Articles

    LitServe Tutorial: Fast and Easy AI Model Serving Framework

    Tutorial LitServe: Framework Serving Model AI yang Cepat dan Mudah Pendahuluan LitServe adalah framework open-source dar...

    Reflex Tutorial: Building Full-Stack Web Apps in Pure Python

    Reflex: Membangun Aplikasi Web Full-Stack dengan Python Murni Reflex memungkinkan Anda membangun aplikasi web lengkap — ...

    SQLModel: Modern Python ORM for Type-Safe AI Applications

    SQLModel: ORM Modern Python untuk Aplikasi AI yang Type-Safe Dalam pengembangan aplikasi AI/ML, pengelolaan data di data...

    Complete FastAPI for Machine Learning Tutorial: Building Production ML APIs

    Tutorial Lengkap FastAPI untuk ML: Build Production ML APIs FastAPI adalah framework web Python modern dengan performa t...