Crud Operations

Read Operations

Find and query documents in MongoDB

Find

Retrieve multiple documents matching a filter.

local documents = collection:Find(filter, limit)

Parameters:

  • filter (table): Query filter
  • limit (number, optional): Maximum documents to return

Returns:

  • table: Array of matching documents

Basic Examples

local players = db:Collection("players")

-- Find all players
local all = players:Find({})

-- Find with filter
local highLevel = players:Find({
    level = { ["$gte"] = 10 }
})

-- Find with limit
local top10 = players:Find({}, 10)

-- Complex filter
local vips = players:Find({
    rank = { ["$in"] = { "VIP", "Premium", "Ultimate" } },
    active = true,
    banned = { ["$ne"] = true }
}, 50)

Real-World Example

-- Get active high-level players
local function GetVeteranPlayers()
    local veterans = players:Find({
        level = { ["$gte"] = 50 },
        playtime = { ["$gte"] = 100 },  -- 100+ hours
        banned = { ["$ne"] = true },
        last_seen = { ["$gte"] = os.time() - (7 * 24 * 60 * 60) }  -- Last 7 days
    }, 20)
    
    return veterans
end

-- Display veteran players
local vets = GetVeteranPlayers()
print("Veteran Players (" .. #vets .. "):")
for i, player in ipairs(vets) do
    print(string.format("%d. %s - Level %d", i, player.username, player.level))
end

Pagination

-- Show page 1
local page1 = players:Find({ active = true }, 20)

-- For page 2+, use aggregation with $skip
local page2 = players:Aggregate({
    { ["$match"] = { active = true } },
    { ["$skip"] = 20 },
    { ["$limit"] = 20 }
})

FindOne

Retrieve a single document - more efficient than Find when you only need one result.

local document = collection:FindOne(filter)

Parameters:

  • filter (table): Query filter

Returns:

  • table: The first matching document, or nil if not found

Basic Examples

-- Find by SteamID
local player = players:FindOne({
    steamid = "STEAM_0:1:12345"
})

if player then
    print("Found:", player.username)
    print("Level:", player.level)
else
    print("Player not found")
end

-- Find by multiple criteria
local admin = players:FindOne({
    rank = "Admin",
    active = true
})

Real-World Example

-- Load player data when they join
hook.Add("PlayerInitialSpawn", "LoadPlayerData", function(ply)
    local steamid = ply:SteamID()
    
    local data = players:FindOne({ steamid = steamid })
    
    if data then
        -- Existing player
        ply.PlayerData = data
        ply:ChatPrint("Welcome back, " .. data.username .. "!")
        ply:ChatPrint("Level: " .. data.level .. " | Credits: " .. data.credits)
        
        -- Update last seen
        players:UpdateOne(
            { steamid = steamid },
            { ["$set"] = { last_seen = os.time() } }
        )
    else
        -- New player
        print("New player detected:", ply:Nick())
    end
end)

Check if Exists

-- Check if item exists before purchase
local function CanPurchaseItem(itemId)
    local item = shopItems:FindOne({ id = itemId })
    
    if not item then
        return false, "Item not found"
    end
    
    if item.stock ~= -1 and item.stock <= 0 then
        return false, "Out of stock"
    end
    
    return true, item
end

Count

Count documents matching a filter - faster than fetching all documents.

local count = collection:Count(filter)

Parameters:

  • filter (table): Query filter

Returns:

  • number: Count of matching documents

Examples

-- Count all players
local total = players:Count({})
print("Total players:", total)

-- Count active players
local active = players:Count({ active = true })
print("Active players:", active)

-- Count VIPs
local vipCount = players:Count({ rank = "VIP" })
print("VIP players:", vipCount)

-- Count high-level players
local veterans = players:Count({
    level = { ["$gte"] = 50 }
})
print("Level 50+ players:", veterans)

-- Count players from last week
local weekAgo = os.time() - (7 * 24 * 60 * 60)
local recentPlayers = players:Count({
    last_seen = { ["$gte"] = weekAgo }
})
print("Players this week:", recentPlayers)

Statistics Dashboard

-- Create admin command to show server statistics
concommand.Add("sv_stats", function(ply)
    if not ply:IsAdmin() then return end
    
    local total = players:Count({})
    local active = players:Count({
        last_seen = { ["$gte"] = os.time() - (7 * 24 * 60 * 60) }
    })
    local banned = players:Count({ banned = true })
    local vips = players:Count({ rank = "VIP" })
    
    ply:ChatPrint("=== SERVER STATISTICS ===")
    ply:ChatPrint("Total Players: " .. total)
    ply:ChatPrint("Active (7 days): " .. active)
    ply:ChatPrint("VIP Members: " .. vips)
    ply:ChatPrint("Banned: " .. banned)
end)

Query Operators

Use MongoDB's powerful query operators to filter documents:

Comparison Operators

-- Equal (implicit)
{ field = value }

-- Not equal
{ field = { ["$ne"] = value } }

-- Greater than
{ field = { ["$gt"] = value } }

-- Greater than or equal
{ field = { ["$gte"] = value } }

-- Less than
{ field = { ["$lt"] = value } }

-- Less than or equal
{ field = { ["$lte"] = value } }

-- In array
{ field = { ["$in"] = { value1, value2, value3 } } }

-- Not in array
{ field = { ["$nin"] = { value1, value2 } } }

Logical Operators

-- AND (implicit - all conditions must match)
{
    level = { ["$gte"] = 10 },
    active = true,
    banned = { ["$ne"] = true }
}

-- OR (any condition must match)
{
    ["$or"] = {
        { rank = "VIP" },
        { rank = "Premium" },
        { level = { ["$gte"] = 50 } }
    }
}

-- Combined AND + OR
{
    active = true,  -- Must be active
    ["$or"] = {     -- AND (VIP OR high level)
        { rank = "VIP" },
        { level = { ["$gte"] = 50 } }
    }
}

Element Operators

-- Field exists
{ email = { ["$exists"] = true } }

-- Field doesn't exist
{ temp_data = { ["$exists"] = false } }

-- Type check
{ age = { ["$type"] = "number" } }

String Operators

-- Regex pattern
{ username = { ["$regex"] = "^Player" } }  -- Starts with "Player"

-- Case-insensitive search
{ username = { ["$regex"] = "admin", ["$options"] = "i" } }

-- Contains
{ bio = { ["$regex"] = "veteran" } }

Real-World Query Examples

Find Players by Rank

local function GetPlayersByRank(rank)
    return players:Find({ rank = rank })
end

-- Usage
local admins = GetPlayersByRank("Admin")
local vips = GetPlayersByRank("VIP")

Search Players

-- Search players by username (case-insensitive)
local function SearchPlayers(query)
    return players:Find({
        username = { ["$regex"] = query, ["$options"] = "i" }
    }, 10)
end

-- Usage
concommand.Add("findplayer", function(ply, cmd, args)
    local query = args[1]
    if not query then
        ply:ChatPrint("Usage: findplayer <name>")
        return
    end
    
    local results = SearchPlayers(query)
    ply:ChatPrint("Found " .. #results .. " players:")
    for i, p in ipairs(results) do
        ply:ChatPrint(i .. ". " .. p.username .. " (Level " .. p.level .. ")")
    end
end)

Get Recent Transactions

local function GetRecentTransactions(steamid, limit)
    limit = limit or 10
    
    return transactions:Aggregate({
        {
            ["$match"] = { steamid = steamid }
        },
        {
            ["$sort"] = { created_at = -1 }
        },
        {
            ["$limit"] = limit
        }
    })
end

Find Inactive Players

-- Find players who haven't logged in for 30 days
local function GetInactivePlayers()
    local thirtyDaysAgo = os.time() - (30 * 24 * 60 * 60)
    
    return players:Find({
        last_seen = { ["$lt"] = thirtyDaysAgo },
        banned = { ["$ne"] = true }
    })
end

Performance Tips

  1. Always use limits - Don't fetch thousands of documents
  2. Create indexes on frequently queried fields
  3. Use FindOne when you only need one document
  4. Use Count instead of Find + #table
  5. Specific filters are faster than broad ones
  6. Project only needed fields (use aggregation $project)

Bad Performance

-- DON'T: Fetch all and filter in Lua
local all = players:Find({})  -- Fetches EVERYTHING
local filtered = {}
for _, p in ipairs(all) do
    if p.level >= 10 then
        table.insert(filtered, p)
    end
end

Good Performance

-- DO: Filter in database
local filtered = players:Find({
    level = { ["$gte"] = 10 }
}, 100)  -- With limit

Next Steps

Update Operations

Learn how to modify documents

Query Filters

Advanced query operators