Compatibility¶
SecantusDB’s conformance target is pymongo: a pymongo client should
not be able to tell SecantusDB apart from a real mongod for the operations
it supports. This page lists the divergences that exist anyway.
Stubs¶
These commands accept the request and return a wire-valid response, but the response is fabricated.
Command |
Behaviour |
|---|---|
|
Version + zeroed metrics (uptime, connections) |
|
Real |
|
Hardcoded values; |
|
Empty log array |
|
|
|
Return |
dbStats and collStats return real count, size, storageSize,
avgObjSize, indexSize, indexSizes, totalIndexSize, and totalSize
computed from the WT tables.
explain reports IXSCAN when an index would be used and COLLSCAN
otherwise, with indexName, keyPattern, and direction populated.
Stopgaps (functional but with limitations)¶
$lookup doesn’t use storage indexes¶
Joins are O(N+M) via an in-memory hash table built once over the foreign
collection (covers array-valued local/foreign fields correctly via
element expansion). Both simple (localField/foreignField) and
let/pipeline forms are accelerated.
A true index-driven join would skip materialising the foreign collection but needs multikey-index support to stay correct for array-valued foreign fields.
_id numeric type bridge¶
Works for finite int / float / Decimal128 (they collide on equal value).
bool is deliberately not numeric. NaN and infinity _id values fall
through to the BSON-blob path; behaviour is unspecified.
Date format strings¶
$dateFromString and $dateToString use Python’s strptime / strftime
codes plus the %L extension for milliseconds. The timezone argument
is supported (IANA, UTC offsets, GMT/UTC).
Still missing: full MongoDB format spec (%G / %V ISO-week, %j
day-of-year edge cases) and the MongoDB-specific format tokens.
$merge whenMatched: "merge"¶
Recursive sub-document merge implemented (matches MongoDB). Arrays are replaced as a whole on overlapping keys.
renameCollection¶
Atomic per the storage RLock, but no protection against concurrent
writers across processes. Tests are single-process so this is fine.
createIndexes options¶
Option |
Status |
|---|---|
|
Honoured |
|
Honoured |
|
Honoured via |
|
Honoured at write time and at picker time |
|
Honoured at index-write and at picker time — strings are stored under collation-normalised bytes so a query carrying a matching |
Cursor TTL¶
Cursors idle longer than 600s are pruned opportunistically (matches
MongoDB’s 10-minute cursor TTL). The clock is injectable via
time_func for deterministic tests.
Deferred¶
(No entries — geo, native TLS / mTLS, MONGODB-X509, native checkpoint
backups, configuration file, per-write j:true routing, and
per-index collation all shipped in earlier slices. See the
sub-pages for the detail.)
Out of scope¶
These are explicit non-goals:
Replica sets / sharding — depend on multi-node cluster topology. SecantusDB is single-process. (Change streams are supported — oplog-backed and single-node — see Change streams. The oplog is queryable at
local.oplog.rslike real mongod.)Authentication mechanisms beyond SCRAM-SHA-256 — x509, LDAP, Kerberos, GSSAPI, MONGODB-AWS, MONGODB-OIDC. SCRAM-SHA-256 itself is implemented; SCRAM-SHA-1 is not advertised (modern drivers default to SHA-256). See Authentication.
Authorization (RBAC) —
createUseraccepts arolesarray but no command consults it. An authenticated principal is treated as fully privileged.Native TLS + mTLS are supported as of v0.5.1b21/b22. Configure via
[tls] cert_file/key_file(server-side TLS) and optionally[tls] ca_file/require_client_cert(mTLS); see Configuration. TheMONGODB-X509cert-as-username auth mechanism is the one remaining follow-on — until it lands, SCRAM-SHA-256 over TLS is the auth + confidentiality story.OP_COMPRESSED— compression negotiation. Clients can be told the server doesn’t support compression.Text search (
$text,$meta: "textScore", text indexes) — would need a full-text index implementation.$where/$function/$accumulator/mapReduce— all four evaluate user-supplied JavaScript and would need an embedded JS engine + sandbox + BSON↔JS shim layer.mapReduceis also explicitly deprecated by MongoDB; the canonicalemit(this.<field>, 1)+values.lengthcount pattern is recognised and translated to$group, but anything else needs realmongod.Real transaction rollback —
commitTransaction/abortTransactionreturn{ok: 1}but operations take effect immediately. Logical sessions ARE tracked end-to-end.
What HAS shipped that’s worth calling out (was previously listed as
“deferred” or “out of scope”): geo operators + 2d / 2dsphere
indexes; capped collections (create capped: true) with FIFO
eviction; profiling (profile command + <db>.system.profile);
SCRAM-SHA-256 authentication; the oplog as a queryable
local.oplog.rs collection. See the changelog for
the full inventory and aggregation /
indexes / change streams for the
detail.
Known edge cases¶
$sampleusesrandom.sampleagainst fresh entropy per call by default. For deterministic test results setSECANTUS_SAMPLE_SEED=<int_or_string>in the environment — at module-import time$samplethen uses a dedicatedrandom.Random(seed)so the seed doesn’t leak into the process-sharedrandomstate.$type: "number"in queries handlesint,float,Decimal128, but the int32-vs-int64 distinction depends on the Python value range, not the original BSON type tag (which is dropped on decode). A doc inserted asInt64(5)reads back as a small Python int and matches$type: "int", not"long".$lookupsimple-form-plus-pipeline — when bothlocalField/foreignFieldandpipelineare present, we pre-filter by the simple form and then run the pipeline. Real MongoDB does this in modern versions; documentation isn’t crystal clear on the order. If a test breaks here, this is the place to look.Aggregation
$groupstable order — group buckets are emitted in first-seen order, not sorted. Matches unsharded MongoDB; sharded behaviour isn’t modelled.