Feature comparison

A three-way feature matrix: MongoDB (real mongod, the conformance target — SecantusDB vendors the same WiredTiger storage engine and advertises wire version 7.0) against SecantusDB’s Python server and Rust server (see The two servers).

The honest, machine-checked statement of conformance is the validation reports — the official driver test suites (pymongo, Go, Node, Java, Kotlin, Ruby, Rust, PHP library + extension, C, C++, .NET) run unmodified against SecantusDB. This page is the human-readable decomposition: which features each server implements, where they diverge from mongod, and where they diverge from each other.

Legend: ✅ supported · ⚠️ partial (see note) · ❌ not supported. The MongoDB column describes mongod 7.0 itself and is the reference.

At a glance

MongoDB

Python server

Rust server

What it is

the real thing

pure-Python in-process server

self-contained Rust server, GIL-free request path

Install

external mongod process

pip install SecantusDB

the secantus-mdb crate (crates.io), or a release archive

Run

daemon

1-line embedded (SecantusDBServer) or secantusd-py daemon

secantusd-rs daemon, or in-process from Rust (secantus_mdb::Server)

pymongo’s own suite

100% by definition

99.5% (1020 pass / 5 fail)

99.5% (1020 pass / 5 fail)

Version line

7.0.x

0.5.4bN (the PyPI package)

0.5.3-beta.N (crates, buildInfo.secantusVersion)

Deployment and topology

Feature

MongoDB

Python server

Rust server

Single-node standalone

✅

✅

✅

Embedded in-process (no external process)

❌

✅

✅ (Rust accept loop in-process, Python is only the launcher)

Storage engine

WiredTiger

WiredTiger (vendored, same version line)

WiredTiger (vendored, same version line)

In-memory mode

enterprise only

✅ (:memory:)

✅

Replica sets (multi-node, elections)

✅

❌ — single-node primary persona only, so drivers accept change streams

❌ — same persona

Sharding / mongos

✅

❌

❌

Windows / macOS / Linux

✅

✅ (prebuilt wheels, no toolchain needed)

✅ binaries for Linux x86_64, macOS arm64 and Windows x86_64; any platform via cargo install

Wire protocol

Feature

MongoDB

Python server

Rust server

OP_MSG (incl. kind-1 document sequences)

✅

✅

✅

Legacy OP_QUERY handshake

✅

✅

✅

OP_COMPRESSED (snappy, zstd, zlib)

✅

❌

✅

Wire-valid error replies (ok: 0, code, codeName)

✅

✅

✅

Unknown command → CommandNotFound (59), connection survives

✅

✅

✅

Commands

Both servers implement the same core dispatch surface: CRUD (insert / find / update / delete / count / distinct / findAndModify / getMore / killCursors / aggregate / explain), DDL (create / drop / collMod / dropDatabase / renameCollection / listCollections / listDatabases / index commands), diagnostics (hello / ping / buildInfo / serverStatus / dbStats / collStats / validate / getLog / currentOp / killOp / fsync / profile / configureFailPoint), sessions / transactions, and the full auth / RBAC command set. The divergences:

Command

MongoDB

Python server

Rust server

mapReduce

⚠️ deprecated

⚠️ {out: {inline: 1}} only; canonical emit/count patterns translated (no JS runtime)

⚠️ same as the Python server (a case-for-case port)

top

✅

⚠️ correct shape, counters always 0

⚠️ correct shape, counters always 0

serverStatus

✅

⚠️ version + mostly-zeroed metrics

⚠️ smaller subset still

dbStats / collStats

✅

✅ real counts/sizes from the WT tables

⚠️ dataSize used for size fields

replSetGetStatus

✅

⚠️ single-node persona reply

⚠️ single-node persona reply

replSetStepDown

✅

⚠️ the single-node behaviour: force: true makes the node a secondary for the period (writes 10107, reads served), then primary again. Election TIMING not reproduced — mongod’s return is driven by an election, so a 0-second period takes ~19s there and 0s here

⚠️ same

getParameter / setParameter

✅

⚠️ the parameters this server actually has (logLevel, quiet, and the read-only handshake ones). A name we do not register is refused 72 rather than accepted and ignored

⚠️ same

Atlas Search index commands (createSearchIndexes, …)

Atlas only

❌ rejected (CommandNotSupported)

❌ rejected

secantusAdmin.* (backup / PITR / prune — SecantusDB-proprietary)

—

✅

✅ (restoreToTimestamp via the secantusd-rs restore CLI rather than a wire command)

Query operators

Identical surface on both servers: all comparison ($eq $ne $gt $gte $lt $lte $in $nin), logical ($and $or $nor $not), element ($exists $type), evaluation ($mod $regex $expr $jsonSchema $comment), array ($all $elemMatch $size), bitwise ($bitsAllSet $bitsAnySet $bitsAllClear $bitsAnyClear), and geo ($geoWithin $geoIntersects $near $nearSphere with $geometry / $box / $center / $centerSphere / $polygon / $maxDistance / $minDistance) operators.

Feature

MongoDB

Python server

Rust server

$where (JavaScript)

✅

❌ (no JS runtime — out of scope)

❌

$text

✅

❌ (no text indexes)

❌

$jsonSchema

✅

⚠️ full keyword set except $ref and metadata keywords

⚠️ same

Update operators

No known divergences from mongod’s operator list, in either server: $set $setOnInsert $unset $inc $mul $min $max $rename $bit $currentDate $push (with $each / $position / $slice / $sort) $addToSet (with $each) $pull $pullAll $pop; positional $ / $[] / $[<identifier>] with arrayFilters; and pipeline-form updates ($set / $addFields / $unset / $project / $replaceRoot / $replaceWith).

Aggregation stages

Both servers: $match $project $addFields/$set $unset $replaceRoot $replaceWith $redact $limit $skip $sort $count $unwind $group $sortByCount $bucket $bucketAuto $densify (incl. month / quarter / year calendar units) $fill $setWindowFields $lookup $graphLookup $facet $sample $out $merge $unionWith $geoNear $documents $collStats $indexStats $changeStream $changeStreamSplitLargeEvent $currentOp $listLocalSessions $listSessions (the session/op stages emit synthesized rows on both).

Feature

MongoDB

Python server

Rust server

$search / $searchMeta / $vectorSearch / $listSearchIndexes

Atlas only

❌ rejected cleanly

❌ rejected cleanly

Leading-$match index acceleration

✅

✅

✅

$lookup index-driven join

✅

✅ (foreign-field index → IXSCAN, else hash join)

✅

$group accumulators and window functions

Both servers: $sum $avg $min $max $first $last $push $addToSet $count $stdDevPop $stdDevSamp $mergeObjects $top $bottom $topN $bottomN $firstN $lastN $maxN $minN; window functions $rank $denseRank $documentNumber $shift $expMovingAvg $locf $linearFill $derivative $integral plus the accumulators over document- and range-based windows (fixed-duration time units; calendar units month / quarter / year are not accepted in window bounds on either server).

Feature

MongoDB

Python server

Rust server

$median / $percentile

✅

❌

❌

$accumulator (JS)

✅

❌ (no JS runtime)

❌

Expression operators

Both servers implement the expression language near-completely: arithmetic (incl. $rand, $log10, all trigonometry $sin … $atan2 / $sinh … and $degreesToRadians / $radiansToDegrees), comparison (incl. $cmp), logical, conditional ($cond $ifNull $switch), type introspection ($type $isNumber $isArray), string (incl. $replaceOne / $replaceAll, $regexMatch / $regexFind / $regexFindAll, byte and code-point variants), array (incl. $sortArray $zip $reduce $firstN / $lastN / $maxN / $minN), set ($setUnion $setIntersection $setDifference $setEquals $setIsSubset $anyElementTrue $allElementsTrue), object ($mergeObjects $objectToArray $arrayToObject $getField $setField), date ($dateAdd $dateSubtract $dateDiff $dateTrunc $dateFromParts $dateToParts $dateToString $dateFromString and every getter incl. $millisecond / $week / $dayOfYear / the ISO family), conversion ($convert, $toInt / $toDouble / $toBool / $toDecimal / $toString / $toDate), timestamps ($tsSecond $tsIncrement), BSON introspection ($binarySize $bsonSize), bitwise ($bitAnd $bitOr $bitXor $bitNot), and $let / $literal with the system variables ($$ROOT $$CURRENT $$NOW $$REMOVE, $redact’s $$KEEP / $$PRUNE / $$DESCEND).

Missing from both servers (error cleanly). Re-measured against mongod 8.2.11 on 2026-09-01, because a list like this goes stale silently and hides features that already work — most of what used to be here did:

  • $meta in an AGGREGATION stage. It works in a find projection (recordId / sortKey / indexKey all compute); inside a pipeline mongod’s answer depends on its metadata dependency analysis, which is not a rule that can be inferred — $project returns recordId where $addFields silently omits it, and recordId survives a $match but not a $limit.

  • $function (no JS runtime).

Previously listed here and since verified working: the accumulator family in expression position ($sum / $avg / $min / $max / $stdDevPop / $stdDevSamp over an array argument), $median / $percentile in expression form, $toLong and $toObjectId.

Rust-server-only limitations: a handful of edge cases the Rust engine rejects rather than risk diverging from the Python oracle — some $dateFromString / $dateToString format directives, Decimal128 arithmetic edges, and sorts over non-totally-orderable value mixes (NaN / bool / Decimal128).

Unknown expression operators report mongod’s error codes: a query $expr returns InvalidPipelineOperator (168) on both servers, and a $project returns mongod’s Location31325 shape on the Python server (the Rust server still returns a generic BadValue for the $project case).

Indexes

Feature

MongoDB

Python server

Rust server

Single-field + compound, mixed ASC/DESC

✅

✅

✅

Unique (E11000 dup-key message shape)

✅

✅

✅

Sparse

✅

✅

✅

Partial (partialFilterExpression + planner implication)

✅

✅

✅

TTL (expireAfterSeconds, background sweeper)

✅

✅

✅

Multikey (array)

✅

✅

✅

Per-index collation

✅

⚠️ matching: strength 1–3 + caseLevel; numericOrdering → COLLSCAN. Ordering is a three-level key (accents, case, caseFirst, backwards, numericOrdering); locale-specific order (Swedish ä) needs ICU

⚠️ same

Hidden indexes

✅

✅

✅

Geo 2dsphere (S2 coverings) / 2d (geohash)

✅

✅

✅

Text

✅

❌ rejected

❌ rejected

Hashed

✅

❌ rejected

❌ rejected

Wildcard ($**)

✅

❌

❌

hint (name, key spec, {$natural: ±1})

✅

✅

✅

explain (IXSCAN/COLLSCAN + winningPlan shape)

✅

✅

✅

Collections

Feature

MongoDB

Python server

Rust server

Capped collections (FIFO eviction, tailable cursors)

✅

✅

✅

Timeseries collections

✅

✅ (duplicate _ids handled)

✅

Clustered collections ({_id: 1})

✅

✅

✅

Schema validation (validator, validationLevel / validationAction)

✅

✅

✅

Views (viewOn + pipeline, incl. views-on-views)

✅

✅

✅

Change streams

Feature

MongoDB

Python server

Rust server

Collection / database / cluster scope

✅

✅

✅

fullDocument: updateLookup / whenAvailable / required

✅

✅

✅

Pre-images (fullDocumentBeforeChange)

✅

✅

✅

resumeAfter / startAfter / startAtOperationTime

✅

✅

✅

showExpandedEvents (DDL events)

✅

✅

✅

splitLargeChangeStreamEvents / $changeStreamSplitLargeEvent

✅

✅

✅

Invalidate on drop / dropDatabase / rename

✅

✅

✅

Resume tokens interchangeable with real mongod

✅

❌ SecantusDB’s own {s,t,n,k} layout — round-trips within SecantusDB only

❌ same layout (interchangeable between the two servers)

Transactions and sessions

Feature

MongoDB

Python server

Rust server

Multi-document transactions (WT-backed, single-node)

✅

✅

✅

commitTransaction / abortTransaction (idempotency, error labels, NoSuchTransaction, write conflicts)

✅

✅

✅

Read/write concern

✅

⚠️ accepted and ignored (single node — no majority/snapshot semantics)

⚠️ same

readConcern: snapshot timestamp pinning

✅

⚠️ accepted, reads not actually pinned

⚠️ same

Retryable writes

✅

⚠️ envelope tolerated, no dedup by txnNumber

⚠️ same

Logical sessions (lsid tracking, 30-min TTL, kill/refresh)

✅

✅

⚠️ startSession real; end/refresh/kill acknowledged as no-ops

Authentication and transport security

Feature

MongoDB

Python server

Rust server

SCRAM-SHA-256 / SCRAM-SHA-1

✅

✅

✅

MONGODB-X509

✅

✅

✅

TLS / mTLS (client-cert verification)

✅

✅

✅ (rustls)

RBAC: built-in roles, custom roles, grant/revoke, privileges

✅

✅

✅

LDAP / Kerberos / PLAIN / MONGODB-AWS / MONGODB-OIDC

✅ (some enterprise)

❌

❌

Internal cluster auth (keyfile)

✅

❌ (no cluster)

❌

Backup and point-in-time recovery

Feature

MongoDB

Python server

Rust server

Oplog (single-node, retention + pruning)

✅

✅

✅

Hot backup archive

✅ (backup cursor / ops tooling)

✅ secantusAdmin.backupArchive / archiveBaseSnapshot

✅ same commands

Restore an archive

✅

✅ secantusAdmin.restoreArchive / secantus-restore-archive

✅

Point-in-time restore (oplog replay)

✅ (ops tooling)

✅ secantusAdmin.restoreToTimestamp

⚠️ secantusd-rs restore CLI; no wire command yet

Archives portable between the two servers

—

✅

✅

Beyond MongoDB: the SQL/PostgreSQL frontend

There are TWO PostgreSQL servers, and only one of them is in the table below. The Python server speaks the PostgreSQL wire protocol as secantusd-py-pg, and the Rust one as secantusd-pg — both against the same WiredTiger data, so psql, psycopg or SQLAlchemy connect to the store MongoDB clients use. See SQL / PostgreSQL interface.

The “Rust server” column below is the Rust MONGODB server (secantusd-rs), which speaks no SQL — so its ❌ is about that binary, not about the Rust side. secantusd-pg is a separate deliverable with its own version line and is not represented here.

What secantusd-pg does, measured rather than claimed (2026-09-29): psycopg 3’s own unmodified suite runs against it with 5,544 passed and none failed (149 skipped, 58 deselected, 34 xfailed), and the differential corpora in tools/probes/pg_corpora/ compare its answers to a live PostgreSQL 14.13. Subqueries and CTEs, window functions, ALTER TABLE, sequences and identity columns, and information_schema all landed on 2026-09-28. CREATE INDEX, CREATE VIEW, EXPLAIN and correlated subqueries are still refused, by name; tasks/backlog.md carries the measured remainder.

Feature

MongoDB

Python server

Rust server

PostgreSQL v3 wire protocol (simple + extended query)

❌

✅

❌

SQL engine (planner / executor / catalog / window functions / EXPLAIN)

❌

✅

❌

PG types & features (bytea, ranges, intervals, hstore, jsonpath, tsvector/tsquery FTS, LISTEN/NOTIFY, RLS, GRANT/REVOKE, plpgsql functions)

❌

✅

❌

PG SCRAM-SHA-256 auth + TLS

❌

✅ (no channel binding)

❌

Out of scope for all SecantusDB servers

Anything that depends on real cluster topology or an embedded JavaScript runtime is out of scope by design, in both servers: multi-node replica sets, elections, sharding, cross-node oplog; $where, $function, $accumulator, JS mapReduce; text / hashed / wildcard indexes; Atlas Search / Vector Search; LDAP / Kerberos / AWS / OIDC auth. See Compatibility for the per-feature detail and the validation reports for the machine-checked conformance numbers this page summarizes.