Section 11: Projects

πŸ• Food Delivery Platform Project

Build a Scalable MongoDB-Powered Food Delivery System

🎯 Project Overview

Welcome to the Food Delivery Platform project! In this comprehensive hands-on tutorial, you'll build a production-ready food delivery system similar to Uber Eats, DoorDash, or Swiggy. You'll implement restaurant management, real-time order tracking, geospatial queries for nearby restaurants, delivery partner assignment, and advanced analytics.

πŸŽ“ Your Learning Journey
Phase 1: Restaurants Phase 2: Menus Phase 3: Orders Phase 4: Delivery Phase 5: Geospatial Phase 6: Analytics

⏱️ Estimated completion time: 3-4 hours | πŸ’ͺ Difficulty: Intermediate to Advanced

πŸ’‘ What Makes This Project Unique?

This project introduces MongoDB's powerful geospatial features, time-series data for delivery tracking, and complex aggregation pipelines for multi-restaurant analytics. You'll learn patterns used by billion-dollar food delivery platforms.

7
Collections
20+
Features
60+
Queries
Geo
Spatial Queries

πŸ“– Meet QuickBite: Your Food Delivery Platform

The Challenge: Maria is launching QuickBite, a food delivery platform connecting hungry customers with local restaurants. The system needs to handle thousands of restaurants across multiple cities, match customers with nearby restaurants based on location, track orders in real-time, optimize delivery routes, and provide insights into food trends and restaurant performance.

Your Mission: As the database architect, you'll design a MongoDB backend that supports:

  • βœ… Restaurant onboarding with location-based discovery
  • βœ… Dynamic menu management with customizations
  • βœ… Real-time order placement and tracking
  • βœ… Delivery partner assignment and route optimization
  • βœ… Geospatial queries (find restaurants within 5km)
  • βœ… Rating and review system
  • βœ… Business analytics and reporting

πŸŽ“ Learning Objectives

By completing this project, you will master:

πŸ—ΊοΈ Geospatial Queries

Use 2dsphere indexes, $near, $geoWithin for location-based features

πŸ“¦ Embedded Documents

Model complex menu items with nested customizations and add-ons

⏱️ Real-Time Tracking

Implement order status updates and delivery location tracking

πŸ” Advanced Aggregation

Build multi-stage pipelines for restaurant analytics

⚑ Query Optimization

Optimize geospatial queries and complex lookups

πŸ“Š Time-Series Data

Track delivery progress with location timestamps

πŸ“ Database Schema Design

Our food delivery platform consists of 7 interconnected collections:

πŸ”— Collection Relationships
    customers ──┬──> orders ──┬──> menuItems ──> restaurants
                β”‚              β”‚
                β”‚              └──> deliveryPartners
                β”‚
                └──> reviews ──> restaurants
            

Key Relationships:
β€’ Orders reference customers, restaurants, and menuItems
β€’ Reviews link customers to restaurants
β€’ Delivery partners are assigned to orders
β€’ Menu items belong to restaurants

πŸͺ restaurants - Restaurant Directory

Store restaurant information with geospatial location data

Field Type Notes
_idObjectIdAuto-generated
nameStringText indexed
descriptionString-
cuisineTypeArray[String]Indexed
locationGeoJSON Point⭐ 2dsphere index
addressObject{street, city, zipCode}
phoneString-
openingHoursObject{open, close}
ratingObject{average, count}
isActiveBooleanIndexed
deliveryFeeNumber-
minOrderAmountNumber-

πŸ” menuItems - Restaurant Menus

Food items with categories, prices, and customizations

  • _id ObjectId
  • restaurantId ObjectId (reference, indexed)
  • name String (text indexed)
  • description String
  • category String (indexed)
  • price Number
  • image String
  • isVegetarian Boolean
  • isAvailable Boolean
  • customizations Array [{name, options, type}]
  • addOns Array [{name, price}]

πŸ‘₯ customers - Customer Profiles

Customer information with delivery addresses

  • _id ObjectId
  • name String
  • email String (unique, indexed)
  • phone String
  • addresses Array [{name, location (GeoJSON), isDefault}]
  • createdAt Date

πŸ“‹ orders - Customer Orders

Complete order information with items and delivery details

  • _id ObjectId
  • orderNumber String (unique, indexed)
  • customerId ObjectId (reference, indexed)
  • restaurantId ObjectId (reference, indexed)
  • items Array [{menuItemId, name, quantity, price, customizations}]
  • subtotal Number
  • deliveryFee Number
  • tax Number
  • total Number
  • status String (indexed)
  • deliveryAddress Object with GeoJSON location
  • deliveryPartnerId ObjectId (reference)
  • createdAt Date (indexed)
  • estimatedDeliveryTime Date
  • deliveredAt Date

🏍️ deliveryPartners - Delivery Personnel

Delivery partner information with real-time location

  • _id ObjectId
  • name String
  • phone String
  • vehicleType String
  • currentLocation GeoJSON Point (2dsphere)
  • isAvailable Boolean (indexed)
  • currentOrderId ObjectId
  • rating Object {average, count}

⭐ reviews - Ratings & Reviews

Customer feedback for restaurants and delivery

  • _id ObjectId
  • orderId ObjectId (reference)
  • customerId ObjectId (reference)
  • restaurantId ObjectId (reference, indexed)
  • restaurantRating Number (1-5)
  • foodRating Number (1-5)
  • deliveryRating Number (1-5)
  • comment String
  • createdAt Date

πŸ“ deliveryTracking - Location History

Time-series data for delivery route tracking

  • _id ObjectId
  • orderId ObjectId (reference, indexed)
  • deliveryPartnerId ObjectId (reference)
  • location GeoJSON Point
  • timestamp Date (indexed)
  • status String
🎯 Key Design Decisions:
  • GeoJSON Points: All locations use GeoJSON format for geospatial queries
  • Embedded Items: Order items are embedded to preserve historical menu prices
  • Separate Tracking: Location history in dedicated collection for scalability
  • Status Indexing: Quick filtering of active orders and available partners

πŸ”§ Initial Setup

1. Create the Database

Initialize QuickBite Database
        use quickBite

        // Verify creation
        db.getName()
        // Output: "quickBite"

2. Create Collections

        db.createCollection("restaurants")
        db.createCollection("menuItems")
        db.createCollection("customers")
        db.createCollection("orders")
        db.createCollection("deliveryPartners")
        db.createCollection("reviews")
        db.createCollection("deliveryTracking")

3.Create Geospatial Indexes

Critical: Geospatial indexes for location queries
        // Restaurant location index (most important!)
        db.restaurants.createIndex({ location: "2dsphere" })

        // Customer address locations
        db.customers.createIndex({ "addresses.location": "2dsphere" })

        // Delivery partner location
        db.deliveryPartners.createIndex({ currentLocation: "2dsphere" })

        // Delivery tracking locations
        db.deliveryTracking.createIndex({ location: "2dsphere" })

4. Create Other Essential Indexes

        // Restaurants
        db.restaurants.createIndex({ name: "text", description: "text" })
        db.restaurants.createIndex({ cuisineType: 1 })
        db.restaurants.createIndex({ isActive: 1 })

        // Menu items
        db.menuItems.createIndex({ restaurantId: 1 })
        db.menuItems.createIndex({ name: "text" })
        db.menuItems.createIndex({ category: 1 })

        // Customers
        db.customers.createIndex({ email: 1 }, { unique: true })

        // Orders
        db.orders.createIndex({ orderNumber: 1 }, { unique: true })
        db.orders.createIndex({ customerId: 1, createdAt: -1 })
        db.orders.createIndex({ restaurantId: 1, createdAt: -1 })
        db.orders.createIndex({ status: 1 })
        db.orders.createIndex({ createdAt: -1 })

        // Delivery partners
        db.deliveryPartners.createIndex({ isAvailable: 1 })

        // Reviews
        db.reviews.createIndex({ restaurantId: 1 })
        db.reviews.createIndex({ orderId: 1 })

        // Delivery tracking
        db.deliveryTracking.createIndex({ orderId: 1, timestamp: 1 })
βœ… Setup Complete!

Your QuickBite database is now ready with all collections and geospatial indexes configured for location-based queries!

πŸͺ Phase 1: Restaurant Management

πŸ“‹ Phase 1 Objectives
βœ“ What You'll Learn:
  • Insert documents with GeoJSON locations
  • Create 2dsphere indexes for spatial queries
  • Query restaurants by cuisine and rating
  • Update restaurant information
🎯 Key Concepts:
  • GeoJSON Point format
  • Coordinate ordering [lng, lat]
  • Embedded documents for addresses
  • Boolean and number field types

Add Restaurants with GeoJSON Locations

Insert Restaurants with Coordinates 🟒 BEGINNER
        db.restaurants.insertMany([
        {
            name: "Pizza Paradise",
            description: "Authentic Italian pizzas with wood-fired oven",
            cuisineType: ["Italian", "Pizza"],
            location: {
            type: "Point",
            coordinates: [-122.4194, 37.7749] // [longitude, latitude] - San Francisco
            },
            address: {
            street: "123 Main Street",
            city: "San Francisco",
            state: "CA",
            zipCode: "94102"
            },
            phone: "+1-415-555-0100",
            openingHours: {
            monday: { open: "11:00", close: "22:00" },
            tuesday: { open: "11:00", close: "22:00" },
            wednesday: { open: "11:00", close: "22:00" },
            thursday: { open: "11:00", close: "22:00" },
            friday: { open: "11:00", close: "23:00" },
            saturday: { open: "11:00", close: "23:00" },
            sunday: { open: "12:00", close: "22:00" }
            },
            rating: {
            average: 4.5,
            count: 328
            },
            isActive: true,
            deliveryFee: 3.99,
            minOrderAmount: 15
        },
        {
            name: "Sushi Express",
            description: "Fresh sushi and Japanese cuisine",
            cuisineType: ["Japanese", "Sushi", "Asian"],
            location: {
            type: "Point",
            coordinates: [-122.4200, 37.7750] // ~0.5 km from Pizza Paradise
            },
            address: {
            street: "456 Market Street",
            city: "San Francisco",
            state: "CA",
            zipCode: "94103"
            },
            phone: "+1-415-555-0101",
            openingHours: {
            monday: { open: "12:00", close: "21:00" },
            tuesday: { open: "12:00", close: "21:00" },
            wednesday: { open: "12:00", close: "21:00" },
            thursday: { open: "12:00", close: "21:00" },
            friday: { open: "12:00", close: "22:00" },
            saturday: { open: "12:00", close: "22:00" },
            sunday: { open: "12:00", close: "21:00" }
            },
            rating: {
            average: 4.7,
            count: 512
            },
            isActive: true,
            deliveryFee: 4.99,
            minOrderAmount: 20
        },
        {
            name: "Burger Bliss",
            description: "Gourmet burgers and American comfort food",
            cuisineType: ["American", "Burgers", "Fast Food"],
            location: {
            type: "Point",
            coordinates: [-122.4180, 37.7740] // ~1 km away
            },
            address: {
            street: "789 Mission Street",
            city: "San Francisco",
            state: "CA",
            zipCode: "94103"
            },
            phone: "+1-415-555-0102",
            openingHours: {
            monday: { open: "10:00", close: "23:00" },
            tuesday: { open: "10:00", close: "23:00" },
            wednesday: { open: "10:00", close: "23:00" },
            thursday: { open: "10:00", close: "23:00" },
            friday: { open: "10:00", close: "00:00" },
            saturday: { open: "10:00", close: "00:00" },
            sunday: { open: "10:00", close: "23:00" }
            },
            rating: {
            average: 4.3,
            count: 267
            },
            isActive: true,
            deliveryFee: 2.99,
            minOrderAmount: 12
        },
        {
            name: "Tandoori Nights",
            description: "Authentic Indian cuisine with traditional flavors",
            cuisineType: ["Indian", "Curry", "Vegetarian"],
            location: {
            type: "Point",
            coordinates: [-122.4220, 37.7760] // ~1.5 km away
            },
            address: {
            street: "321 Valencia Street",
            city: "San Francisco",
            state: "CA",
            zipCode: "94110"
            },
            phone: "+1-415-555-0103",
            openingHours: {
            monday: { open: "11:30", close: "22:00" },
            tuesday: { open: "11:30", close: "22:00" },
            wednesday: { open: "11:30", close: "22:00" },
            thursday: { open: "11:30", close: "22:00" },
            friday: { open: "11:30", close: "23:00" },
            saturday: { open: "11:30", close: "23:00" },
            sunday: { open: "11:30", close: "22:00" }
            },
            rating: {
            average: 4.6,
            count: 445
            },
            isActive: true,
            deliveryFee: 4.49,
            minOrderAmount: 18
        }
        ])

Search Restaurants by Cuisine

Find Restaurants by Cuisine Type 🟒 BEGINNER
        // Find all Italian restaurants
        db.restaurants.find({ cuisineType: "Italian" })

        // Find restaurants with multiple cuisines (any match)
        db.restaurants.find({ cuisineType: { $in: ["Indian", "Japanese"] } })
πŸ“€ Expected Output
        {
          _id: ObjectId("..."),
          name: "Pizza Paradise",
          cuisineType: [ "Italian", "Pizza" ],
          rating: { average: 4.5, count: 328 },
          deliveryFee: 3.99,
          minOrderAmount: 15,
          ...
        }

πŸ’‘ What to notice: The query matches any document where the cuisineType array contains "Italian". MongoDB automatically searches within arrays without special syntax.

Update Restaurant Information

        // Update delivery fee
        db.restaurants.updateOne(
        { name: "Pizza Paradise" },
        { $set: { deliveryFee: 4.49 } }
        )

        // Mark restaurant as inactive
        db.restaurants.updateOne(
        { name: "Burger Bliss" },
        { $set: { isActive: false } }
        )

πŸ” Phase 2: Menu Management

πŸ“‹ Phase 2 Objectives
βœ“ What You'll Learn:
  • Model menu items with nested arrays
  • Create customization options
  • Reference restaurants with ObjectId
  • Query by category and price
🎯 Key Concepts:
  • Embedded documents for options
  • Arrays of objects pattern
  • Foreign key references
  • Range queries on price

Create Menu Items

Add Menu Items with Customizations
        // Get restaurant ID first
        var pizzaParadise = db.restaurants.findOne({ name: "Pizza Paradise" })

        db.menuItems.insertMany([
        {
            restaurantId: pizzaParadise._id,
            name: "Margherita Pizza",
            description: "Classic pizza with fresh mozzarella, tomatoes, and basil",
            category: "Pizza",
            price: 14.99,
            image: "/images/margherita.jpg",
            isVegetarian: true,
            isAvailable: true,
            customizations: [
            {
                name: "Size",
                options: [
                { value: "Small (10\")", priceModifier: -3 },
                { value: "Medium (12\")", priceModifier: 0 },
                { value: "Large (14\")", priceModifier: 4 },
                { value: "Extra Large (16\")", priceModifier: 7 }
                ],
                type: "single",
                required: true
            },
            {
                name: "Crust",
                options: [
                { value: "Thin Crust", priceModifier: 0 },
                { value: "Thick Crust", priceModifier: 1.5 },
                { value: "Stuffed Crust", priceModifier: 3 }
                ],
                type: "single",
                required: true
            }
            ],
            addOns: [
            { name: "Extra Cheese", price: 2.50 },
            { name: "Mushrooms", price: 1.50 },
            { name: "Olives", price: 1.50 },
            { name: "Pepperoni", price: 2.00 }
            ]
        },
        {
            restaurantId: pizzaParadise._id,
            name: "Pepperoni Pizza",
            description: "Loaded with premium pepperoni and cheese",
            category: "Pizza",
            price: 16.99,
            image: "/images/pepperoni.jpg",
            isVegetarian: false,
            isAvailable: true,
            customizations: [
            {
                name: "Size",
                options: [
                { value: "Medium (12\")", priceModifier: 0 },
                { value: "Large (14\")", priceModifier: 4 },
                { value: "Extra Large (16\")", priceModifier: 7 }
                ],
                type: "single",
                required: true
            }
            ],
            addOns: [
            { name: "Extra Pepperoni", price: 3.00 },
            { name: "JalapeΓ±os", price: 1.00 },
            { name: "Extra Cheese", price: 2.50 }
            ]
        },
        {
            restaurantId: pizzaParadise._id,
            name: "Garlic Breadsticks",
            description: "Freshly baked breadsticks with garlic butter",
            category: "Appetizers",
            price: 5.99,
            image: "/images/breadsticks.jpg",
            isVegetarian: true,
            isAvailable: true,
            customizations: [],
            addOns: [
            { name: "Marinara Sauce", price: 0.75 },
            { name: "Cheese Dip", price: 1.25 }
            ]
        }
        ])

        // Add Sushi Express menu
        var sushiExpress = db.restaurants.findOne({ name: "Sushi Express" })

        db.menuItems.insertMany([
        {
            restaurantId: sushiExpress._id,
            name: "California Roll",
            description: "Crab, avocado, and cucumber roll",
            category: "Sushi Rolls",
            price: 8.99,
            image: "/images/california-roll.jpg",
            isVegetarian: false,
            isAvailable: true,
            customizations: [
            {
                name: "Pieces",
                options: [
                { value: "6 pieces", priceModifier: 0 },
                { value: "12 pieces", priceModifier: 7 }
                ],
                type: "single",
                required: true
            }
            ],
            addOns: [
            { name: "Wasabi", price: 0 },
            { name: "Ginger", price: 0 },
            { name: "Extra Soy Sauce", price: 0.50 }
            ]
        },
        {
            restaurantId: sushiExpress._id,
            name: "Salmon Nigiri",
            description: "Fresh salmon over sushi rice",
            category: "Nigiri",
            price: 6.99,
            image: "/images/salmon-nigiri.jpg",
            isVegetarian: false,
            isAvailable: true,
            customizations: [
            {
                name: "Pieces",
                options: [
                { value: "2 pieces", priceModifier: 0 },
                { value: "4 pieces", priceModifier: 6 },
                { value: "6 pieces", priceModifier: 11 }
                ],
                type: "single",
                required: true
            }
            ],
            addOns: []
        }
        ])

Query Menu Items

Get Restaurant Menu
        // Get all menu items for Pizza Paradise
        db.menuItems.find({ restaurantId: pizzaParadise._id })

        // Get only available vegetarian items
        db.menuItems.find({
        restaurantId: pizzaParadise._id,
        isVegetarian: true,
        isAvailable: true
        })

        // Search menu items by name
        db.menuItems.find({
        restaurantId: pizzaParadise._id,
        $text: { $search: "pizza" }
        })

Update Menu Availability

        // Mark item as out of stock
        db.menuItems.updateOne(
        { name: "Margherita Pizza", restaurantId: pizzaParadise._id },
        { $set: { isAvailable: false } }
        )

        // Update item price
        db.menuItems.updateOne(
        { name: "Garlic Breadsticks" },
        { $set: { price: 6.49 } }
        )

πŸ“‹ Phase 3: Customer Orders

πŸ“‹ Phase 3 Objectives
βœ“ What You'll Learn:
  • Create customers with multiple addresses
  • Build complete order documents
  • Update order status transitions
  • Query orders by status and customer
🎯 Key Concepts:
  • Embedded order items array
  • Calculated fields (subtotal, tax, total)
  • Status workflow management
  • Timestamp tracking

Create Customer

        db.customers.insertOne({
        name: "John Doe",
        email: "john.doe@email.com",
        phone: "+1-415-555-9999",
        addresses: [
            {
            name: "Home",
            street: "150 Main Street, Apt 5B",
            city: "San Francisco",
            state: "CA",
            zipCode: "94102",
            location: {
                type: "Point",
                coordinates: [-122.4195, 37.7748] // Very close to Pizza Paradise
            },
            isDefault: true
            },
            {
            name: "Office",
            street: "500 Market Street, Floor 12",
            city: "San Francisco",
            state: "CA",
            zipCode: "94105",
            location: {
                type: "Point",
                coordinates: [-122.3985, 37.7900]
            },
            isDefault: false
            }
        ],
        createdAt: new Date()
        })

Place an Order

Complete Order with Items and Customizations
        var customer = db.customers.findOne({ email: "john.doe@email.com" })
        var restaurant = db.restaurants.findOne({ name: "Pizza Paradise" })
        var margherita = db.menuItems.findOne({ name: "Margherita Pizza" })
        var breadsticks = db.menuItems.findOne({ name: "Garlic Breadsticks" })

        var orderNumber = "QBO-" + new Date().getTime()
        var defaultAddress = customer.addresses.find(addr => addr.isDefault)

        // Calculate order totals
        var subtotal = 14.99 + 4 + 2.50 + 5.99 + 0.75  // Pizza (Large) + Extra Cheese + Breadsticks + Sauce
        var deliveryFee = restaurant.deliveryFee
        var tax = subtotal * 0.0875  // 8.75% tax
        var total = subtotal + deliveryFee + tax

        db.orders.insertOne({
        orderNumber: orderNumber,
        customerId: customer._id,
        restaurantId: restaurant._id,
        items: [
            {
            menuItemId: margherita._id,
            name: "Margherita Pizza",
            quantity: 1,
            basePrice: 14.99,
            customizations: [
                { name: "Size", value: "Large (14\")", priceModifier: 4 }
            ],
            addOns: [
                { name: "Extra Cheese", price: 2.50 }
            ],
            itemTotal: 21.49  // 14.99 + 4 + 2.50
            },
            {
            menuItemId: breadsticks._id,
            name: "Garlic Breadsticks",
            quantity: 1,
            basePrice: 5.99,
            customizations: [],
            addOns: [
                { name: "Marinara Sauce", price: 0.75 }
            ],
            itemTotal: 6.74  // 5.99 + 0.75
            }
        ],
        subtotal: 28.23,
        deliveryFee: 3.99,
        tax: 2.47,
        total: 34.69,
        status: "placed",
        deliveryAddress: {
            street: defaultAddress.street,
            city: defaultAddress.city,
            state: defaultAddress.state,
            zipCode: defaultAddress.zipCode,
            location: defaultAddress.location
        },
        deliveryPartnerId: null,
        createdAt: new Date(),
        estimatedDeliveryTime: new Date(Date.now() + 45 * 60000), // 45 minutes
        deliveredAt: null
        })

        print("Order created: " + orderNumber)

Update Order Status

Order Lifecycle Updates
        // Restaurant accepts order
        db.orders.updateOne(
        { orderNumber: orderNumber },
        { $set: { status: "accepted" } }
        )

        // Order is being prepared
        db.orders.updateOne(
        { orderNumber: orderNumber },
        { $set: { status: "preparing" } }
        )

        // Order ready for pickup
        db.orders.updateOne(
        { orderNumber: orderNumber },
        { $set: { status: "ready_for_pickup" } }
        )

        // Order picked up by delivery partner
        db.orders.updateOne(
        { orderNumber: orderNumber },
        { 
            $set: { 
            status: "out_for_delivery",
            pickedUpAt: new Date()
            } 
        }
        )

        // Order delivered
        db.orders.updateOne(
        { orderNumber: orderNumber },
        { 
            $set: { 
            status: "delivered",
            deliveredAt: new Date()
            } 
        }
        )

Query Orders

        // Get customer's order history
        db.orders.find({ customerId: customer._id }).sort({ createdAt: -1 })

        // Get all orders for a restaurant
        db.orders.find({ restaurantId: restaurant._id }).sort({ createdAt: -1 })

        // Get active orders
        db.orders.find({
        status: { $in: ["placed", "accepted", "preparing", "out_for_delivery"] }
        })

        // Get orders from today
        var today = new Date()
        today.setHours(0, 0, 0, 0)

        db.orders.find({
        createdAt: { $gte: today }
        }).sort({ createdAt: -1 })

🏍️ Phase 4: Delivery Partner Management

Add delivery partners and assign orders.

Add Delivery Partners

        db.deliveryPartners.insertMany([
        {
            name: "Mike Rodriguez",
            phone: "+1-415-555-1001",
            vehicleType: "Motorcycle",
            currentLocation: {
            type: "Point",
            coordinates: [-122.4190, 37.7745]
            },
            isAvailable: true,
            currentOrderId: null,
            rating: {
            average: 4.8,
            count: 523
            }
        },
        {
            name: "Sarah Chen",
            phone: "+1-415-555-1002",
            vehicleType: "Bicycle",
            currentLocation: {
            type: "Point",
            coordinates: [-122.4210, 37.7755]
            },
            isAvailable: true,
            currentOrderId: null,
            rating: {
            average: 4.9,
            count: 687
            }
        },
        {
            name: "David Kumar",
            phone: "+1-415-555-1003",
            vehicleType: "Car",
            currentLocation: {
            type: "Point",
            coordinates: [-122.4175, 37.7735]
            },
            isAvailable: false,
            currentOrderId: ObjectId(),  // Currently on delivery
            rating: {
            average: 4.7,
            count: 412
            }
        }
        ])

Assign Delivery Partner to Order

        var order = db.orders.findOne({ orderNumber: orderNumber })
        var partner = db.deliveryPartners.findOne({ name: "Mike Rodriguez" })

        // Assign partner to order
        db.orders.updateOne(
        { _id: order._id },
        { $set: { deliveryPartnerId: partner._id } }
        )

        // Mark partner as busy
        db.deliveryPartners.updateOne(
        { _id: partner._id },
        { 
            $set: { 
            isAvailable: false,
            currentOrderId: order._id
            } 
        }
        )

Track Delivery Location

Record Location Updates
        // Delivery partner picks up order
        db.deliveryTracking.insertOne({
        orderId: order._id,
        deliveryPartnerId: partner._id,
        location: {
            type: "Point",
            coordinates: [-122.4194, 37.7749] // Restaurant location
        },
        timestamp: new Date(),
        status: "picked_up"
        })

        // En route update 1
        db.deliveryTracking.insertOne({
        orderId: order._id,
        deliveryPartnerId: partner._id,
        location: {
            type: "Point",
            coordinates: [-122.4196, 37.7748]
        },
        timestamp: new Date(),
        status: "en_route"
        })

        // En route update 2 (closer to destination)
        db.deliveryTracking.insertOne({
        orderId: order._id,
        deliveryPartnerId: partner._id,
        location: {
            type: "Point",
            coordinates: [-122.4195, 37.7748]
        },
        timestamp: new Date(),
        status: "en_route"
        })

        // Delivered
        db.deliveryTracking.insertOne({
        orderId: order._id,
        deliveryPartnerId: partner._id,
        location: {
            type: "Point",
            coordinates: [-122.4195, 37.7748] // Customer location
        },
        timestamp: new Date(),
        status: "delivered"
        })

        // Mark partner as available again
        db.deliveryPartners.updateOne(
        { _id: partner._id },
        { 
            $set: { 
            isAvailable: true,
            currentOrderId: null,
            currentLocation: {
                type: "Point",
                coordinates: [-122.4195, 37.7748]
            }
            } 
        }
        )

Get Delivery Route History

        // Get complete delivery route
        db.deliveryTracking.find({
        orderId: order._id
        }).sort({ timestamp: 1 })

πŸ—ΊοΈ Phase 5: Geospatial Queries

πŸ“‹ Phase 5 Objectives
βœ“ What You'll Learn:
  • Use $near to find closest restaurants
  • Query within radius using $geoWithin
  • Calculate distances with $geoNear
  • Convert meters to kilometers
🎯 Key Concepts:
  • $maxDistance in meters
  • Radian conversion for circles
  • Spherical vs planar calculations
  • 2dsphere index requirement
πŸ—ΊοΈ Visual Map of Spatial Queries

                 2km radius           5km radius
                    β”‚                    β”‚
                ────┼────            ────┼────
               β•±    β”‚    β•²          β•±    β”‚    β•²
              β”‚  πŸ• β”‚ 🍣  β”‚       β”‚  πŸ•  β”‚  🍣  β”‚
              β”‚   β•² β”‚ β•±   β”‚       β”‚    β•² β”‚ β•±    β”‚
              β”‚    πŸ πŸ“   β”‚       β”‚     πŸ πŸ“    β”‚
              β”‚   β•± β”‚ β•²   β”‚       β”‚    β•± β”‚ β•²    β”‚
              β”‚  πŸ” β”‚ πŸ›  β”‚       β”‚  πŸ”  β”‚  πŸ›  β”‚
               β•²    β”‚    β•±          β•²    β”‚    β•±
                ────┼────            ────┼────

         $geoWithin (all within)    $near (sorted by distance)
         Returns: πŸ• 🍣 πŸ” πŸ›       Returns: πŸ• β†’ 🍣 β†’ πŸ” β†’ πŸ›
         (unordered)                 (closest first)

πŸ“ Legend:
β€’ 🏠 = Customer location (John's home)
β€’ πŸ• = Pizza Paradise (0.5 km away)
β€’ 🍣 = Sushi Express (1.2 km away)
β€’ πŸ” = Burger Bliss (1.8 km away)
β€’ πŸ› = Tandoori Nights (2.3 km away)

Find Restaurants Near Customer

$near Query - Find Closest Restaurants 🟑 INTERMEDIATE
        // Customer location (John's home)
        var customerLocation = {
        type: "Point",
        coordinates: [-122.4195, 37.7748]
        }

        // Find restaurants within 5km, sorted by distance
        db.restaurants.find({
        location: {
            $near: {
            $geometry: customerLocation,
            $maxDistance: 5000  // 5000 meters = 5 km
            }
        },
        isActive: true
        })
$geoWithin - Find All Restaurants in Circle
        // Find all restaurants within 2km radius
        db.restaurants.find({
        location: {
            $geoWithin: {
            $centerSphere: [
                [-122.4195, 37.7748],  // [longitude, latitude]
                2 / 6378.1  // radius in radians (2km / Earth radius)
            ]
            }
        },
        isActive: true
        })

Find Nearby Restaurants with Aggregation

$geoNear Aggregation - Distance Calculation πŸ”΄ ADVANCED
        // Find restaurants with distance in meters
        db.restaurants.aggregate([
        {
            $geoNear: {
            near: customerLocation,
            distanceField: "distance",
            maxDistance: 5000,
            query: { isActive: true },
            spherical: true
            }
        },
        {
            $project: {
            name: 1,
            cuisineType: 1,
            rating: 1,
            deliveryFee: 1,
            distance: 1,
            distanceKm: { $round: [{ $divide: ["$distance", 1000] }, 2] }
            }
        }
        ])

Find Restaurants by Cuisine Near Location

        // Find Italian restaurants within 3km
        db.restaurants.aggregate([
        {
            $geoNear: {
            near: customerLocation,
            distanceField: "distance",
            maxDistance: 3000,
            query: { 
                isActive: true,
                cuisineType: "Italian"
            },
            spherical: true
            }
        },
        {
            $project: {
            name: 1,
            rating: 1,
            deliveryFee: 1,
            distanceKm: { $round: [{ $divide: ["$distance", 1000] }, 2] }
            }
        },
        { $sort: { "rating.average": -1 } }
        ])

Find Available Delivery Partners Nearby

Find Closest Available Delivery Partner
        // Restaurant needs a delivery partner
        var restaurantLocation = {
        type: "Point",
        coordinates: [-122.4194, 37.7749]
        }

        // Find nearest available delivery partner within 2km
        db.deliveryPartners.find({
        currentLocation: {
            $near: {
            $geometry: restaurantLocation,
            $maxDistance: 2000
            }
        },
        isAvailable: true
        }).limit(1)
πŸ’‘ Geospatial Query Tips:
  • $near: Returns results sorted by distance (closest first)
  • $geoWithin: Returns all results within boundary (no sorting)
  • $geoNear: Aggregation stage that adds distance field
  • Coordinates: Always [longitude, latitude] order in GeoJSON!
  • Distance: $maxDistance is in meters for GeoJSON

πŸ“Š Phase 6: Analytics & Reporting

Extract business insights from your food delivery data.

Restaurant Performance

Top Performing Restaurants
        db.orders.aggregate([
        { $match: { status: "delivered" } },
        {
            $group: {
            _id: "$restaurantId",
            totalOrders: { $sum: 1 },
            totalRevenue: { $sum: "$subtotal" },
            avgOrderValue: { $avg: "$subtotal" }
            }
        },
        {
            $lookup: {
            from: "restaurants",
            localField: "_id",
            foreignField: "_id",
            as: "restaurant"
            }
        },
        { $unwind: "$restaurant" },
        {
            $project: {
            _id: 0,
            restaurantName: "$restaurant.name",
            totalOrders: 1,
            totalRevenue: { $round: ["$totalRevenue", 2] },
            avgOrderValue: { $round: ["$avgOrderValue", 2] }
            }
        },
        { $sort: { totalRevenue: -1 } }
        ])

Peak Hours Analysis

        db.orders.aggregate([
        {
            $group: {
            _id: { $hour: "$createdAt" },
            orderCount: { $sum: 1 },
            avgOrderValue: { $avg: "$total" }
            }
        },
        {
            $project: {
            _id: 0,
            hour: "$_id",
            orderCount: 1,
            avgOrderValue: { $round: ["$avgOrderValue", 2] }
            }
        },
        { $sort: { hour: 1 } }
        ])

Popular Menu Items

        db.orders.aggregate([
        { $match: { status: "delivered" } },
        { $unwind: "$items" },
        {
            $group: {
            _id: {
                menuItemId: "$items.menuItemId",
                name: "$items.name"
            },
            timesSold: { $sum: "$items.quantity" },
            revenue: { $sum: "$items.itemTotal" }
            }
        },
        {
            $project: {
            _id: 0,
            itemName: "$_id.name",
            timesSold: 1,
            revenue: { $round: ["$revenue", 2] }
            }
        },
        { $sort: { timesSold: -1 } },
        { $limit: 10 }
        ])

Delivery Partner Performance

        db.orders.aggregate([
        {
            $match: { 
            status: "delivered",
            deliveryPartnerId: { $ne: null }
            }
        },
        {
            $group: {
            _id: "$deliveryPartnerId",
            totalDeliveries: { $sum: 1 },
            avgDeliveryTime: {
                $avg: {
                $divide: [
                    { $subtract: ["$deliveredAt", "$pickedUpAt"] },
                    60000  // Convert to minutes
                ]
                }
            }
            }
        },
        {
            $lookup: {
            from: "deliveryPartners",
            localField: "_id",
            foreignField: "_id",
            as: "partner"
            }
        },
        { $unwind: "$partner" },
        {
            $project: {
            _id: 0,
            partnerName: "$partner.name",
            totalDeliveries: 1,
            avgDeliveryTime: { $round: ["$avgDeliveryTime", 1] },
            rating: "$partner.rating.average"
            }
        },
        { $sort: { totalDeliveries: -1 } }
        ])

Revenue by Day

        db.orders.aggregate([
        { $match: { status: "delivered" } },
        {
            $group: {
            _id: {
                year: { $year: "$createdAt" },
                month: { $month: "$createdAt" },
                day: { $dayOfMonth: "$createdAt" }
            },
            dailyRevenue: { $sum: "$total" },
            orderCount: { $sum: 1 }
            }
        },
        {
            $project: {
            _id: 0,
            date: {
                $concat: [
                { $toString: "$_id.year" },
                "-",
                { $toString: "$_id.month" },
                "-",
                { $toString: "$_id.day" }
                ]
            },
            revenue: { $round: ["$dailyRevenue", 2] },
            orders: "$orderCount"
            }
        },
        { $sort: { date: -1 } }
        ])

Customer Insights

Top Customers by Order Frequency
        db.orders.aggregate([
        {
            $group: {
            _id: "$customerId",
            totalOrders: { $sum: 1 },
            totalSpent: { $sum: "$total" },
            avgOrderValue: { $avg: "$total" }
            }
        },
        {
            $lookup: {
            from: "customers",
            localField: "_id",
            foreignField: "_id",
            as: "customer"
            }
        },
        { $unwind: "$customer" },
        {
            $project: {
            _id: 0,
            customerName: "$customer.name",
            email: "$customer.email",
            totalOrders: 1,
            totalSpent: { $round: ["$totalSpent", 2] },
            avgOrderValue: { $round: ["$avgOrderValue", 2] }
            }
        },
        { $sort: { totalOrders: -1 } },
        { $limit: 10 }
        ])

⚑ Performance Optimization

πŸ—ΊοΈ Geospatial Indexes

2dsphere indexes are critical for $near and $geoWithin queries to perform efficiently

πŸ“‡ Compound Indexes

Create indexes like {restaurantId: 1, isAvailable: 1} for filtered menu queries

🎯 Query Optimization

Always filter by isActive, status before doing expensive operations

πŸ’Ύ Embedded vs Referenced

Order items are embedded (snapshot), but restaurant is referenced

Optimize Geospatial Queries

        // Check if geospatial index is being used
        db.restaurants.find({
        location: {
            $near: {
            $geometry: { type: "Point", coordinates: [-122.4195, 37.7748] },
            $maxDistance: 5000
            }
        },
        isActive: true
        }).explain("executionStats")

        // Look for "stage": "GEO_NEAR_2DSPHERE" in the winning plan
⚠️ Performance Best Practices:
  • Always have 2dsphere index before running geospatial queries
  • Limit $maxDistance to reasonable values (5-10km for food delivery)
  • Add { isActive: true } filter to avoid querying closed restaurants
  • Use projection to limit fields returned in large collections
  • Consider caching popular restaurant lists by location

πŸ† Advanced Challenges

Challenge 1: Surge Pricing

Implement dynamic delivery fees based on demand (number of active orders in area)

Challenge 2: ETA Calculator

Calculate accurate delivery time based on distance, traffic, and preparation time

Challenge 3: Zone Management

Create delivery zones using $geoWithin with polygons for different areas

Challenge 4: Smart Matching

Assign orders to delivery partners based on location, rating, and current load

Challenge 5: Loyalty Program

Track customer points, tier levels, and automatic discount application

Challenge 6: Heat Map

Generate delivery density heatmap by aggregating orders by geographic regions

πŸ’‘ Challenge Hints:
  • Surge Pricing: Use aggregation to count orders in last hour within radius, apply multiplier to base fee
  • ETA: Create function that queries $geoNear distance, adds restaurant prep time (15-30 min), adds travel time (distance/speed)
  • Zones: Use GeoJSON Polygon type with $geoWithin to check if address is in delivery zone
  • Smart Matching: Aggregate available partners, calculate score based on distance + rating + active orders
  • Loyalty: Add points field to customers, create tiers collection, use $lookup to apply tier discounts
  • Heat Map: Use $geoNear with $bucket to group orders by distance ranges from city center