Crud Operations

Update Operations

Modify documents in MongoDB

UpdateOne

Update a single document matching a filter.

local count = collection:UpdateOne(filter, update, upsert)

Parameters:

  • filter (table): Query filter
  • update (table): Update operations
  • upsert (boolean, optional): Create document if not found (default: false)

Returns:

  • number: Number of documents modified (0 or 1)

Basic Examples

local players = db:Collection("players")

-- Simple update
local updated = players:UpdateOne(
    { steamid = "STEAM_0:1:12345" },
    { ["$set"] = { last_seen = os.time() } }
)

if updated > 0 then
    print("Player updated")
else
    print("Player not found")
end

-- Update multiple fields
players:UpdateOne(
    { steamid = "STEAM_0:1:12345" },
    {
        ["$set"] = {
            last_seen = os.time(),
            username = "NewName",
            rank = "VIP"
        }
    }
)

-- Increment a value
players:UpdateOne(
    { steamid = "STEAM_0:1:12345" },
    { ["$inc"] = { credits = 500 } }
)

Upsert (Create or Update)

-- Create document if it doesn't exist, update if it does
players:UpdateOne(
    { steamid = "STEAM_0:1:12345" },
    {
        ["$set"] = {
            username = "Player1",
            last_seen = os.time()
        },
        ["$setOnInsert"] = {
            created_at = os.time(),
            level = 1,
            credits = 1000
        }
    },
    true  -- upsert = true
)

Real-World Example

-- Update player when they disconnect
hook.Add("PlayerDisconnect", "SavePlayerOnLeave", function(ply)
    local steamid = ply:SteamID()
    
    players:UpdateOne(
        { steamid = steamid },
        {
            ["$set"] = {
                last_seen = os.time(),
                username = ply:Nick()
            },
            ["$inc"] = {
                total_sessions = 1
            }
        }
    )
end)

UpdateMany

Update all documents matching a filter - useful for bulk updates.

local count = collection:UpdateMany(filter, update, upsert)

Parameters:

  • filter (table): Query filter
  • update (table): Update operations
  • upsert (boolean, optional): Create document if none match (default: false)

Returns:

  • number: Number of documents modified

Basic Examples

-- Update all VIP players
local updated = players:UpdateMany(
    { rank = "VIP" },
    { ["$inc"] = { credits = 1000 } }
)
print("Gave bonus to", updated, "VIP players")

-- Mark old players as inactive
local thirtyDaysAgo = os.time() - (30 * 24 * 60 * 60)
local updated = players:UpdateMany(
    { last_seen = { ["$lt"] = thirtyDaysAgo } },
    { ["$set"] = { active = false } }
)
print("Marked", updated, "players as inactive")

-- Reset daily quests
players:UpdateMany(
    {},  -- Empty filter = all documents
    {
        ["$set"] = {
            daily_quest_completed = false,
            daily_quest_date = os.date("%Y-%m-%d")
        }
    }
)

Real-World Examples

-- Daily reset timer
timer.Create("DailyReset", 24 * 60 * 60, 0, function()
    -- Reset daily bonuses
    local updated = players:UpdateMany(
        {},
        {
            ["$set"] = {
                daily_bonus_claimed = false,
                daily_login_bonus = false
            }
        }
    )
    
    print("Daily reset complete -", updated, "players")
end)

-- Seasonal event
local function StartSummerEvent()
    local updated = players:UpdateMany(
        { level = { ["$gte"] = 10 } },
        {
            ["$inc"] = { event_tokens = 50 },
            ["$set"] = { event_participant = true }
        }
    )
    
    PrintMessage(HUD_PRINTTALK, "Summer event started! " .. updated .. " players received tokens!")
end

-- Price adjustment
local function UpdateItemPrices(multiplier)
    local updated = shopItems:UpdateMany(
        { category = "weapons" },
        {
            ["$mul"] = { price = multiplier }
        }
    )
    
    print("Updated", updated, "weapon prices")
end

Update Operators

MongoDB provides powerful operators to modify documents:

Field Operators

-- Set field value
{ ["$set"] = { field = value } }

-- Remove field
{ ["$unset"] = { field = "" } }

-- Rename field
{ ["$rename"] = { old_field = "new_field" } }

-- Set only on insert (with upsert)
{ ["$setOnInsert"] = { field = value } }

-- Set to current timestamp
{ ["$currentDate"] = { field = true } }

Numeric Operators

-- Increment by amount (can be negative)
{ ["$inc"] = { credits = 100 } }
{ ["$inc"] = { credits = -50 } }

-- Multiply
{ ["$mul"] = { price = 1.5 } }

-- Set to minimum value (only if less than current)
{ ["$min"] = { health = 50 } }

-- Set to maximum value (only if greater than current)
{ ["$max"] = { high_score = 1000 } }

Array Operators

-- Add to array
{ ["$push"] = { items = "new_item" } }

-- Add multiple items
{ ["$push"] = { items = { ["$each"] = { "item1", "item2" } } } }

-- Add if doesn't exist (unique)
{ ["$addToSet"] = { tags = "veteran" } }

-- Remove from array
{ ["$pull"] = { items = "unwanted_item" } }

-- Remove first item (-1) or last item (1)
{ ["$pop"] = { items = 1 } }  -- Remove last
{ ["$pop"] = { items = -1 } }  -- Remove first

Practical Examples

Player Credits System

-- Add credits
local function AddCredits(ply, amount)
    local updated = players:UpdateOne(
        { steamid = ply:SteamID() },
        { ["$inc"] = { credits = amount } }
    )
    
    if updated > 0 then
        ply:ChatPrint("You received " .. amount .. " credits!")
        return true
    end
    return false
end

-- Remove credits (check if enough first)
local function RemoveCredits(ply, amount)
    local updated = players:UpdateOne(
        {
            steamid = ply:SteamID(),
            credits = { ["$gte"] = amount }  -- Must have enough
        },
        { ["$inc"] = { credits = -amount } }
    )
    
    if updated > 0 then
        return true
    else
        ply:ChatPrint("Not enough credits!")
        return false
    end
end

Achievement System

-- Award achievement
local function AwardAchievement(ply, achievementId)
    local updated = players:UpdateOne(
        { steamid = ply:SteamID() },
        {
            ["$addToSet"] = {
                achievements = achievementId
            },
            ["$inc"] = {
                achievement_points = 10
            }
        }
    )
    
    if updated > 0 then
        ply:ChatPrint("Achievement unlocked: " .. achievementId)
        -- Give reward
        AddCredits(ply, 100)
    end
end

Inventory Management

-- Add item to inventory
local function AddItemToInventory(steamid, itemId, quantity)
    -- Check if item already exists
    local inv = inventories:FindOne({
        steamid = steamid,
        ["items.item_id"] = itemId
    })
    
    if inv then
        -- Item exists - increment quantity
        inventories:UpdateOne(
            {
                steamid = steamid,
                ["items.item_id"] = itemId
            },
            {
                ["$inc"] = { ["items.$.quantity"] = quantity }
            }
        )
    else
        -- New item - add to array
        inventories:UpdateOne(
            { steamid = steamid },
            {
                ["$push"] = {
                    items = {
                        item_id = itemId,
                        quantity = quantity,
                        added_at = os.time()
                    }
                }
            }
        )
    end
end

Level Up System

-- Level up player
local function LevelUp(ply)
    local updated = players:UpdateOne(
        { steamid = ply:SteamID() },
        {
            ["$inc"] = {
                level = 1,
                skill_points = 5
            },
            ["$push"] = {
                level_history = {
                    level = ply.Level + 1,
                    timestamp = os.time()
                }
            }
        }
    )
    
    if updated > 0 then
        ply:ChatPrint("Level Up! You are now level " .. (ply.Level + 1))
        ply:ChatPrint("You received 5 skill points!")
    end
end

Conditional Updates

Update only if conditions are met:

-- Purchase item only if player has enough money
local function BuyItem(ply, itemId, price)
    local updated = players:UpdateOne(
        {
            steamid = ply:SteamID(),
            credits = { ["$gte"] = price }  -- Condition
        },
        {
            ["$inc"] = { credits = -price },
            ["$push"] = { purchases = itemId }
        }
    )
    
    if updated > 0 then
        ply:ChatPrint("Purchase successful!")
        return true
    else
        ply:ChatPrint("Not enough credits!")
        return false
    end
end

Best Practices

  1. Use atomic operators ($inc, $push) instead of read-modify-write
  2. Check return value to know if update succeeded
  3. Use upsert for "create or update" scenarios
  4. Use UpdateMany for bulk updates instead of loops
  5. Be specific with filters to avoid unintended updates
  6. Validate conditions in the filter (e.g., credits >= price)

❌ Bad: Read-Modify-Write

-- DON'T: Race condition possible
local player = players:FindOne({ steamid = steamid })
player.credits = player.credits + 100
players:UpdateOne({ steamid = steamid }, { ["$set"] = player })

✅ Good: Atomic Update

-- DO: Atomic operation
players:UpdateOne(
    { steamid = steamid },
    { ["$inc"] = { credits = 100 } }
)

Next Steps

Delete Operations

Learn how to remove documents

Common Patterns

Best practices and patterns