Skip to content

v4.0.0

Choose a tag to compare

@shadowsmind shadowsmind released this 24 Sep 12:33
· 102 commits to main since this release

mongo4s 4.0.0

Pipeline updates, sessions without a transaction, $geoNear and three more stages, and a testkit fake that answers
the way the server does. Most breaks are compile errors; a few calls now refuse what they used to accept quietly.
Full migration guide: COMPATIBILITY.md.

libraryDependencies += "org.mongo4s" %% "mongo4s-core" % "4.0.0"

Breaking changes

  • An Update that changes one path twice is refused when it renders — inc(n, 1).and(inc(n, 2)) used to send
    {"$inc": {"n": 2}} and lose the first change. Paths that overlap across operators are refused too; the server
    refused those anyway. No compile error behind this one.
  • findPage returns KeysetPage(items, next) — pass next as after; it is None on the last page.
  • watchAttempting yields ChangeEvent[DecodeResult[A]], so an event whose document fails to decode still
    arrives, resume token included. watchAsAttempting on the client and the database changed the same way.
  • distinct over an array answers with its elements — distinct[C, B] through DistinctOf. A call that named
    the type argument names both now, or neither.
  • regex, mod, hasSize and popFirst/popLast compile only on text, numeric and collection fields.
  • A limit or batchSize below one and a negative skip throw where they are set — FindQuery,
    CountOptions, WatchOptions, Stage and Page. MongoDB reads a limit of 0 as no limit at all.
  • ChangeEvent gained namespace and destination — positional construction takes two more arguments.
  • Update has a Pipeline case, Stage four new cases, MongoCollection three new members — an exhaustive
    match or an implementation of your own has more to cover.
  • The testkit fake takes the runtime's RsBridge instead of an emit function —
    FakeMongoCollection[IO, S, User](codec) — and its listIndexes describes indexes as the server does.
  • WireCodec.spellsAs removed.

New features

  • Pipeline updates — Update.pipeline(stages*) over $addFields, $project, $unset and $replaceRoot,
    taken by updateOne, updateMany, findOneAndUpdate and bulk updates.
  • $geoNear with GeoNearOptions — query, distance bounds, multiplier, includeLocs. $unset,
    $sortByCount and $bucketAuto; bucketAutoRounded compiles only on a numeric field, the only kind the
    server rounds.
  • createIndexes, dropIndexes, renameCollection.
  • MongoClient.withSession and SessionOptions — a causally consistent or snapshot session without a
    transaction, given to every call in the body.
  • The findOneAnd* options carry collation, hint, maxTime, comment and bypassDocumentValidation.
  • findPage(descending = true), Index.expiringAtFieldTime, BaseMongoRepository.createDirect,
    BulkWriteFailed.result, Effect.sleep.

Bug fixes

Each reproduced by a failing test first; the fake-versus-server ones are pinned by parity specs against MongoDB 8.2.

  • A sum type's codec, and one mapped with imap/iemap, reported the wrong naming — on a direct collection a
    snake_case enum entity got camelCase filters and updates that matched nothing.
  • emap/iemap over an Option codec could not read back the empty value it wrote.
  • A commit that ran out of maxCommitTime was retried until the retry window closed. Whole-transaction retries
    now back off with jitter, as the driver specification requires.
  • A transaction body that threw while being built left the transaction open, and the default bracketCase —
    kyo's and rapid's — released nothing in the same case.
  • A repository write failing in a later batch named the command by its position in that batch, and its result
    left out the batches before it.
  • Cancelling a unit call left its subscription running on cats-effect and kyo.
  • zio and rapid asked a cursor for a whole buffer to answer one or option.
  • Repository.findOneAndUpdate replaced your projection; a sub-millisecond maxTime meant no limit; Either's codec
    let a _type field overwrite the discriminator; BsonDecoder[BigDecimal] threw on a non-finite Double;
    Repository.exists counted every match; selectAs recomputed names and decoders for every document.
  • The fake answers like the server in many more places — duplicates in insertMany and unordered bulks, unique
    indexes enforced and named as the server names them, streams read when they run, collations refused, numbers
    compared across widths, replacements keep their _id, no-op updates not counted as modified, and index creation
    and drops failing where the server fails.

Benchmarks

A/B against 3.0.0 on one machine, all six harnesses: no measurable change. Allocations per operation are identical
and every difference sits inside run-to-run error; the driver bump to 5.12.0 is not slower.
BENCHMARKS.md keeps the 3.0.0 measurements, which
stand.

Compatibility

  • Scala 3.9 LTS; building requires JDK 25. mongo4s-kyo remains outside the binary compatibility promise while
    kyo is on a release candidate.
  • MongoDB Java driver 5.12.0. Tested against JDK 25 and MongoDB 8.2.
  • Not binary compatible with 3.x — see the migration guide.