Test cases for MongoDB semantics not reproduced by the heuristics calculator - #1743
Open
LautaroPetaccio wants to merge 1 commit into
Open
Test cases for MongoDB semantics not reproduced by the heuristics calculator#1743LautaroPetaccio wants to merge 1 commit into
LautaroPetaccio wants to merge 1 commit into
Conversation
This was referenced Sep 7, 2026
… calculator Twenty-four defects in MongoHeuristicsCalculator, each as its own @disabled test, so they can be enabled one at a time and in any order as the behaviour is implemented. Every expected value was obtained by running the same query and the same document against a real MongoDB 7.0.40 server, so the assertions state what the database does rather than an interpretation of the documentation. Ten of them make the calculator throw. As MongoHandler does not catch anything, the exception escapes the heuristics computation for the action, so the ExtraHeuristicsDto is lost, including the SQL heuristics computed before it: $type with a string alias or with a list of aliases, a bitmask given as an Integer or as a list of bit positions, and $not holding more than one operator, are not parsed, and the calculator throws a NullPointerException on the resulting null operation; an operator that is not modelled at all is not parsed either, which covers $expr, $jsonSchema, $where, $text, $geoWithin, $geoIntersects and $comment. The last one attaches to an otherwise ordinary query, so {"a": 1, "$comment": "..."} is enough to lose the heuristics of the action; an empty list of values, an empty array in the document, and comparing two empty arrays aggregate over no element and throw IllegalArgumentException; an ordering comparison involving NaN builds a Truthness with neither of its values equal to 1, which its own constructor rejects. Any double field can hold NaN; a value the calculator cannot compare, such as a sub-document or binary data, reaches the "Unsupported type" branch and throws. The test for the operators that are not modelled asserts only that nothing is thrown, not any particular score: whether such an operator should be supported, and what it should answer, is a decision for the heuristic. What it should not do is cost the action its heuristics. The other fourteen are answered, but not the way MongoDB answers them. Those that report a match where MongoDB has none are the harmful direction, as a condition no data can satisfy is recorded as covered and the search stops working towards it. Several share one rule: MongoDB matches a field when its value satisfies the condition, or when it holds an array of which any element does, and that applies to every condition on a field rather than only to equality. $all is the same rule quantified the other way round, over the expected values. Four tests of behaviour that is already correct are added as well, as a guard while the heuristic is changed. One of them, testNotEqualsAgainstAnArrayField, answers correctly only because two of the reported defects cancel each other out; fixing either one alone turns it into a false positive, which is why it is worth keeping visible.
LautaroPetaccio
force-pushed
the
tests/mongo-semantics-defects
branch
from
September 7, 2026 17:54
aded92d to
c293276
Compare
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.
Test cases only, no change to the heuristics, as agreed: twenty-four defects in
MongoHeuristicsCalculator, each as its own@Disabledtest so they can be enabled one at a time and in any order.Every expected value was obtained by running the same query and the same document against a real MongoDB 7.0.40 server, so the assertions state what the database does rather than an interpretation of the documentation. Enabling a test is a one-line change: delete its
@Disabled.The suite is green as it stands: 299 passing, 24 skipped. I also checked the other direction, that every one of the twenty-four actually fails when its annotation is removed.
Ten make the calculator throw
MongoHandler.computeFindDistancedoes not catch anything, so the exception escapes the heuristics computation for the whole action and theExtraHeuristicsDtois lost, including the SQL heuristics computed just before it.testTypeWithAStringAlias{"a":{"$type":"string"}}is not parsed → NPE on the null operationtestTypeWithAListOfAliases{"$type":["string","int"]}is not parsed → NPEtestBitsWithAnIntegerBitmask{"a":{"$bitsAllSet":1}}is not parsed (onlyLongis accepted) → NPEtestBitsWithBitPositions{"$bitsAllSet":[0,2]}is not parsed → NPEtestNotWithMoreThanOneInnerOperator{"$not":{"$gt":1,"$lt":9}}and a nested$notare not parsed → NPEtestOperatorsThatAreNotModelledDoNotThrow$expr,$jsonSchema,$where,$text,$geoWithin,$geoIntersects,$commentare not parsed → NPEtestInAndNotInWithAnEmptyListOfValues{"$in":[]}aggregates over no element →IllegalArgumentExceptiontestInAndNotInAgainstAnEmptyArrayField{"tags":[]}with$in→ sametestEqualsBetweenEmptyArraystestOrderingComparisonWithNaNTruthnessbuilt for it has neither value equal to 1 and its own constructor rejects ittestFieldsHoldingASubDocument"Unsupported type"branch → throwsThe parse gaps are worth a look for how easily they hide: the E2E endpoints write
$typewith the numeric code2and their bitmasks as1L/5L, which are exactly the two forms that do parse.QueryParserTest.testParseInvalidBitwiseValuesalso currently asserts that an Integer bitmask is invalid, which is the opposite of what MongoDB accepts, so that assertion will need removing along with the@Disabled.Fourteen are answered, but not as MongoDB answers them
The ones reporting a match where MongoDB has none are the harmful direction: a condition no data can satisfy is recorded as covered, so the search stops working towards it.
testAllQuantifiesOverTheExpectedValues{$all:["a","b","c"]}vs["a"]→ no matchtestNotInAgainstAnArrayField{$nin:["a"]}vs["a","b"]→ no matchtestNotOnAMissingFieldWithAnInnerOperatorThatMatchesIt{$not:{$ne:5}}vs{}→ no matchtestElemMatchOnAnArrayOfScalars{$elemMatch:{x:1}}vs[1,2,3]→ no matchtestBitsDoesNotMatchANonIntegralNumber3.5is not truncated to3→ no matchtestNullInsideAnArrayField{$ne:null}vs[1,null,3]→ no matchtestOrderingComparisonBetweenAnObjectIdAndANumber{$gt:5.0}vs an ObjectId → no matchtestFieldsAreMatchedByAnElementOfTheArrayTheyHold{$gt:2}vs[1,2,3]→ matchtestRegexMatchingAnElementOfAnArrayField{$regex:"^ab"}vs["abc","x"]→ matchtestNotEqualsBetweenIncomparableTypes{$ne:"abc"}vs42→ matchtestAllOnAScalarField{$all:["a"]}vs{f:"a"}→ matchtestInMatchingAnArrayFieldAsAWhole{$in:[["a","b"]]}vs["a","b"]→ matchtestDottedFieldPath{"a.x":1}vs{a:{x:1}}→ matchSeveral of these are one underlying rule, in case that helps in grouping the work: MongoDB matches a field when its value satisfies the condition or when it holds an array of which any element does, and that applies to every condition on a field, not only to equality.
$allis the same rule quantified the other way round, over the expected values.$typehas a second problem behind the parse gap: the check comparesactualValue.getClass().getTypeName()against a class name fromBsonTypeClassMap, so$type:"array"comparesjava.util.ArrayListagainstjava.util.Listand cannot match. Fixing only the parsing would turn that crash into a silent wrong answer.Four tests of behaviour that is already correct
Added as a guard while the heuristic is being changed: ordering operators correctly not matching across incomparable BSON types,
$eqagainst a whole array including the order of its elements,$typewith a numeric code and$bitswith aLongbitmask, andtestNotEqualsAgainstAnArrayField.That last one is worth singling out. It answers correctly today, but only because two of the reported defects cancel out:
$nedoes not look at the elements of the array, which alone would answer true, and the comparison between incomparable BSON types answers false regardless of the operator, which brings it back to false. Fixing either one on its own turns it into a false positive.What held up
The scores themselves hold up: across 928 combinations of the five most used operators and fourteen value types, every
Truthnessstays within range, none isNaN, and none reports a full score for a document MongoDB does not return, so there is no silent full coverage beyond the wrong answers listed above. The gradient over numbers is monotone for$eq,$gtand$lt, which is the property the search depends on:$and,$orand$norwith an empty array do make the calculator throw, but MongoDB rejects those queries itself, so they cannot reach it from a system under test and are not reported.A note on the operators that are not modelled
testOperatorsThatAreNotModelledDoNotThrowis the one test that does not ask for an operator to be supported. Whether$expror$whereshould ever be modelled, and what the score should be when they are not, is a decision for the heuristic, so the test asserts only that nothing is thrown.$commentis the one worth looking at first: it attaches to an otherwise ordinary query, so{"a": 1, "$comment": "..."}is by itself enough to lose the heuristics of the action.Replaces
#1739 and #1740, both closed. No heuristic changes here.