`local.oplog.rs` queryable from pymongo, `$merge` pipeline form + `$fill` stage + `$$var.path` resolution
Summary: local.oplog.rs queryable from pymongo, $merge pipeline form + $fill stage + $$var.path resolution (v0.5.1b17).
Real mongod exposes the oplog as a queryable collection at
local.oplog.rs — pymongo clients can db.oplog.rs.find() against
it the same way they would against any collection. Until this release,
SecantusDB's oplog was internal only: Storage.read_oplog /
oplog_floor_seq / oplog_tail_seq were Python methods but had no
wire surface. Now local.oplog.rs is a synthetic read-only view —
list_collections("local") surfaces it, find / count /
listCollections.options route to a reader that walks the oplog WT
table directly, and write attempts (insert, update, delete,
findAndModify, drop, create, createIndexes) refuse with code
13 (Unauthorized) like mongod does. The deferred admin UI /oplog
page is unblocked as a follow-up; for now, debugging an in-flight
change-stream pipeline is as simple as
client.local.oplog_rs.find({"op": "u"}).sort("ts", -1).limit(20).
The aggregation expression library picks up two of the three remaining
stages on most "more stages" wishlists. $merge was partly
implemented; this batch fills in the rest: whenMatched: [<pipeline>]
runs a sub-pipeline against the matched target doc with $$new bound
to the source doc and any user let vars threaded through;
whenMatched: "delete" (MongoDB 5.0+) removes the matched doc; a
unique-index guard refuses non-_id on fields without a unique:
true index covering them, matching mongod's rule against silent
on-field collapse.
$fill lands fresh — the 5.3+ stage for filling missing/null fields.
Three modes per output field: {value: <expr>} replaces with an
evaluated expression; {method: "locf"} carries the last observation
forward within the partition's sortBy order; {method: "linear"}
interpolates between bracketing non-null anchors along the sortBy field
(works for numbers and datetimes — timedelta arithmetic divides cleanly
to float and multiplies back to timedelta). Partitioning via
partitionByFields or partitionBy; sortBy required when any output
uses method.
The $merge pipeline form was the first thing in the repo to exercise
$$var.path (e.g. $$new.delta), and surfaced that the expression
evaluator only did exact-name var lookup. Fixed in the same batch:
$$var.field.path now walks the dotted path into the resolved value
across $$ROOT.f / $$CURRENT.f / user-let vars.