Affects: 2.12.2 (latest release) and master — criteria-inmemory (ExpressionInterpreter)
Description:
ExpressionInterpreter.binaryCall evaluates EQUAL, NOT_EQUAL, IN and NOT_IN with Objects.equals, and CONTAINS with Iterables.contains, which uses equals as well.
For BigDecimal, equals is true only when the scale matches too, so a stored 2.00 is not equal to a queried 2. MongoDB ignores scale and selects the value. The two backends therefore return different result sets for the same criterion, which is the failure a two-engine setup exists to avoid: one engine answers a query against stored data, the other answers the same query in memory against a single changed item, and the two disagree.
Scale is a property of the representation, not of the number. The practical case is a boundary value: read back from a previous page as 2.00 and fed into the same criterion, it silently drops the item in memory while the server keeps it.
The asymmetry sits inside one backend. Ordering goes through Comparable, so the very same two values are tied when sorted and unequal when filtered, with nothing in any type signature to hint at it.
Steps to reproduce:
repository.insert(generator.get().withId("id1").withValue(new BigDecimal("2.00")));
repository.insert(generator.get().withId("id2").withValue(new BigDecimal("3")));
repository.find(criteria.value.is(new BigDecimal("2"))); // empty
repository.find(criteria.value.in(new BigDecimal("2"), new BigDecimal("3.0"))); // empty
// same for an iterable attribute holding 2.00
repository.find(criteria.list.contains(new BigDecimal("2"))); // empty
Actual: empty in all three cases.
Expected: id1, id1+id2, id1 — what MongoDB returns for the same data.
Over a list of strings the two equalities agree, which is why contains in particular looks correct until a numeric element is involved.
I will deliver a pull request for this issue.
Affects: 2.12.2 (latest release) and master — criteria-inmemory (ExpressionInterpreter)
Description:
ExpressionInterpreter.binaryCall evaluates EQUAL, NOT_EQUAL, IN and NOT_IN with Objects.equals, and CONTAINS with Iterables.contains, which uses equals as well.
For BigDecimal, equals is true only when the scale matches too, so a stored 2.00 is not equal to a queried 2. MongoDB ignores scale and selects the value. The two backends therefore return different result sets for the same criterion, which is the failure a two-engine setup exists to avoid: one engine answers a query against stored data, the other answers the same query in memory against a single changed item, and the two disagree.
Scale is a property of the representation, not of the number. The practical case is a boundary value: read back from a previous page as 2.00 and fed into the same criterion, it silently drops the item in memory while the server keeps it.
The asymmetry sits inside one backend. Ordering goes through Comparable, so the very same two values are tied when sorted and unequal when filtered, with nothing in any type signature to hint at it.
Steps to reproduce:
repository.insert(generator.get().withId("id1").withValue(new BigDecimal("2.00")));
repository.insert(generator.get().withId("id2").withValue(new BigDecimal("3")));
repository.find(criteria.value.is(new BigDecimal("2"))); // empty
repository.find(criteria.value.in(new BigDecimal("2"), new BigDecimal("3.0"))); // empty
// same for an iterable attribute holding 2.00
repository.find(criteria.list.contains(new BigDecimal("2"))); // empty
Actual: empty in all three cases.
Expected: id1, id1+id2, id1 — what MongoDB returns for the same data.
Over a list of strings the two equalities agree, which is why contains in particular looks correct until a numeric element is involved.
I will deliver a pull request for this issue.