Conversation
…perator documents they are written as
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
This was referenced Sep 26, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
libbson's JSON reader (1.28.1) decides what an embedded object is from its first key alone.
$type,$regexand$optionsopen its legacy binary and regular expression values, and they are also MongoDB operators. Every shell payload reaches BSON throughMongoDBConnection.jsonToBson, which passed the whole text tobson_new_from_json. So:{sig: {$type: "binData"}}failed withMissing "$binary" after "$type". So did any alias that starts with a hex digit (double,bool,date,decimal,array). A$typelist failed withInvalid read of "[".{name: {$regex: "^a", $exists: true}}failed withInvalid key "$exists", and{name: {$regex: /^a/i}}failed withUnexpected nested object value.{$regex: "a", $options: "i"}became a regular expression value. mongosh never does that. An insert stored a regex where mongosh stores a subdocument. A stored subdocument of that shape turned into a regex when a script, or a rerun of Show DDL, wrote it back.Only the first key of an object below the root triggers this, and libbson decodes escapes before it compares, so
"$type"is misread too. libbson has no flag to turn the legacy grammar off.Fix
MongoBsonAssembly(new, pure) scans the UTF-8 bytes for an object below the root that opens with one of the three keys. When there is none, the text goes to libbson whole, as before. The scan takes 6 ms on a 6.5 MB text, against 149 to 201 ms for libbson's own parse.{"key": value}. libbson reads its value in nested context, so wrappers such as{"$oid": …}still work, and a root key is never special.0,1and on.$codewith$scope, and the rest) is never taken apart.jsonToBsonkeeps its signature and hands the text toMongoBsonBuilder, a file of its own so the probe below compiles the same code. It builds the parts withbson_new,bson_concatandbson_append_document/bson_append_arraywith explicit key lengths, so a key holding NUL is refused. Any failure discards the partial document.jsonToBsonwas checked, and each now reads its text the way mongosh would:countDocuments)find(...)in the script for the rowsMongoDBQueryBuilder: NOT CONTAINS and case-insensitive!=send{$not: {$regularExpression: …}}.$notaccepts a$regexoperator document only from MongoDB 4.0.7 but accepts a regex value on every server, and the docs promise no minimum version. These arms send exactly what they sent before. The positive arms keep{$regex, $options}, which still pastes into mongosh.scripts/check-mongodb-filter-shapes.sh, which CI runs, compiled a C probe that sent every shape tobson_new_from_jsonwhole, so it never ran the assembler it claimed to check. It now compilesMongoScriptJson,MongoBsonAssemblyandMongoBsonBuilderwithswiftcagainstLibs/libbson, builds every shape through them, and checks the BSON type libbson stored at each path where the two readings differ, read back from its canonical Extended JSON. Two raw shapes join the list:{sig: {$type: "binData"}}and{f: {$regex: "^a", $exists: true}}.buildUriare replaced with the same text as in fix(plugin-mongodb): list views as views, keep index key order and options, and add db.createView to the shell #3150, so the file lints clean.Codescope. CHANGELOG: one Fixed entry and one Changed entry.Verified
MongoBsonAssemblyTestscases. Among them:build MongoDBDriverandplugins(all 40) pass.check-mongodb-filter-shapes.sh: all 30 shapes build as the query means them. With the planner reduced to the old whole-text parse, 5 fail: three$regexshapes stored as regex values instead of documents,{$type: "binData"}refused withMissing "$binary", and$regexbeside$existsrefused withInvalid key "$exists".shellcheckandactionlintpass.Live on MongoDB 7.0.43, using a harness built from the plugin sources before and after the change, compared with mongosh 2.10.0:
countDocumentswere refused. After, all 14 match mongosh. That includes{$in: [{$regex: "^a"}]}, which is now refused with the samecannot nest $ under $in. It used to return [1,3].{$type: "binData"},{$regex: "a", $options: "i"}or{$type: "00", $binary: "AAAA"}was refused,find().forEach(replaceOne)over such a stored document was refused, and$set: {u: {$regex: "x"}}stored a regex. After, every stored document is byte-identical to what mongosh stores.$type: "binData", a view pipeline with$type: "array", apartialFilterExpressionwith$type: "double", abulkWriteupdateManywith a$typefilter, export, explain and cursor count were all refused before and all succeed now.=,!=, IN and NOT IN ignoring case, STARTS WITH and ENDS WITH.$notarms unchanged on the wire./^a/and{$regex: "^a"}, with and withouti.{sig: {$type: "binData"}}was refused for both rows and count before. Now it returns row 1 with count 1.{name: {$regex: "^a", $options: "i"}}used to store the validator back as a regex. It now stays the operator document.findOneAndUpdate,distinct, a collation index, delete, explain and ping.MongoScriptJson.topLevelElements, which the host already runs on everyinsertManyandbulkWritearray.Deliberately not fixed here
{"$options": "i", "$regex": "^a"}is now stored as an embedded document, not a regex value. That is exact for how the grid shows a stored subdocument. It changes a legacy EJSON v1 regex brought in from a pre-4.2 mongoexport file.fix/mongodb-grid-write-valuesrewrites that path and lists$regexinMongoExtendedJsonForm.wrapperKeys. Whichever of the two lands second should drop it there, so the grid and the shell agree.bson_new_from_jsondirectly, and are unchanged here. Edit Document (feat(plugin-mongodb): edit MongoDB documents as Extended JSON, found by their stored _id and saved under an exact guard #3152) should move both to the planner together.Codescope holding an object that opens with$type,$regexor$optionsis still refused. It is documented, with the workaround.reviveand the missing constructors (items 1 to 3 of the design) go in a separate PR.$notarms send the same regex value as before.No UI test: every path needs a connected MongoDB collection, and CI has no MongoDB server. The planner and the builder are covered in-process.
TableProTestsdoes not link CLibMongoc, so the libbson emitter is covered by the live checks above and bycheck-mongodb-filter-shapes.shin CI.