diff options
author | David Storch <david.storch@10gen.com> | 2016-08-12 15:58:56 -0400 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2016-08-18 11:14:17 -0400 |
commit | 26543060c852aac22f26143a04bf7789ec8fec53 (patch) | |
tree | df3ae49e5c4745058be29b7ec8a8e4b528b50a9a /src/mongo/bson | |
parent | 13fa28982d008568f7620d73ddec0c61fad7cbc8 (diff) | |
download | mongo-26543060c852aac22f26143a04bf7789ec8fec53.tar.gz |
SERVER-24508 BSONObj::ComparatorInterface
BSONObj instances should now be compared via the comparator
interface's evaluate() method. This preferred over using
BSONObj::woCompare() directly. If the comparison doesn't
require any database semantics (e.g. there is no collation),
there is a global instance of the SimpleBSONObjComparator
which should be used for BSONObj comparisons. If the
comparison requires special semantics, then callers must
instantiate their own comparator object.
Diffstat (limited to 'src/mongo/bson')
-rw-r--r-- | src/mongo/bson/bson_field_test.cpp | 12 | ||||
-rw-r--r-- | src/mongo/bson/bson_obj_test.cpp | 418 | ||||
-rw-r--r-- | src/mongo/bson/bsonobj.h | 68 | ||||
-rw-r--r-- | src/mongo/bson/bsonobj_comparator_interface.h | 195 | ||||
-rw-r--r-- | src/mongo/bson/bsonobjbuilder_test.cpp | 26 | ||||
-rw-r--r-- | src/mongo/bson/mutable/mutable_bson_test.cpp | 90 | ||||
-rw-r--r-- | src/mongo/bson/simple_bsonobj_comparator.cpp | 38 | ||||
-rw-r--r-- | src/mongo/bson/simple_bsonobj_comparator.h | 49 |
8 files changed, 617 insertions, 279 deletions
diff --git a/src/mongo/bson/bson_field_test.cpp b/src/mongo/bson/bson_field_test.cpp index b6fbb0fd565..0a0b2d9aec2 100644 --- a/src/mongo/bson/bson_field_test.cpp +++ b/src/mongo/bson/bson_field_test.cpp @@ -36,31 +36,31 @@ using mongo::BSONObj; TEST(Assignment, Simple) { BSONField<int> x("x"); BSONObj o = BSON(x << 5); - ASSERT_EQUALS(BSON("x" << 5), o); + ASSERT_BSONOBJ_EQ(BSON("x" << 5), o); } TEST(Make, Simple) { BSONField<int> x("x"); BSONObj o = BSON(x.make(5)); - ASSERT_EQUALS(BSON("x" << 5), o); + ASSERT_BSONOBJ_EQ(BSON("x" << 5), o); } TEST(Query, GreaterThan) { BSONField<int> x("x"); BSONObj o = BSON(x(5)); - ASSERT_EQUALS(BSON("x" << 5), o); + ASSERT_BSONOBJ_EQ(BSON("x" << 5), o); o = BSON(x.gt(5)); - ASSERT_EQUALS(BSON("x" << BSON("$gt" << 5)), o); + ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$gt" << 5)), o); } TEST(Query, NotEqual) { BSONField<int> x("x"); BSONObj o = BSON(x(10)); - ASSERT_EQUALS(BSON("x" << 10), o); + ASSERT_BSONOBJ_EQ(BSON("x" << 10), o); o = BSON(x.ne(5)); - ASSERT_EQUALS(BSON("x" << BSON("$ne" << 5)), o); + ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$ne" << 5)), o); } } // unnamed namespace diff --git a/src/mongo/bson/bson_obj_test.cpp b/src/mongo/bson/bson_obj_test.cpp index f59f74ebd82..bf38662a321 100644 --- a/src/mongo/bson/bson_obj_test.cpp +++ b/src/mongo/bson/bson_obj_test.cpp @@ -42,92 +42,94 @@ TEST(BSONObjToString, EmptyArray) { } TEST(BSONObjCompare, Timestamp) { - ASSERT_LT(BSON("" << Timestamp(0, 3)), BSON("" << Timestamp(~0U, 2))); - ASSERT_GT(BSON("" << Timestamp(2, 3)), BSON("" << Timestamp(2, 2))); - ASSERT_EQ(BSON("" << Timestamp(3ULL)), BSON("" << Timestamp(0, 3))); + ASSERT_BSONOBJ_LT(BSON("" << Timestamp(0, 3)), BSON("" << Timestamp(~0U, 2))); + ASSERT_BSONOBJ_GT(BSON("" << Timestamp(2, 3)), BSON("" << Timestamp(2, 2))); + ASSERT_BSONOBJ_EQ(BSON("" << Timestamp(3ULL)), BSON("" << Timestamp(0, 3))); } TEST(BSONObjCompare, NumberDouble) { - ASSERT_LT(BSON("" << 0.0), BSON("" << 1.0)); - ASSERT_LT(BSON("" << -1.0), BSON("" << 0.0)); - ASSERT_LT(BSON("" << -1.0), BSON("" << 1.0)); - - ASSERT_LT(BSON("" << 0.0), BSON("" << 0.1)); - ASSERT_LT(BSON("" << 0.1), BSON("" << 1.0)); - ASSERT_LT(BSON("" << -1.0), BSON("" << -0.1)); - ASSERT_LT(BSON("" << -0.1), BSON("" << 0.0)); - ASSERT_LT(BSON("" << -0.1), BSON("" << 0.1)); - - ASSERT_LT(BSON("" << 0.0), BSON("" << std::numeric_limits<double>::denorm_min())); - ASSERT_GT(BSON("" << 0.0), BSON("" << -std::numeric_limits<double>::denorm_min())); - - ASSERT_LT(BSON("" << 1.0), BSON("" << (1.0 + std::numeric_limits<double>::epsilon()))); - ASSERT_GT(BSON("" << -1.0), BSON("" << (-1.0 - std::numeric_limits<double>::epsilon()))); - - ASSERT_EQ(BSON("" << 0.0), BSON("" << -0.0)); - - ASSERT_GT(BSON("" << std::numeric_limits<double>::infinity()), BSON("" << 0.0)); - ASSERT_GT(BSON("" << std::numeric_limits<double>::infinity()), - BSON("" << std::numeric_limits<double>::max())); // max is finite - ASSERT_GT(BSON("" << std::numeric_limits<double>::infinity()), - BSON("" << -std::numeric_limits<double>::infinity())); - - ASSERT_LT(BSON("" << -std::numeric_limits<double>::infinity()), BSON("" << 0.0)); - ASSERT_LT(BSON("" << -std::numeric_limits<double>::infinity()), - BSON("" << -std::numeric_limits<double>::max())); - ASSERT_LT(BSON("" << -std::numeric_limits<double>::infinity()), - BSON("" << std::numeric_limits<double>::infinity())); - - ASSERT_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), BSON("" << 0.0)); - ASSERT_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), - BSON("" << -std::numeric_limits<double>::max())); - ASSERT_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), - BSON("" << std::numeric_limits<double>::infinity())); - ASSERT_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), - BSON("" << -std::numeric_limits<double>::infinity())); + ASSERT_BSONOBJ_LT(BSON("" << 0.0), BSON("" << 1.0)); + ASSERT_BSONOBJ_LT(BSON("" << -1.0), BSON("" << 0.0)); + ASSERT_BSONOBJ_LT(BSON("" << -1.0), BSON("" << 1.0)); + + ASSERT_BSONOBJ_LT(BSON("" << 0.0), BSON("" << 0.1)); + ASSERT_BSONOBJ_LT(BSON("" << 0.1), BSON("" << 1.0)); + ASSERT_BSONOBJ_LT(BSON("" << -1.0), BSON("" << -0.1)); + ASSERT_BSONOBJ_LT(BSON("" << -0.1), BSON("" << 0.0)); + ASSERT_BSONOBJ_LT(BSON("" << -0.1), BSON("" << 0.1)); + + ASSERT_BSONOBJ_LT(BSON("" << 0.0), BSON("" << std::numeric_limits<double>::denorm_min())); + ASSERT_BSONOBJ_GT(BSON("" << 0.0), BSON("" << -std::numeric_limits<double>::denorm_min())); + + ASSERT_BSONOBJ_LT(BSON("" << 1.0), BSON("" << (1.0 + std::numeric_limits<double>::epsilon()))); + ASSERT_BSONOBJ_GT(BSON("" << -1.0), + BSON("" << (-1.0 - std::numeric_limits<double>::epsilon()))); + + ASSERT_BSONOBJ_EQ(BSON("" << 0.0), BSON("" << -0.0)); + + ASSERT_BSONOBJ_GT(BSON("" << std::numeric_limits<double>::infinity()), BSON("" << 0.0)); + ASSERT_BSONOBJ_GT(BSON("" << std::numeric_limits<double>::infinity()), + BSON("" << std::numeric_limits<double>::max())); // max is finite + ASSERT_BSONOBJ_GT(BSON("" << std::numeric_limits<double>::infinity()), + BSON("" << -std::numeric_limits<double>::infinity())); + + ASSERT_BSONOBJ_LT(BSON("" << -std::numeric_limits<double>::infinity()), BSON("" << 0.0)); + ASSERT_BSONOBJ_LT(BSON("" << -std::numeric_limits<double>::infinity()), + BSON("" << -std::numeric_limits<double>::max())); + ASSERT_BSONOBJ_LT(BSON("" << -std::numeric_limits<double>::infinity()), + BSON("" << std::numeric_limits<double>::infinity())); + + ASSERT_BSONOBJ_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), BSON("" << 0.0)); + ASSERT_BSONOBJ_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), + BSON("" << -std::numeric_limits<double>::max())); + ASSERT_BSONOBJ_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), + BSON("" << std::numeric_limits<double>::infinity())); + ASSERT_BSONOBJ_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), + BSON("" << -std::numeric_limits<double>::infinity())); // TODO in C++11 use hex floating point to test distinct NaN representations - ASSERT_EQ(BSON("" << std::numeric_limits<double>::quiet_NaN()), - BSON("" << std::numeric_limits<double>::signaling_NaN())); + ASSERT_BSONOBJ_EQ(BSON("" << std::numeric_limits<double>::quiet_NaN()), + BSON("" << std::numeric_limits<double>::signaling_NaN())); } TEST(BSONObjCompare, NumberLong_Double) { - ASSERT_EQ(BSON("" << 0ll), BSON("" << 0.0)); - ASSERT_EQ(BSON("" << 0ll), BSON("" << -0.0)); + ASSERT_BSONOBJ_EQ(BSON("" << 0ll), BSON("" << 0.0)); + ASSERT_BSONOBJ_EQ(BSON("" << 0ll), BSON("" << -0.0)); - ASSERT_EQ(BSON("" << 1ll), BSON("" << 1.0)); - ASSERT_EQ(BSON("" << -1ll), BSON("" << -1.0)); + ASSERT_BSONOBJ_EQ(BSON("" << 1ll), BSON("" << 1.0)); + ASSERT_BSONOBJ_EQ(BSON("" << -1ll), BSON("" << -1.0)); - ASSERT_LT(BSON("" << 0ll), BSON("" << 1.0)); - ASSERT_LT(BSON("" << -1ll), BSON("" << 0.0)); - ASSERT_LT(BSON("" << -1ll), BSON("" << 1.0)); + ASSERT_BSONOBJ_LT(BSON("" << 0ll), BSON("" << 1.0)); + ASSERT_BSONOBJ_LT(BSON("" << -1ll), BSON("" << 0.0)); + ASSERT_BSONOBJ_LT(BSON("" << -1ll), BSON("" << 1.0)); - ASSERT_LT(BSON("" << 0ll), BSON("" << 0.1)); - ASSERT_LT(BSON("" << 0.1), BSON("" << 1ll)); - ASSERT_LT(BSON("" << -1ll), BSON("" << -0.1)); - ASSERT_LT(BSON("" << -0.1), BSON("" << 0ll)); + ASSERT_BSONOBJ_LT(BSON("" << 0ll), BSON("" << 0.1)); + ASSERT_BSONOBJ_LT(BSON("" << 0.1), BSON("" << 1ll)); + ASSERT_BSONOBJ_LT(BSON("" << -1ll), BSON("" << -0.1)); + ASSERT_BSONOBJ_LT(BSON("" << -0.1), BSON("" << 0ll)); - ASSERT_LT(BSON("" << 0ll), BSON("" << std::numeric_limits<double>::denorm_min())); - ASSERT_GT(BSON("" << 0ll), BSON("" << -std::numeric_limits<double>::denorm_min())); + ASSERT_BSONOBJ_LT(BSON("" << 0ll), BSON("" << std::numeric_limits<double>::denorm_min())); + ASSERT_BSONOBJ_GT(BSON("" << 0ll), BSON("" << -std::numeric_limits<double>::denorm_min())); - ASSERT_LT(BSON("" << 1ll), BSON("" << (1.0 + std::numeric_limits<double>::epsilon()))); - ASSERT_GT(BSON("" << -1ll), BSON("" << (-1.0 - std::numeric_limits<double>::epsilon()))); + ASSERT_BSONOBJ_LT(BSON("" << 1ll), BSON("" << (1.0 + std::numeric_limits<double>::epsilon()))); + ASSERT_BSONOBJ_GT(BSON("" << -1ll), + BSON("" << (-1.0 - std::numeric_limits<double>::epsilon()))); - ASSERT_GT(BSON("" << std::numeric_limits<double>::infinity()), BSON("" << 0ll)); - ASSERT_GT(BSON("" << std::numeric_limits<double>::infinity()), - BSON("" << std::numeric_limits<long long>::max())); - ASSERT_GT(BSON("" << std::numeric_limits<double>::infinity()), - BSON("" << std::numeric_limits<long long>::min())); + ASSERT_BSONOBJ_GT(BSON("" << std::numeric_limits<double>::infinity()), BSON("" << 0ll)); + ASSERT_BSONOBJ_GT(BSON("" << std::numeric_limits<double>::infinity()), + BSON("" << std::numeric_limits<long long>::max())); + ASSERT_BSONOBJ_GT(BSON("" << std::numeric_limits<double>::infinity()), + BSON("" << std::numeric_limits<long long>::min())); - ASSERT_LT(BSON("" << -std::numeric_limits<double>::infinity()), BSON("" << 0ll)); - ASSERT_LT(BSON("" << -std::numeric_limits<double>::infinity()), - BSON("" << std::numeric_limits<long long>::max())); - ASSERT_LT(BSON("" << -std::numeric_limits<double>::infinity()), - BSON("" << std::numeric_limits<long long>::min())); + ASSERT_BSONOBJ_LT(BSON("" << -std::numeric_limits<double>::infinity()), BSON("" << 0ll)); + ASSERT_BSONOBJ_LT(BSON("" << -std::numeric_limits<double>::infinity()), + BSON("" << std::numeric_limits<long long>::max())); + ASSERT_BSONOBJ_LT(BSON("" << -std::numeric_limits<double>::infinity()), + BSON("" << std::numeric_limits<long long>::min())); - ASSERT_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), BSON("" << 0ll)); - ASSERT_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), - BSON("" << std::numeric_limits<long long>::min())); + ASSERT_BSONOBJ_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), BSON("" << 0ll)); + ASSERT_BSONOBJ_LT(BSON("" << std::numeric_limits<double>::quiet_NaN()), + BSON("" << std::numeric_limits<long long>::min())); for (int powerOfTwo = 0; powerOfTwo < 63; powerOfTwo++) { const long long lNum = 1ll << powerOfTwo; @@ -136,22 +138,22 @@ TEST(BSONObjCompare, NumberLong_Double) { // All powers of two in this range can be represented exactly as doubles. invariant(lNum == static_cast<long long>(dNum)); - ASSERT_EQ(BSON("" << lNum), BSON("" << dNum)); - ASSERT_EQ(BSON("" << -lNum), BSON("" << -dNum)); + ASSERT_BSONOBJ_EQ(BSON("" << lNum), BSON("" << dNum)); + ASSERT_BSONOBJ_EQ(BSON("" << -lNum), BSON("" << -dNum)); - ASSERT_GT(BSON("" << (lNum + 1)), BSON("" << dNum)); - ASSERT_LT(BSON("" << (lNum - 1)), BSON("" << dNum)); - ASSERT_GT(BSON("" << (-lNum + 1)), BSON("" << -dNum)); - ASSERT_LT(BSON("" << (-lNum - 1)), BSON("" << -dNum)); + ASSERT_BSONOBJ_GT(BSON("" << (lNum + 1)), BSON("" << dNum)); + ASSERT_BSONOBJ_LT(BSON("" << (lNum - 1)), BSON("" << dNum)); + ASSERT_BSONOBJ_GT(BSON("" << (-lNum + 1)), BSON("" << -dNum)); + ASSERT_BSONOBJ_LT(BSON("" << (-lNum - 1)), BSON("" << -dNum)); if (powerOfTwo <= 52) { // is dNum - 0.5 representable? - ASSERT_GT(BSON("" << lNum), BSON("" << (dNum - 0.5))); - ASSERT_LT(BSON("" << -lNum), BSON("" << -(dNum - 0.5))); + ASSERT_BSONOBJ_GT(BSON("" << lNum), BSON("" << (dNum - 0.5))); + ASSERT_BSONOBJ_LT(BSON("" << -lNum), BSON("" << -(dNum - 0.5))); } if (powerOfTwo <= 51) { // is dNum + 0.5 representable? - ASSERT_LT(BSON("" << lNum), BSON("" << (dNum + 0.5))); - ASSERT_GT(BSON("" << -lNum), BSON("" << -(dNum + 0.5))); + ASSERT_BSONOBJ_LT(BSON("" << lNum), BSON("" << (dNum + 0.5))); + ASSERT_BSONOBJ_GT(BSON("" << -lNum), BSON("" << -(dNum + 0.5))); } } @@ -162,13 +164,13 @@ TEST(BSONObjCompare, NumberLong_Double) { const double closestAbove = 9223372036854775808.0; // 2**63 const double closestBelow = 9223372036854774784.0; // 2**63 - epsilon - ASSERT_GT(BSON("" << maxLL), BSON("" << (maxLL - 1))); - ASSERT_LT(BSON("" << maxLL), BSON("" << closestAbove)); - ASSERT_GT(BSON("" << maxLL), BSON("" << closestBelow)); + ASSERT_BSONOBJ_GT(BSON("" << maxLL), BSON("" << (maxLL - 1))); + ASSERT_BSONOBJ_LT(BSON("" << maxLL), BSON("" << closestAbove)); + ASSERT_BSONOBJ_GT(BSON("" << maxLL), BSON("" << closestBelow)); - ASSERT_LT(BSON("" << -maxLL), BSON("" << -(maxLL - 1))); - ASSERT_GT(BSON("" << -maxLL), BSON("" << -closestAbove)); - ASSERT_LT(BSON("" << -maxLL), BSON("" << -closestBelow)); + ASSERT_BSONOBJ_LT(BSON("" << -maxLL), BSON("" << -(maxLL - 1))); + ASSERT_BSONOBJ_GT(BSON("" << -maxLL), BSON("" << -closestAbove)); + ASSERT_BSONOBJ_LT(BSON("" << -maxLL), BSON("" << -closestBelow)); } { @@ -182,122 +184,137 @@ TEST(BSONObjCompare, NumberLong_Double) { invariant(static_cast<double>(minLL) == equal); invariant(static_cast<long long>(equal) == minLL); - ASSERT_LT(BSON("" << minLL), BSON("" << (minLL + 1))); + ASSERT_BSONOBJ_LT(BSON("" << minLL), BSON("" << (minLL + 1))); - ASSERT_EQ(BSON("" << minLL), BSON("" << equal)); - ASSERT_LT(BSON("" << minLL), BSON("" << closestAbove)); - ASSERT_GT(BSON("" << minLL), BSON("" << closestBelow)); + ASSERT_BSONOBJ_EQ(BSON("" << minLL), BSON("" << equal)); + ASSERT_BSONOBJ_LT(BSON("" << minLL), BSON("" << closestAbove)); + ASSERT_BSONOBJ_GT(BSON("" << minLL), BSON("" << closestBelow)); } } TEST(BSONObjCompare, NumberDecimalScaleAndZero) { - ASSERT_LT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128(1.0))); - ASSERT_LT(BSON("" << Decimal128(-1.0)), BSON("" << Decimal128(0.0))); - ASSERT_LT(BSON("" << Decimal128(-1.0)), BSON("" << Decimal128(1.0))); - - ASSERT_LT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128(0.1))); - ASSERT_LT(BSON("" << Decimal128(0.1)), BSON("" << Decimal128(1.0))); - ASSERT_LT(BSON("" << Decimal128(-1.0)), BSON("" << Decimal128(-0.1))); - ASSERT_LT(BSON("" << Decimal128(-0.1)), BSON("" << Decimal128(-0.0))); - ASSERT_LT(BSON("" << Decimal128(-0.1)), BSON("" << Decimal128(0.1))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128(1.0))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-1.0)), BSON("" << Decimal128(0.0))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-1.0)), BSON("" << Decimal128(1.0))); + + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128(0.1))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(0.1)), BSON("" << Decimal128(1.0))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-1.0)), BSON("" << Decimal128(-0.1))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-0.1)), BSON("" << Decimal128(-0.0))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-0.1)), BSON("" << Decimal128(0.1))); } TEST(BSONObjCompare, NumberDecimalMaxAndMins) { - ASSERT_LT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128::kSmallestPositive)); - ASSERT_GT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128::kLargestNegative)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128::kSmallestPositive)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128(0.0)), BSON("" << Decimal128::kLargestNegative)); // over 34 digits of precision so it should be equal - ASSERT_EQ(BSON("" << Decimal128(1.0)), - BSON("" << Decimal128(1.0).add(Decimal128::kSmallestPositive))); - ASSERT_EQ(BSON("" << Decimal128(0.0)), BSON("" << Decimal128(-0.0))); - - ASSERT_EQ(BSON("" << Decimal128(0)), BSON("" << Decimal128(0))); - ASSERT_EQ(BSON("" << Decimal128::kSmallestPositive), BSON("" << Decimal128::kSmallestPositive)); - ASSERT_EQ(BSON("" << Decimal128::kLargestNegative), BSON("" << Decimal128::kLargestNegative)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(1.0)), + BSON("" << Decimal128(1.0).add(Decimal128::kSmallestPositive))); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(0.0)), BSON("" << Decimal128(-0.0))); + + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(0)), BSON("" << Decimal128(0))); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kSmallestPositive), + BSON("" << Decimal128::kSmallestPositive)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kLargestNegative), + BSON("" << Decimal128::kLargestNegative)); } TEST(BSONObjCompare, NumberDecimalInfinity) { - ASSERT_GT(BSON("" << Decimal128::kPositiveInfinity), BSON("" << Decimal128(0.0))); - ASSERT_GT(BSON("" << Decimal128::kPositiveInfinity), BSON("" << Decimal128::kLargestPositive)); - ASSERT_GT(BSON("" << Decimal128::kPositiveInfinity), BSON("" << Decimal128::kNegativeInfinity)); - - ASSERT_EQ(BSON("" << Decimal128::kPositiveInfinity), BSON("" << Decimal128::kPositiveInfinity)); - ASSERT_EQ(BSON("" << Decimal128::kNegativeInfinity), BSON("" << Decimal128::kNegativeInfinity)); - - ASSERT_LT(BSON("" << Decimal128::kNegativeInfinity), BSON("" << Decimal128(0.0))); - ASSERT_LT(BSON("" << Decimal128::kNegativeInfinity), BSON("" << Decimal128::kSmallestNegative)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128::kPositiveInfinity), BSON("" << Decimal128(0.0))); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128::kPositiveInfinity), + BSON("" << Decimal128::kLargestPositive)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128::kPositiveInfinity), + BSON("" << Decimal128::kNegativeInfinity)); + + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kPositiveInfinity), + BSON("" << Decimal128::kPositiveInfinity)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kNegativeInfinity), + BSON("" << Decimal128::kNegativeInfinity)); + + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeInfinity), BSON("" << Decimal128(0.0))); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeInfinity), + BSON("" << Decimal128::kSmallestNegative)); } TEST(BSONObjCompare, NumberDecimalPosNaN) { // +/-NaN is well ordered and compares smallest, so +NaN and -NaN should behave the same - ASSERT_LT(BSON("" << Decimal128::kPositiveNaN), BSON("" << 0.0)); - ASSERT_LT(BSON("" << Decimal128::kPositiveNaN), BSON("" << Decimal128::kSmallestNegative)); - ASSERT_LT(BSON("" << Decimal128::kPositiveNaN), BSON("" << Decimal128::kPositiveInfinity)); - ASSERT_LT(BSON("" << Decimal128::kPositiveNaN), BSON("" << Decimal128::kNegativeInfinity)); - - ASSERT_EQ(BSON("" << Decimal128::kPositiveNaN), BSON("" << Decimal128::kNegativeNaN)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kPositiveNaN), BSON("" << 0.0)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kPositiveNaN), + BSON("" << Decimal128::kSmallestNegative)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kPositiveNaN), + BSON("" << Decimal128::kPositiveInfinity)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kPositiveNaN), + BSON("" << Decimal128::kNegativeInfinity)); + + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kPositiveNaN), BSON("" << Decimal128::kNegativeNaN)); } TEST(BSONObjCompare, NumberDecimalNegNan) { - ASSERT_LT(BSON("" << Decimal128::kNegativeNaN), BSON("" << 0.0)); - ASSERT_LT(BSON("" << Decimal128::kNegativeNaN), BSON("" << Decimal128::kSmallestNegative)); - ASSERT_LT(BSON("" << Decimal128::kNegativeNaN), BSON("" << Decimal128::kPositiveInfinity)); - ASSERT_LT(BSON("" << Decimal128::kNegativeNaN), BSON("" << Decimal128::kNegativeInfinity)); - - ASSERT_EQ(BSON("" << Decimal128::kNegativeNaN), BSON("" << Decimal128::kPositiveNaN)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeNaN), BSON("" << 0.0)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeNaN), + BSON("" << Decimal128::kSmallestNegative)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeNaN), + BSON("" << Decimal128::kPositiveInfinity)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeNaN), + BSON("" << Decimal128::kNegativeInfinity)); + + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kNegativeNaN), BSON("" << Decimal128::kPositiveNaN)); } TEST(BSONObjCompare, NumberDecimalCompareInt) { - ASSERT_EQ(BSON("" << Decimal128(0.0)), BSON("" << 0)); - ASSERT_EQ(BSON("" << Decimal128(502.0)), BSON("" << 502)); - ASSERT_EQ(BSON("" << Decimal128(std::numeric_limits<int>::max())), - BSON("" << std::numeric_limits<int>::max())); - ASSERT_EQ(BSON("" << Decimal128(-std::numeric_limits<int>::max())), - BSON("" << -std::numeric_limits<int>::max())); - - ASSERT_LT(BSON("" << Decimal128::kNegativeNaN), BSON("" << -std::numeric_limits<int>::max())); - ASSERT_LT(BSON("" << Decimal128::kPositiveNaN), BSON("" << -std::numeric_limits<int>::max())); - ASSERT_LT(BSON("" << Decimal128::kNegativeInfinity), - BSON("" << -std::numeric_limits<int>::max())); - ASSERT_GT(BSON("" << Decimal128::kPositiveInfinity), - BSON("" << std::numeric_limits<int>::max())); - - ASSERT_GT(BSON("" << Decimal128(1.0)), BSON("" << 0)); - ASSERT_LT(BSON("" << Decimal128(-1.0)), BSON("" << 0)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(0.0)), BSON("" << 0)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(502.0)), BSON("" << 502)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(std::numeric_limits<int>::max())), + BSON("" << std::numeric_limits<int>::max())); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(-std::numeric_limits<int>::max())), + BSON("" << -std::numeric_limits<int>::max())); + + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeNaN), + BSON("" << -std::numeric_limits<int>::max())); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kPositiveNaN), + BSON("" << -std::numeric_limits<int>::max())); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeInfinity), + BSON("" << -std::numeric_limits<int>::max())); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128::kPositiveInfinity), + BSON("" << std::numeric_limits<int>::max())); + + ASSERT_BSONOBJ_GT(BSON("" << Decimal128(1.0)), BSON("" << 0)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-1.0)), BSON("" << 0)); } TEST(BSONObjCompare, NumberDecimalCompareLong) { - ASSERT_EQ(BSON("" << Decimal128(0.0)), BSON("" << 0ll)); - ASSERT_EQ(BSON("" << Decimal128(502.0)), BSON("" << 502ll)); - ASSERT_EQ(BSON("" << Decimal128(std::numeric_limits<int64_t>::max())), - BSON("" << std::numeric_limits<long long>::max())); - ASSERT_EQ(BSON("" << Decimal128(-std::numeric_limits<int64_t>::max())), - BSON("" << -std::numeric_limits<long long>::max())); - - ASSERT_LT(BSON("" << Decimal128::kNegativeNaN), - BSON("" << -std::numeric_limits<long long>::max())); - ASSERT_LT(BSON("" << Decimal128::kPositiveNaN), - BSON("" << -std::numeric_limits<long long>::max())); - ASSERT_LT(BSON("" << Decimal128::kNegativeInfinity), - BSON("" << -std::numeric_limits<long long>::max())); - ASSERT_GT(BSON("" << Decimal128::kPositiveInfinity), - BSON("" << std::numeric_limits<long long>::max())); - - ASSERT_GT(BSON("" << Decimal128(1.0)), BSON("" << 0ll)); - ASSERT_LT(BSON("" << Decimal128(-1.0)), BSON("" << 0ll)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(0.0)), BSON("" << 0ll)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(502.0)), BSON("" << 502ll)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(std::numeric_limits<int64_t>::max())), + BSON("" << std::numeric_limits<long long>::max())); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(-std::numeric_limits<int64_t>::max())), + BSON("" << -std::numeric_limits<long long>::max())); + + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeNaN), + BSON("" << -std::numeric_limits<long long>::max())); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kPositiveNaN), + BSON("" << -std::numeric_limits<long long>::max())); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128::kNegativeInfinity), + BSON("" << -std::numeric_limits<long long>::max())); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128::kPositiveInfinity), + BSON("" << std::numeric_limits<long long>::max())); + + ASSERT_BSONOBJ_GT(BSON("" << Decimal128(1.0)), BSON("" << 0ll)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-1.0)), BSON("" << 0ll)); } TEST(BSONObjCompare, NumberDecimalCompareDoubleExactRepresentations) { - ASSERT_EQ(BSON("" << Decimal128(0.0)), BSON("" << 0.0)); - ASSERT_EQ(BSON("" << Decimal128(1.0)), BSON("" << 1.0)); - ASSERT_EQ(BSON("" << Decimal128(-1.0)), BSON("" << -1.0)); - ASSERT_EQ(BSON("" << Decimal128(0.125)), BSON("" << 0.125)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(0.0)), BSON("" << 0.0)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(1.0)), BSON("" << 1.0)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(-1.0)), BSON("" << -1.0)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128(0.125)), BSON("" << 0.125)); - ASSERT_LT(BSON("" << Decimal128(0.0)), BSON("" << 0.125)); - ASSERT_LT(BSON("" << Decimal128(-1.0)), BSON("" << -0.125)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(0.0)), BSON("" << 0.125)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(-1.0)), BSON("" << -0.125)); - ASSERT_GT(BSON("" << Decimal128(1.0)), BSON("" << 0.125)); - ASSERT_GT(BSON("" << Decimal128(0.0)), BSON("" << -0.125)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128(1.0)), BSON("" << 0.125)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128(0.0)), BSON("" << -0.125)); } TEST(BSONObjCompare, NumberDecimalCompareDoubleNoDoubleRepresentation) { @@ -307,20 +324,21 @@ TEST(BSONObjCompare, NumberDecimalCompareDoubleNoDoubleRepresentation) { // then compare equal to both double(0.10000000000000000555) and // double(0.999999999999999876). The following test cases check that // proper well ordering is applied to double and decimal comparisons. - ASSERT_GT(BSON("" << Decimal128("0.3")), BSON("" << 0.1)); - ASSERT_LT(BSON("" << Decimal128("0.1")), BSON("" << 0.3)); - ASSERT_LT(BSON("" << Decimal128("-0.3")), BSON("" << -0.1)); - ASSERT_GT(BSON("" << Decimal128("-0.1")), BSON("" << -0.3)); - ASSERT_LT(BSON("" << Decimal128("0.1")), BSON("" << 0.1)); - ASSERT_GT(BSON("" << Decimal128("0.3")), BSON("" << 0.3)); - ASSERT_GT(BSON("" << Decimal128("-0.1")), BSON("" << -0.1)); - ASSERT_LT(BSON("" << Decimal128("-0.3")), BSON("" << -0.3)); - ASSERT_EQ(BSON("" << Decimal128("0.5")), BSON("" << 0.5)); - ASSERT_GT(BSON("" << Decimal128("0.5000000000000000000000000000000001")), BSON("" << 0.5)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128("0.3")), BSON("" << 0.1)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128("0.1")), BSON("" << 0.3)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128("-0.3")), BSON("" << -0.1)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128("-0.1")), BSON("" << -0.3)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128("0.1")), BSON("" << 0.1)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128("0.3")), BSON("" << 0.3)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128("-0.1")), BSON("" << -0.1)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128("-0.3")), BSON("" << -0.3)); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128("0.5")), BSON("" << 0.5)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128("0.5000000000000000000000000000000001")), + BSON("" << 0.5)); // Double 0.1 should compare well against significantly different decimals - ASSERT_LT(BSON("" << Decimal128(0.0)), BSON("" << 0.1)); - ASSERT_GT(BSON("" << Decimal128(1.0)), BSON("" << 0.1)); + ASSERT_BSONOBJ_LT(BSON("" << Decimal128(0.0)), BSON("" << 0.1)); + ASSERT_BSONOBJ_GT(BSON("" << Decimal128(1.0)), BSON("" << 0.1)); } TEST(BSONObjCompare, NumberDecimalCompareDoubleQuantize) { @@ -332,29 +350,29 @@ TEST(BSONObjCompare, NumberDecimalCompareDoubleQuantize) { Decimal128 roundedDoubleLargestNegValue("-179769313486232E294"); Decimal128 roundedDoubleOneAboveSmallestNegValue("-179769313486231E294"); - ASSERT_EQ(BSON("" << roundedDoubleLargestPosValue), - BSON("" << Decimal128(std::numeric_limits<double>::max()))); - ASSERT_EQ(BSON("" << roundedDoubleLargestNegValue), - BSON("" << Decimal128(-std::numeric_limits<double>::max()))); + ASSERT_BSONOBJ_EQ(BSON("" << roundedDoubleLargestPosValue), + BSON("" << Decimal128(std::numeric_limits<double>::max()))); + ASSERT_BSONOBJ_EQ(BSON("" << roundedDoubleLargestNegValue), + BSON("" << Decimal128(-std::numeric_limits<double>::max()))); - ASSERT_GT(BSON("" << roundedDoubleOneAboveLargestPosValue), - BSON("" << Decimal128(std::numeric_limits<double>::max()))); - ASSERT_LT(BSON("" << roundedDoubleOneAboveSmallestNegValue), - BSON("" << Decimal128(-std::numeric_limits<double>::min()))); + ASSERT_BSONOBJ_GT(BSON("" << roundedDoubleOneAboveLargestPosValue), + BSON("" << Decimal128(std::numeric_limits<double>::max()))); + ASSERT_BSONOBJ_LT(BSON("" << roundedDoubleOneAboveSmallestNegValue), + BSON("" << Decimal128(-std::numeric_limits<double>::min()))); } TEST(BSONObjCompare, NumberDecimalCompareDoubleInfinity) { - ASSERT_EQ(BSON("" << Decimal128::kPositiveInfinity), - BSON("" << std::numeric_limits<double>::infinity())); - ASSERT_EQ(BSON("" << Decimal128::kNegativeInfinity), - BSON("" << -std::numeric_limits<double>::infinity())); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kPositiveInfinity), + BSON("" << std::numeric_limits<double>::infinity())); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kNegativeInfinity), + BSON("" << -std::numeric_limits<double>::infinity())); } TEST(BSONObjCompare, NumberDecimalCompareDoubleNaN) { - ASSERT_EQ(BSON("" << Decimal128::kPositiveNaN), - BSON("" << std::numeric_limits<double>::quiet_NaN())); - ASSERT_EQ(BSON("" << Decimal128::kNegativeNaN), - BSON("" << -std::numeric_limits<double>::quiet_NaN())); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kPositiveNaN), + BSON("" << std::numeric_limits<double>::quiet_NaN())); + ASSERT_BSONOBJ_EQ(BSON("" << Decimal128::kNegativeNaN), + BSON("" << -std::numeric_limits<double>::quiet_NaN())); } TEST(BSONObjCompare, StringSymbol) { @@ -552,7 +570,7 @@ TEST(BSONObj, ShareOwnershipWith) { // Now that tmp is out of scope, if obj didn't retain ownership, it would be accessing free'd // memory which should error on ASAN and debug builds. ASSERT(obj.isOwned()); - ASSERT_EQ(obj, BSON("a" << 1)); + ASSERT_BSONOBJ_EQ(obj, BSON("a" << 1)); } } // unnamed namespace diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h index fa405e9f299..726a5f43be9 100644 --- a/src/mongo/bson/bsonobj.h +++ b/src/mongo/bson/bsonobj.h @@ -95,6 +95,31 @@ typedef std::multiset<BSONElement, BSONElementCmpWithoutField> BSONElementMSet; */ class BSONObj { public: + // Declared in bsonobj_comparator_interface.h. + class ComparatorInterface; + + /** + * Operator overloads for relops return a DeferredComparison which can subsequently be evaluated + * by a BSONObj::ComparatorInterface. + */ + struct DeferredComparison { + enum class Type { + kLT, + kLTE, + kEQ, + kGT, + kGTE, + kNE, + }; + + DeferredComparison(Type type, const BSONObj& lhs, const BSONObj& rhs) + : type(type), lhs(lhs), rhs(rhs) {} + + Type type; + const BSONObj& lhs; + const BSONObj& rhs; + }; + static const char kMinBSONLength = 5; /** Construct an empty BSONObj -- that is, {}. */ @@ -394,6 +419,15 @@ public: /** Alternative output format */ std::string hexDump() const; + // + // Comparison API. + // + // BSONObj instances can be compared either using woCompare() or via operator overloads. Most + // callers should prefer operator overloads. Note that the operator overloads return a + // DeferredComparison, which must be subsequently evaluated by a BSONObj::ComparatorInterface. + // See bsonobj_comparator_interface.h for details. + // + /**wo='well ordered'. fields must be in same order in each object. Ordering is with respect to the signs of the elements and allows ascending / descending key mixing. @@ -416,17 +450,28 @@ public: bool considerFieldName = true, const StringData::ComparatorInterface* comparator = nullptr) const; - bool operator<(const BSONObj& other) const { - return woCompare(other) < 0; + DeferredComparison operator<(const BSONObj& other) const { + return DeferredComparison(DeferredComparison::Type::kLT, *this, other); + } + + DeferredComparison operator<=(const BSONObj& other) const { + return DeferredComparison(DeferredComparison::Type::kLTE, *this, other); } - bool operator<=(const BSONObj& other) const { - return woCompare(other) <= 0; + + DeferredComparison operator>(const BSONObj& other) const { + return DeferredComparison(DeferredComparison::Type::kGT, *this, other); + } + + DeferredComparison operator>=(const BSONObj& other) const { + return DeferredComparison(DeferredComparison::Type::kGTE, *this, other); } - bool operator>(const BSONObj& other) const { - return woCompare(other) > 0; + + DeferredComparison operator==(const BSONObj& other) const { + return DeferredComparison(DeferredComparison::Type::kEQ, *this, other); } - bool operator>=(const BSONObj& other) const { - return woCompare(other) >= 0; + + DeferredComparison operator!=(const BSONObj& other) const { + return DeferredComparison(DeferredComparison::Type::kNE, *this, other); } bool equal(const BSONObj& r) const; @@ -505,13 +550,6 @@ public: /** true unless corrupt */ bool valid() const; - bool operator==(const BSONObj& other) const { - return equal(other); - } - bool operator!=(const BSONObj& other) const { - return !operator==(other); - } - enum MatchType { Equality = 0, LT = 0x1, diff --git a/src/mongo/bson/bsonobj_comparator_interface.h b/src/mongo/bson/bsonobj_comparator_interface.h new file mode 100644 index 00000000000..56fa736dab0 --- /dev/null +++ b/src/mongo/bson/bsonobj_comparator_interface.h @@ -0,0 +1,195 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include <map> +#include <set> +#include <unordered_map> +#include <unordered_set> + +#include "mongo/base/disallow_copying.h" +#include "mongo/bson/bsonobj.h" +#include "mongo/util/assert_util.h" + +namespace mongo { + +/** + * A BSONObj::ComparatorInterface is an abstract class for comparing BSONObj objects. Usage for + * comparing two BSON objects, 'lhs' and 'rhs', where 'comparator' is an instance of a class + * implementing this interface, is as shown below: + * + * bool lessThan = comparator.evaluate(lhs < rhs); + * bool lessThanOrEqual = comparator.evaluate(lhs <= rhs); + * bool equal = comparator.evaluate(lhs == rhs); + * bool greaterThanOrEqual = comparator.evaluate(lhs >= rhs); + * bool greaterThan = comparator.evaluate(lhs > rhs); + * bool notEqual = comparator.evaluate(lhs != rhs); + * + * Can also be used to obtain function objects compatible for use with standard library algorithms + * such as std::sort, and to construct STL sets and maps which respect this comparator. + * + * All methods are thread-safe. + */ +class BSONObj::ComparatorInterface { + MONGO_DISALLOW_COPYING(ComparatorInterface); + +public: + /** + * Functor compatible for use with ordered STL containers. + */ + class LessThan { + public: + explicit LessThan(const ComparatorInterface* comparator) : _comparator(comparator) {} + + bool operator()(const BSONObj& lhs, const BSONObj& rhs) const { + return _comparator->compare(lhs, rhs) < 0; + } + + private: + const ComparatorInterface* _comparator; + }; + + /** + * Functor compatible for use with unordered STL containers. + */ + class EqualTo { + public: + explicit EqualTo(const ComparatorInterface* comparator) : _comparator(comparator) {} + + bool operator()(const BSONObj& lhs, const BSONObj& rhs) const { + return _comparator->compare(lhs, rhs) == 0; + } + + private: + const ComparatorInterface* _comparator; + }; + + using BSONObjSet = std::set<BSONObj, BSONObj::ComparatorInterface::LessThan>; + + // TODO SERVER-23990: Make the BSONObj hash collation-aware. + using BSONObjUnorderedSet = + std::unordered_set<BSONObj, BSONObj::Hasher, BSONObj::ComparatorInterface::EqualTo>; + + template <typename T> + using BSONObjMap = std::map<BSONObj, T, BSONObj::ComparatorInterface::LessThan>; + + // TODO SERVER-23990: Make the BSONObj hash collation-aware. + template <typename T> + using BSONObjIndexedMap = + std::unordered_map<BSONObj, T, BSONObj::Hasher, BSONObj::ComparatorInterface::EqualTo>; + + virtual ~ComparatorInterface() = default; + + /** + * Compares two BSONObj objects. Returns <0, 0, >0 if 'lhs' < 'rhs', 'lhs' == 'rhs', or 'lhs' > + * 'rhs' respectively. + */ + virtual int compare(const BSONObj& lhs, const BSONObj& rhs) const = 0; + + /** + * Evaluates a deferred comparison object generated by invocation of one of the BSONObj operator + * overloads for relops. + */ + bool evaluate(BSONObj::DeferredComparison deferredComparison) const { + int cmp = compare(deferredComparison.lhs, deferredComparison.rhs); + switch (deferredComparison.type) { + case BSONObj::DeferredComparison::Type::kLT: + return cmp < 0; + case BSONObj::DeferredComparison::Type::kLTE: + return cmp <= 0; + case BSONObj::DeferredComparison::Type::kEQ: + return cmp == 0; + case BSONObj::DeferredComparison::Type::kGT: + return cmp > 0; + case BSONObj::DeferredComparison::Type::kGTE: + return cmp >= 0; + case BSONObj::DeferredComparison::Type::kNE: + return cmp != 0; + } + + MONGO_UNREACHABLE; + } + + /** + * Returns a function object which computes whether one BSONObj is less than another under this + * comparator. This comparator must outlive the returned function object. + */ + LessThan makeLessThan() const { + return LessThan(this); + } + + /** + * Returns a function object which computes whether one BSONObj is equal to another under this + * comparator. This comparator must outlive the returned function object. + */ + EqualTo makeEqualTo() const { + return EqualTo(this); + } + + /** + * Construct an empty BSONObjSet whose ordering is given by this comparator. This comparator + * must outlive the returned set. + */ + BSONObjSet makeOrderedBSONObjSet() const { + return BSONObjSet(LessThan(this)); + } + + /** + * Construct an empty BSONObjUnorderedSet whose equivalence classes are given by this + * comparator. This comparator must outlive the returned set. + */ + BSONObjUnorderedSet makeUnorderedBSONObjSet() const { + // TODO SERVER-23990: Make the BSONObj hash collation-aware. + return BSONObjUnorderedSet(0, BSONObj::Hasher(), EqualTo(this)); + } + + /** + * Construct an empty ordered map from BSONObj to type T whose ordering is given by this + * comparator. This comparator must outlive the returned map. + */ + template <typename T> + BSONObjMap<T> makeOrderedBSONObjMap() const { + return BSONObjMap<T>(LessThan(this)); + } + + /** + * Construct an empty unordered map from BSONObj to type T whose ordering is given by this + * comparator. This comparator must outlive the returned map. + */ + template <typename T> + BSONObjIndexedMap<T> makeBSONObjIndexedMap() const { + // TODO SERVER-23990: Make the BSONObj hash collation-aware. + return BSONObjIndexedMap<T>(0, BSONObj::Hasher(), EqualTo(this)); + } + +protected: + constexpr ComparatorInterface() = default; +}; + +} // namespace mongo diff --git a/src/mongo/bson/bsonobjbuilder_test.cpp b/src/mongo/bson/bsonobjbuilder_test.cpp index 4f11df260bb..003f40c27dd 100644 --- a/src/mongo/bson/bsonobjbuilder_test.cpp +++ b/src/mongo/bson/bsonobjbuilder_test.cpp @@ -259,7 +259,7 @@ TEST(BSONObjBuilderTest, AppendNumberLongLongMinCompareObject) { BSONObj o2 = BSON("a" << std::numeric_limits<long long>::min()); - ASSERT_EQUALS(o1, o2); + ASSERT_BSONOBJ_EQ(o1, o2); } TEST(BSONObjBuilderTest, AppendMaxTimestampConversion) { @@ -285,11 +285,11 @@ TEST(BSONObjBuilderTest, ResumeBuilding) { secondBuilder.append("c", "d"); } auto obj = BSONObj(b.buf()); - ASSERT_EQ(obj, - BSON("a" - << "b" - << "c" - << "d")); + ASSERT_BSONOBJ_EQ(obj, + BSON("a" + << "b" + << "c" + << "d")); } TEST(BSONObjBuilderTest, ResumeBuildingWithNesting) { @@ -307,18 +307,18 @@ TEST(BSONObjBuilderTest, ResumeBuildingWithNesting) { secondBuilder.append("a", BSON("c" << 3)); } auto obj = BSONObj(b.buf()); - ASSERT_EQ(obj, - BSON("ll" << BSON("f" << BSON("cc" - << "dd")) - << "a" - << BSON("c" << 3))); + ASSERT_BSONOBJ_EQ(obj, + BSON("ll" << BSON("f" << BSON("cc" + << "dd")) + << "a" + << BSON("c" << 3))); } TEST(BSONObjBuilderTest, ResetToEmptyResultsInEmptyObj) { BSONObjBuilder bob; bob.append("a", 3); bob.resetToEmpty(); - ASSERT_EQ(BSONObj(), bob.obj()); + ASSERT_BSONOBJ_EQ(BSONObj(), bob.obj()); } TEST(BSONObjBuilderTest, ResetToEmptyForNestedBuilderOnlyResetsInnerObj) { @@ -328,7 +328,7 @@ TEST(BSONObjBuilderTest, ResetToEmptyForNestedBuilderOnlyResetsInnerObj) { innerObj.append("b", 4); innerObj.resetToEmpty(); innerObj.done(); - ASSERT_EQ(BSON("a" << 3 << "nestedObj" << BSONObj()), bob.obj()); + ASSERT_BSONOBJ_EQ(BSON("a" << 3 << "nestedObj" << BSONObj()), bob.obj()); } } // unnamed namespace diff --git a/src/mongo/bson/mutable/mutable_bson_test.cpp b/src/mongo/bson/mutable/mutable_bson_test.cpp index 24629617926..d1584ff9714 100644 --- a/src/mongo/bson/mutable/mutable_bson_test.cpp +++ b/src/mongo/bson/mutable/mutable_bson_test.cpp @@ -807,13 +807,13 @@ TEST(Serialization, RoundTrip) { obj = mongo::fromjson(jsonSampleWithDecimal); mmb::Document doc(obj.copy()); mongo::BSONObj built = doc.getObject(); - ASSERT_EQUALS(obj, built); + ASSERT_BSONOBJ_EQ(obj, built); } TEST(Documentation, Example1) { // Create a new document mmb::Document doc; - ASSERT_EQUALS(mongo::fromjson("{}"), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{}"), doc.getObject()); // Get the root of the document. mmb::Element root = doc.root(); @@ -822,41 +822,41 @@ TEST(Documentation, Example1) { // everything, then push that Element into the root object, making it a child of root. mmb::Element e0 = doc.makeElementInt("ltuae", 42); ASSERT_OK(root.pushBack(e0)); - ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42 }"), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ ltuae : 42 }"), doc.getObject()); // Create a new empty mongo::Object-typed Element named 'magic', and push it back as a // child of the root, making it a sibling of e0. mmb::Element e1 = doc.makeElementObject("magic"); ASSERT_OK(root.pushBack(e1)); - ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, magic : {} }"), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ ltuae : 42, magic : {} }"), doc.getObject()); // Create a new mongo::NumberDouble typed Element to represent Pi, and insert it as child // of the new object we just created. mmb::Element e3 = doc.makeElementDouble("pi", 3.14); ASSERT_OK(e1.pushBack(e3)); - ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, magic : { pi : 3.14 } }"), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ ltuae : 42, magic : { pi : 3.14 } }"), doc.getObject()); // Create a new mongo::NumberDouble to represent Plancks constant in electrovolt // micrometers, and add it as a child of the 'magic' object. mmb::Element e4 = doc.makeElementDouble("hbar", 1.239); ASSERT_OK(e1.pushBack(e4)); - ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, magic : { pi : 3.14, hbar : 1.239 } }"), - doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ ltuae : 42, magic : { pi : 3.14, hbar : 1.239 } }"), + doc.getObject()); // Rename the parent element of 'hbar' to be 'constants'. ASSERT_OK(e4.parent().rename("constants")); - ASSERT_EQUALS(mongo::fromjson("{ ltuae : 42, constants : { pi : 3.14, hbar : 1.239 } }"), - doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ ltuae : 42, constants : { pi : 3.14, hbar : 1.239 } }"), + doc.getObject()); // Rename 'ltuae' to 'answer' by accessing it as the root objects left child. ASSERT_OK(doc.root().leftChild().rename("answer")); - ASSERT_EQUALS(mongo::fromjson("{ answer : 42, constants : { pi : 3.14, hbar : 1.239 } }"), - doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ answer : 42, constants : { pi : 3.14, hbar : 1.239 } }"), + doc.getObject()); // Sort the constants by name. mmb::sortChildren(doc.root().rightChild(), mmb::FieldNameLessThan()); - ASSERT_EQUALS(mongo::fromjson("{ answer : 42, constants : { hbar : 1.239, pi : 3.14 } }"), - doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ answer : 42, constants : { hbar : 1.239, pi : 3.14 } }"), + doc.getObject()); } TEST(Documentation, Example2) { @@ -919,7 +919,7 @@ TEST(Documentation, Example2) { mongo::BSONObjBuilder builder; doc.writeTo(&builder); - ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), doc.getObject()); } namespace { @@ -943,7 +943,7 @@ TEST(Documentation, Example2InPlaceWithDamageVector) { // Make the object, and make a copy for reference. mongo::BSONObj obj = mongo::fromjson(inJson); const mongo::BSONObj copyOfObj = obj.getOwned(); - ASSERT_EQUALS(obj, copyOfObj); + ASSERT_BSONOBJ_EQ(obj, copyOfObj); // Create a new document representing BSONObj with the above contents. mmb::Document doc(obj, mmb::Document::kInPlaceEnabled); @@ -984,7 +984,7 @@ TEST(Documentation, Example2InPlaceWithDamageVector) { // Demonstrate that while the document has changed, the underlying BSONObj has not yet // changed. ASSERT_FALSE(obj == doc); - ASSERT_EQUALS(copyOfObj, obj); + ASSERT_BSONOBJ_EQ(copyOfObj, obj); // Ensure that in-place updates are still enabled. ASSERT_EQUALS(mmb::Document::kInPlaceEnabled, doc.getCurrentInPlaceMode()); @@ -1011,7 +1011,7 @@ TEST(Documentation, Example2InPlaceWithDamageVector) { mongo::BSONObjBuilder builder; doc.writeTo(&builder); - ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), doc.getObject()); } TEST(Documentation, Example3) { @@ -1041,7 +1041,7 @@ TEST(Documentation, Example3) { " 'xs': { 'x' : 'x', 'X' : 'X' }," " 'ys': { 'y' : 'y', 'Y' : 'Y', 'why' : ['not'] }" "}"; - ASSERT_EQUALS(mongo::fromjson(outJson), outObj); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), outObj); } TEST(Document, LifecycleConstructDefault) { @@ -1159,7 +1159,7 @@ TEST(Document, RenameDeserialization) { "{" " 'a' : { 'b' : { 'C' : { 'd' : 4 } } }" "}"; - ASSERT_EQUALS(mongo::fromjson(outJson), outObj); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), outObj); } TEST(Document, CantRenameRootElement) { @@ -1189,7 +1189,7 @@ TEST(Document, RemoveElementWithOpaqueRightSibling) { " 'b' : 2, 'c' : 3" "}"; mongo::BSONObj outObj = doc.getObject(); - ASSERT_EQUALS(mongo::fromjson(outJson), outObj); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), outObj); } TEST(Document, AddRightSiblingToElementWithOpaqueRightSibling) { @@ -1214,7 +1214,7 @@ TEST(Document, AddRightSiblingToElementWithOpaqueRightSibling) { " 'a' : 1, 'X' : 'X', 'b' : 2, 'c' : 3" "}"; mongo::BSONObj outObj = doc.getObject(); - ASSERT_EQUALS(mongo::fromjson(outJson), outObj); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), outObj); } TEST(Document, ArrayIndexedAccessFromJson) { @@ -1422,7 +1422,7 @@ TEST(Document, ArraySerialization) { "}"; const mongo::BSONObj outObj = doc.getObject(); - ASSERT_EQUALS(mongo::fromjson(outJson), outObj); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), outObj); } TEST(Document, SetValueBSONElementFieldNameHandling) { @@ -1441,7 +1441,7 @@ TEST(Document, SetValueBSONElementFieldNameHandling) { a.setValueBSONElement(b); static const char outJson[] = "{ a : 5 }"; - ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), doc.getObject()); } TEST(Document, SetValueElementFromSeparateDocument) { @@ -1455,10 +1455,10 @@ TEST(Document, SetValueElementFromSeparateDocument) { auto setFrom = doc2.root().leftChild(); ASSERT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(mongo::fromjson("{ a : 5 }"), doc1.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ a : 5 }"), doc1.getObject()); // Doc containing the 'setFrom' element should be unchanged. - ASSERT_EQ(inObj2, doc2.getObject()); + ASSERT_BSONOBJ_EQ(inObj2, doc2.getObject()); } TEST(Document, SetValueElementIsNoopWhenSetToSelf) { @@ -1468,7 +1468,7 @@ TEST(Document, SetValueElementIsNoopWhenSetToSelf) { auto element = doc.root().leftChild(); ASSERT_OK(element.setValueElement(element)); - ASSERT_EQ(inObj, doc.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc.getObject()); } TEST(Document, SetValueElementIsNoopWhenSetToSelfFromCopy) { @@ -1479,7 +1479,7 @@ TEST(Document, SetValueElementIsNoopWhenSetToSelfFromCopy) { auto elementCopy = element; ASSERT_OK(element.setValueElement(elementCopy)); - ASSERT_EQ(inObj, doc.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc.getObject()); } TEST(Document, SetValueElementIsNoopWhenSetToSelfNonRootElement) { @@ -1490,7 +1490,7 @@ TEST(Document, SetValueElementIsNoopWhenSetToSelfNonRootElement) { ASSERT_EQ("c", element.getFieldName()); ASSERT_OK(element.setValueElement(element)); - ASSERT_EQ(inObj, doc.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc.getObject()); } TEST(Document, SetValueElementSetToNestedObject) { @@ -1504,10 +1504,10 @@ TEST(Document, SetValueElementSetToNestedObject) { auto setFrom = doc2.root().leftChild(); ASSERT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(mongo::fromjson("{ a : { c : 5, d : 6 } }"), doc1.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ a : { c : 5, d : 6 } }"), doc1.getObject()); // Doc containing the 'setFrom' element should be unchanged. - ASSERT_EQ(inObj2, doc2.getObject()); + ASSERT_BSONOBJ_EQ(inObj2, doc2.getObject()); } TEST(Document, SetValueElementNonRootElements) { @@ -1523,10 +1523,10 @@ TEST(Document, SetValueElementNonRootElements) { ASSERT_EQ("e", setFrom.getFieldName()); ASSERT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(mongo::fromjson("{ a : { b : 5, c : 8 } }"), doc1.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ a : { b : 5, c : 8 } }"), doc1.getObject()); // Doc containing the 'setFrom' element should be unchanged. - ASSERT_EQ(inObj2, doc2.getObject()); + ASSERT_BSONOBJ_EQ(inObj2, doc2.getObject()); } TEST(Document, SetValueElementSetRootToSelfErrors) { @@ -1535,7 +1535,7 @@ TEST(Document, SetValueElementSetRootToSelfErrors) { auto element = doc.root(); ASSERT_NOT_OK(element.setValueElement(element)); - ASSERT_EQ(inObj, doc.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc.getObject()); } TEST(Document, SetValueElementSetRootToAnotherDocRootErrors) { @@ -1549,8 +1549,8 @@ TEST(Document, SetValueElementSetRootToAnotherDocRootErrors) { auto setFrom = doc2.root(); ASSERT_NOT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(inObj, doc1.getObject()); - ASSERT_EQ(inObj2, doc2.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc1.getObject()); + ASSERT_BSONOBJ_EQ(inObj2, doc2.getObject()); } TEST(Document, SetValueElementSetRootToNotRootInSelfErrors) { @@ -1560,7 +1560,7 @@ TEST(Document, SetValueElementSetRootToNotRootInSelfErrors) { auto setTo = doc.root(); auto setFrom = doc.root().leftChild(); ASSERT_NOT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(inObj, doc.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc.getObject()); } TEST(Document, SetValueElementSetRootToNotRootInAnotherDocErrors) { @@ -1574,8 +1574,8 @@ TEST(Document, SetValueElementSetRootToNotRootInAnotherDocErrors) { auto setFrom = doc2.root().leftChild(); ASSERT_NOT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(inObj, doc1.getObject()); - ASSERT_EQ(inObj2, doc2.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc1.getObject()); + ASSERT_BSONOBJ_EQ(inObj2, doc2.getObject()); } TEST(Document, SetValueElementSetToOwnRootErrors) { @@ -1587,7 +1587,7 @@ TEST(Document, SetValueElementSetToOwnRootErrors) { auto setFrom = doc.root(); ASSERT_NOT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(inObj, doc.getObject()); + ASSERT_BSONOBJ_EQ(inObj, doc.getObject()); } TEST(Document, SetValueElementSetToOtherDocRoot) { @@ -1602,8 +1602,8 @@ TEST(Document, SetValueElementSetToOtherDocRoot) { auto setFrom = doc2.root(); ASSERT_OK(setTo.setValueElement(setFrom)); - ASSERT_EQ(mongo::fromjson("{ a : { b : { c : 5 } } }"), doc1.getObject()); - ASSERT_EQ(inObj2, doc2.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson("{ a : { b : { c : 5 } } }"), doc1.getObject()); + ASSERT_BSONOBJ_EQ(inObj2, doc2.getObject()); } TEST(Document, CreateElementWithEmptyFieldName) { @@ -1921,7 +1921,7 @@ TEST(TypeSupport, EncodingEquivalenceObject) { ASSERT_TRUE(a.ok()); ASSERT_EQUALS(a.getType(), mongo::Object); ASSERT_TRUE(a.hasValue()); - ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueObject()); + ASSERT_BSONOBJ_EQ(value1, mmb::ConstElement(a).getValueObject()); // Construct via call passing BSON element ASSERT_OK(doc.root().appendElement(thing)); @@ -1962,7 +1962,7 @@ TEST(TypeSupport, EncodingEquivalenceArray) { ASSERT_TRUE(a.ok()); ASSERT_EQUALS(a.getType(), mongo::Array); ASSERT_TRUE(a.hasValue()); - ASSERT_EQUALS(value1, mmb::ConstElement(a).getValueArray()); + ASSERT_BSONOBJ_EQ(value1, mmb::ConstElement(a).getValueArray()); // Construct via call passing BSON element ASSERT_OK(doc.root().appendElement(thing)); @@ -2714,7 +2714,7 @@ TEST(Document, ManipulateComplexObjInLeafHeap) { static const char outJson[] = "{ embedded: { a: 1, b: 2, c: 2.0, d : ['w', 'y', 'z'] }, free: {} }"; - ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), doc.getObject()); } TEST(DocumentInPlace, EphemeralDocumentsDoNotUseInPlaceMode) { @@ -2889,7 +2889,7 @@ TEST(DocumentInPlace, DisablingInPlaceDoesNotDiscardUpdates) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); static const char outJson[] = "{ foo : true, bar : false, baz : 'baz' }"; - ASSERT_EQUALS(mongo::fromjson(outJson), doc.getObject()); + ASSERT_BSONOBJ_EQ(mongo::fromjson(outJson), doc.getObject()); } TEST(DocumentInPlace, StringLifecycle) { diff --git a/src/mongo/bson/simple_bsonobj_comparator.cpp b/src/mongo/bson/simple_bsonobj_comparator.cpp new file mode 100644 index 00000000000..38b04e70034 --- /dev/null +++ b/src/mongo/bson/simple_bsonobj_comparator.cpp @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + + +#include "mongo/platform/basic.h" + +#include "mongo/bson/simple_bsonobj_comparator.h" + +namespace mongo { + +const SimpleBSONObjComparator SimpleBSONObjComparator::kInstance{}; + +} // namespace mongo diff --git a/src/mongo/bson/simple_bsonobj_comparator.h b/src/mongo/bson/simple_bsonobj_comparator.h new file mode 100644 index 00000000000..76d4070f059 --- /dev/null +++ b/src/mongo/bson/simple_bsonobj_comparator.h @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#pragma once + +#include "mongo/bson/bsonobj_comparator_interface.h" + +namespace mongo { + +/** + * A BSONObj comparator that has simple binary compare semantics. + */ +class SimpleBSONObjComparator final : public BSONObj::ComparatorInterface { +public: + // Global simple comparator for stateless BSONObj comparisons. BSONObj comparisons that require + // database logic, such as collations, much instantiate their own comparator. + static const SimpleBSONObjComparator kInstance; + + int compare(const BSONObj& lhs, const BSONObj& rhs) const final { + return lhs.woCompare(rhs, BSONObj(), true, nullptr); + } +}; + +} // namespace mongo |