All releases

v3.0.0

The audit release: a whole-codebase adversarial review, a second review of the fixes themselves, and roughly 80 new differential scenarios pinning every behaviour against real MongoDB 8.0.

Highlights

  • 01

    Silent divergences, found and closed

    A dotted $set through an array destroyed data. {$set:{"a.0.b":1}} on {a:[5]} silently replaced the element where the server refuses with PathNotViable, and the aggregation twin collapsed an array of subdocuments into one document. $all with $elemMatch members matched nothing. $project dropped computed dotted fields when the parent was absent. distinct returned three values where the server returns one. Every one of these answered differently from MongoDB rather than failing loudly. That is the worst failure mode a test double has, and the one this release set out to eliminate. Every claim was checked against a live mongo:8.0 before any code changed. Four review findings turned out to be wrong: regex i really does fold Unicode case, a matched replaceOne always reports modifiedCount 1, $push modifiers without $each are stored literally, and the server kills a transaction at the failing write. Each is now pinned by a scenario, so the true behaviour cannot be "fixed" into a regression later.

    Read the guide →
  • 02

    One ordering rule instead of four

    Cross-type numeric comparison had drifted into four private copies, two of them lossy: the value engine rounded doubles to their shortest decimal rendering, so Decimal128("0.1") compared equal to double 0.1 where the server orders them apart. The update engine kept its own copy plus UTF-16 string ordering, which meant find({v: NumberDecimal("0.1")}) and $pull:{v: NumberDecimal("0.1")} gave opposite answers for the same stored value. Sort, _id equality, $min/$max, $pull, $addToSet and array filters now share one exact rule, and query-style comparisons are type-bracketed as the server's are.

    Read the guide →
  • 03

    Your catch block sees the server's exception

    insertMany raised MongoWriteException where the driver raises MongoBulkWriteException: the code and category were the same, but catchability differed, and the mismatch was invisible to a differential lane that only compared error codes. The lane now compares the exception class too, which immediately surfaced ten more scenarios: query errors that should be MongoQueryException, an index build that leaked the storage engine's own exception, malformed update operands arriving as IllegalArgumentException instead of a translated write error.

    Read the guide →
  • 04

    Two configurations now refused instead of silently corrupting

    A class-level @Execution(CONCURRENT) ran a class's methods concurrently against one shared context while each method's cleanup dropped the others' databases. A Spring context defining reactive Mongo beans connected to the real server, because the emulator rewires only the synchronous ones. Both now fail fast with an actionable message. Both are breaking for suites that are green today. See the migration notes in the changelog.

    Read the guide →

Full notes

Breaking changes

  • insertMany now raises the driver's MongoBulkWriteException on a duplicate key, where the shim previously raised MongoWriteException. The two are siblings, not subtypes, so an existing catch (MongoWriteException e) around a batch insert stops catching. Change it to MongoBulkWriteException, or catch MongoException for both. Single-document writes are unchanged. Several other operations likewise moved to the exception class a real server produces (MongoQueryException for query-shaped errors, DuplicateKeyException for a failed unique-index build, translated write errors for malformed update operands).
  • A class-level @Execution(CONCURRENT) on an emulator test class is refused when parallel execution is enabled. It ran the class's own methods concurrently against one context while each method's cleanup dropped the others' data. Ask for parallel classes with junit.jupiter.execution.parallel.mode.classes.default=concurrent instead.
  • An embedded Spring test context that defines a reactive MongoDB bean is refused, because those beans are outside the emulator's reach and connect to the configured server for real. Exclude the reactive auto-configuration from the slice, or set bonsai.reactive.allow-unmanaged=true to boot anyway.
  • Nothing a consumer imports was renamed. Coordinates, packages, annotations and properties are unchanged from 2.x, so this is not a 2.0-style migration. The major reflects corrections that are visible to code which already compiles and passes; every one of them moves the library towards real MongoDB, so a suite whose assertions were already true against a real server should upgrade as a no-op.

Changed

  • The differential lane's error signatures now include the exception class, so a future divergence in exception type, not just code or category, fails the build. Three documented expected divergences gained compatibility-matrix rows: codec.uuid-unspecified, transaction.uniqueIndexTiming and crud.replaceNaturalOrder.

Fixed

  • Silent divergences: dotted $set/$inc through arrays (data loss), $project computed dotted parents, $all member semantics, exact cross-type numeric ordering unified across the whole codebase, UTF-8/code-point string ordering, type-bracketed query comparisons, distinct cross-type deduplication, replaceOne modifiedCount, $type alias validation, non-finite arithmetic, mixed-type _id ordering with a consistent hashCode, a phantom index left behind by a failed unique-index build, and insertMany generated-id write-back.
  • Exception families: MongoBulkWriteException for insertMany, MongoQueryException for query-shaped errors, DuplicateKeyException for failed index builds, translated write errors for malformed update operands, a real E11000 for in-transaction unique-index violations, and the driver's own rejection of empty batches.
  • Harness correctness: the @Nested deadlock refusal runs before any shared state is touched, the parallel-methods refusal honours an explicit @Execution(SAME_THREAD) and names the declaring class, the SEC-1 non-local-client warning works again after a Spring Data accessor rename had silenced it, plus session close/cleanup races, drop-versus-commit lock discipline, per-fork durability files and shutdown-hook lifecycle.
  • Includes the 2.0.2 fix: an unterminated ${ placeholder in a seed fixture is rejected rather than seeded as literal text.