Patterns · 4 of 4

Upsert & soft-delete

Two idempotent patterns you'll reach for again and again.

Upsert by key

proc upsert_user(email, name) {
    let existing = find_one("users", {email: email})
    if existing == null {
        insert("users", {email: email, name: name, signups: 1})
        return {created: true, email: email}
    }
    update("users", {email: email}, {
        $set: {name: name},
        $inc: {signups: 1}
    })
    return {created: false, signups: existing.signups + 1}
}

Upsert with merge

proc upsert_setting(user_id, key, value) {
    let s = find_one("settings", {user_id: user_id})
    if s == null {
        insert("settings", {user_id: user_id, prefs: {key: value}})
    } else {
        update("settings", {user_id: user_id}, {$set: {"prefs.key": value}})
    }
    return {ok: true}
}

Counter upsert

proc bump_counter(name) {
    let c = find_one("counters", {name: name})
    if c == null {
        insert("counters", {name: name, value: 1})
        return {value: 1}
    }
    update("counters", {name: name}, {$inc: {value: 1}})
    return {value: c.value + 1}
}

Soft-delete

proc soft_delete(collection, id, actor) {
    let doc = find_one(collection, {_id: id})
    if doc == null { abort "not found" }
    update(collection, {_id: id}, {
        $set: {deleted: true, deleted_at: "now", deleted_by: actor}
    })
    insert("audit_log", {
        action: "delete", collection: collection,
        doc_id: id, actor: actor, original: doc
    })
    return {ok: true}
}

Soft-undelete

proc undelete(collection, id, actor) {
    update(collection, {_id: id, deleted: true}, {
        $unset: {deleted: "", deleted_at: "", deleted_by: ""}
    })
    insert("audit_log", {action: "undelete", doc_id: id, actor: actor})
    return {ok: true}
}

Filter out soft-deletes in reads

proc list_active_posts() {
    return find("posts", {deleted: {$exists: false}})
}

Or just {deleted: {$ne: true}} if you sometimes set deleted: false.

Hard-purge after grace period

proc purge_old_soft_deletes(days) {
    let candidates = find("posts", {deleted: true, deleted_age_days: {$gte: days}})
    for p in candidates {
        delete_one("posts", {_id: p._id})
        insert("audit_log", {action: "purge", doc_id: p._id})
    }
    return {purged: count("posts", {deleted: true})}
}

Idempotent insert with dedupe key

proc record_payment(idempotency_key, account_id, amount) {
    if count("payments", {idempotency_key: idempotency_key}) > 0 {
        return {duplicate: true}
    }
    insert("payments", {
        idempotency_key: idempotency_key,
        account_id: account_id, amount: amount, status: "ok"
    })
    update("accounts", {_id: account_id}, {$inc: {balance: amount}})
    return {duplicate: false}
}
Report Issue