Utilities

Migration Guide

Migrate from gmsv_mongo V1 to V2

Why Upgrade?

V2 brings significant improvements:

Better Performance

Improved connection pooling and async runtime

New Features

Bulk operations, aggregation, indexes

Better Errors

Detailed error messages and logging

Cleaner API

More explicit method names

Breaking Changes

1. Method Naming

Methods have been renamed for clarity:

V1 MethodV2 MethodNotes
Collection:Insert()Collection:InsertOne()More explicit
Collection:Update()Collection:UpdateOne()More explicit
Collection:Delete()Collection:DeleteOne()More explicit
Collection:Find()Collection:Find()Same

V1 method names still work but are deprecated. Update your code to V2 methods for future compatibility.

2. Return Values

Some methods return different values in V2:

InsertOne:

-- V1: Returns boolean
local success = collection:Insert(document)

-- V2: Returns document ID
local id = collection:InsertOne(document)
print("Inserted with ID:", id)

UpdateOne/UpdateMany:

-- V1: Returns boolean
local success = collection:Update(filter, update)

-- V2: Returns count of modified documents
local count = collection:UpdateOne(filter, update)
print("Updated", count, "documents")

DeleteOne/DeleteMany:

-- V1: Returns boolean
local success = collection:Delete(filter)

-- V2: Returns count of deleted documents
local count = collection:DeleteOne(filter)
print("Deleted", count, "documents")

3. Configuration Changes

V1:

local client = MongoDB.Client("mongodb://localhost:27017")

V2 (backward compatible):

-- Simple connection (same as V1)
local client = MongoDB.Client("mongodb://localhost:27017")

-- Or with options (new in V2)
local client = MongoDB.ClientWithOptions("mongodb://localhost:27017", {
    app_name = "MyGModServer",
    max_pool_size = 50,
    retry_writes = true
})

Migration Steps

Step 1: Update the Binary

  1. Download the V2 binary from GitHub Releases
  2. Replace the old binary in garrysmod/lua/bin/
  3. Restart your server

Step 2: Update Method Calls

Search your codebase for these patterns:

Find and Replace:

-- Find: collection:Insert(
-- Replace: collection:InsertOne(

-- Find: collection:Update(
-- Replace: collection:UpdateOne(

-- Find: collection:Delete(
-- Replace: collection:DeleteOne(

Step 3: Update Return Value Handling

Before (V1):

local success = players:Insert({ name = "Player1" })
if success then
    print("Insert successful")
end

After (V2):

local id = players:InsertOne({ name = "Player1" })
if id then
    print("Insert successful, ID:", id)
end

Step 4: Test in Development

Before deploying to production:

  1. Test all database operations in a development environment
  2. Verify data integrity
  3. Check error handling
  4. Test performance

Step 5: Deploy to Production

  1. Backup your MongoDB database
  2. Update the binary
  3. Update your Lua code
  4. Monitor for errors
  5. Have a rollback plan ready

Migration Examples

Example 1: Basic CRUD

V1 Code:

local players = db:Collection("players")

-- Insert
local ok = players:Insert({ name = "John", score = 100 })

-- Find
local results = players:Find({ score = { ["$gt"] = 50 } })

-- Update
players:Update({ name = "John" }, { ["$set"] = { score = 150 } })

-- Delete
players:Delete({ name = "John" })

V2 Code:

local players = db:Collection("players")

-- Insert (returns ID)
local id = players:InsertOne({ name = "John", score = 100 })
print("Inserted with ID:", id)

-- Find (same, but can add limit)
local results = players:Find({ score = { ["$gt"] = 50 } }, 10)

-- Update (returns count)
local count = players:UpdateOne({ name = "John" }, { ["$set"] = { score = 150 } })
print("Updated", count, "documents")

-- Delete (returns count)
local count = players:DeleteOne({ name = "John" })
print("Deleted", count, "documents")

Example 2: Player Data System

V1 Code:

function SavePlayer(ply)
    local steamid = ply:SteamID()
    
    local exists = players:Find({ steamid = steamid })
    
    if #exists > 0 then
        -- Update existing
        players:Update(
            { steamid = steamid },
            { ["$set"] = { last_seen = os.time() } }
        )
    else
        -- Insert new
        players:Insert({
            steamid = steamid,
            name = ply:Nick(),
            created_at = os.time()
        })
    end
end

V2 Code (with upsert):

function SavePlayer(ply)
    local steamid = ply:SteamID()
    
    -- Simpler: use upsert
    players:UpdateOne(
        { steamid = steamid },
        {
            ["$set"] = { 
                name = ply:Nick(),
                last_seen = os.time() 
            },
            ["$setOnInsert"] = { 
                created_at = os.time(),
                level = 1
            }
        },
        true  -- upsert: create if doesn't exist
    )
end

Example 3: Bulk Operations

V1 Code:

-- Insert multiple items (inefficient)
for _, item in ipairs(itemsToInsert) do
    items:Insert(item)
end

-- Update multiple players (inefficient)
local activePlayers = players:Find({ active = true })
for _, player in ipairs(activePlayers) do
    players:Update(
        { _id = player._id },
        { ["$inc"] = { credits = 100 } }
    )
end

V2 Code (much faster):

-- Bulk insert
local ids = items:InsertMany(itemsToInsert)
print("Inserted", #ids, "items")

-- Bulk update
local count = players:UpdateMany(
    { active = true },
    { ["$inc"] = { credits = 100 } }
)
print("Updated", count, "players")

New Features in V2

Take advantage of these new features:

1. Bulk Operations

-- InsertMany
local ids = players:InsertMany({
    { name = "Alice", level = 10 },
    { name = "Bob", level = 15 },
    { name = "Charlie", level = 20 }
})

-- UpdateMany
local count = players:UpdateMany(
    { level = { ["$lt"] = 10 } },
    { ["$inc"] = { level = 1 } }
)

-- DeleteMany
local count = logs:DeleteMany({
    created_at = { ["$lt"] = os.time() - (30 * 24 * 60 * 60) }
})

2. Aggregation Pipelines

-- Get statistics
local stats = players:Aggregate({
    {
        ["$group"] = {
            _id = "$rank",
            count = { ["$sum"] = 1 },
            avgLevel = { ["$avg"] = "$level" }
        }
    },
    {
        ["$sort"] = { count = -1 }
    }
})

3. Index Management

-- Create indexes for better performance
players:CreateIndex({ steamid = 1 }, true, "steamid_unique")
players:CreateIndex({ level = -1, score = -1 }, false, "level_score")

-- List indexes
local indexes = players:ListIndexes()

-- Drop index
players:DropIndex("old_index")

4. Count Documents

-- Count matching documents
local total = players:Count({})
local active = players:Count({ active = true })
local vips = players:Count({ rank = "VIP" })

5. FindOne

-- Find single document (more efficient than Find with limit)
local player = players:FindOne({ steamid = "STEAM_0:1:12345" })

6. Database Management

-- List collections
local collections = db:ListCollections()

-- Get collection stats
local stats = db:Stats("players")

-- Drop database (careful!)
db:Drop()

Compatibility Mode

If you need time to migrate, you can use V1 compatibility mode:

MongoDB.UseV2(false)  -- Switch to V1 mode
-- Your old V1 code here
MongoDB.UseV2(true)   -- Switch back to V2 mode

Compatibility mode is for transition only. Don't rely on it long-term. Plan to fully migrate to V2.

Performance Improvements

V2 offers significant performance improvements:

OperationV1 PerformanceV2 PerformanceImprovement
Insert 1000 docs (loop)~2000ms~2000msSame
Insert 1000 docs (bulk)N/A~200ms10x faster
Update 100 docs (loop)~500ms~500msSame
Update 100 docs (bulk)N/A~50ms10x faster
Query with index~50ms~20ms2.5x faster
AggregationN/A~30msNew feature

Troubleshooting

Issue: "Method not found"

Problem: Using V2 methods but module is in V1 compatibility mode.

Solution:

MongoDB.UseV2(true)  -- Enable V2 mode

Issue: Code expects boolean, gets string/number

Problem: V2 return values changed.

Solution: Update your code to handle new return types:

-- V1 code
if players:Insert(doc) then
    print("Success")
end

-- V2 code
local id = players:InsertOne(doc)
if id then
    print("Success, ID:", id)
end

Issue: Performance degradation

Problem: Not using V2 features optimally.

Solution:

  1. Use bulk operations (InsertMany, UpdateMany)
  2. Create indexes on frequently queried fields
  3. Use FindOne instead of Find for single documents
  4. Add limits to Find queries

Checklist

Use this checklist to track your migration:

  • Downloaded V2 binary
  • Updated binary in garrysmod/lua/bin/
  • Updated method names (InsertInsertOne, etc.)
  • Updated return value handling
  • Tested in development environment
  • Created indexes for frequently queried fields
  • Converted loops to bulk operations where possible
  • Updated error handling
  • Tested edge cases
  • Backup production database
  • Deployed to production
  • Monitored for errors

Getting Help

If you encounter issues during migration:

  1. Check the API Reference
  2. Review Examples
  3. Check GitHub Issues
  4. Ask in the community