/** * Copyright (C) 2015 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 . * * 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/bsonobj.h" #include "mongo/bson/simple_bsonobj_comparator.h" #include "mongo/db/index/index_access_method.h" #include "mongo/db/json.h" #include "mongo/unittest/unittest.h" namespace mongo { namespace { using std::vector; TEST(IndexAccessMethodSetDifference, EmptyInputsShouldHaveNoDifference) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet(); BSONObjSet right = bsonCmp.makeBSONObjSet(); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQ(0UL, diff.first.size()); ASSERT_EQ(0UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, EmptyLeftShouldHaveNoDifference) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet(); BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 0)}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQ(0UL, diff.first.size()); ASSERT_EQ(1UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, EmptyRightShouldReturnAllOfLeft) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << 1)}); BSONObjSet right = bsonCmp.makeBSONObjSet(); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQ(2UL, diff.first.size()); ASSERT_EQ(0UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, IdenticalSetsShouldHaveNoDifference) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << "string"), BSON("" << BSONNULL)}); BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << "string"), BSON("" << BSONNULL)}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQ(0UL, diff.first.size()); ASSERT_EQ(0UL, diff.second.size()); } // // Number type comparisons. // void assertDistinct(BSONObj left, BSONObj right) { SimpleBSONObjComparator bsonCmp; BSONObjSet leftSet = bsonCmp.makeBSONObjSet({left}); BSONObjSet rightSet = bsonCmp.makeBSONObjSet({right}); auto diff = IndexAccessMethod::setDifference(leftSet, rightSet); ASSERT_EQ(1UL, diff.first.size()); ASSERT_EQ(1UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, ZerosOfDifferentTypesAreNotEquivalent) { const BSONObj intObj = BSON("" << static_cast(0)); const BSONObj longObj = BSON("" << static_cast(0)); const BSONObj doubleObj = BSON("" << static_cast(0.0)); // These should compare equal with woCompare(), but should not be treated equal by the index. ASSERT_EQ(0, intObj.woCompare(longObj)); ASSERT_EQ(0, longObj.woCompare(doubleObj)); assertDistinct(intObj, longObj); assertDistinct(intObj, doubleObj); assertDistinct(longObj, intObj); assertDistinct(longObj, doubleObj); assertDistinct(doubleObj, intObj); assertDistinct(doubleObj, longObj); const BSONObj decimalObj = fromjson("{'': NumberDecimal('0')}"); ASSERT_EQ(0, doubleObj.woCompare(decimalObj)); assertDistinct(intObj, decimalObj); assertDistinct(longObj, decimalObj); assertDistinct(doubleObj, decimalObj); assertDistinct(decimalObj, intObj); assertDistinct(decimalObj, longObj); assertDistinct(decimalObj, doubleObj); } TEST(IndexAccessMethodSetDifference, ShouldDetectOneDifferenceAmongManySimilarities) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << "string"), BSON("" << BSONNULL), BSON("" << static_cast(1)), // This is different. BSON("" << BSON("sub" << "document")), BSON("" << BSON_ARRAY(1 << "hi" << 42))}); BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << "string"), BSON("" << BSONNULL), BSON("" << static_cast(1.0)), // This is different. BSON("" << BSON("sub" << "document")), BSON("" << BSON_ARRAY(1 << "hi" << 42))}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQUALS(1UL, diff.first.size()); ASSERT_EQUALS(1UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, SingleObjInLeftShouldFindCorrespondingObjInRight) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 2)}); BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQUALS(0UL, diff.first.size()); ASSERT_EQUALS(2UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, SingleObjInRightShouldFindCorrespondingObjInLeft) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 2)}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQUALS(2UL, diff.first.size()); ASSERT_EQUALS(0UL, diff.second.size()); } TEST(IndexAccessMethodSetDifference, LeftSetAllSmallerThanRightShouldBeDisjoint) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 4), BSON("" << 5), BSON("" << 6)}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQUALS(3UL, diff.first.size()); ASSERT_EQUALS(3UL, diff.second.size()); for (auto&& obj : diff.first) { ASSERT(left.find(obj) != left.end()); } for (auto&& obj : diff.second) { ASSERT(right.find(obj) != right.end()); } } TEST(IndexAccessMethodSetDifference, LeftSetAllLargerThanRightShouldBeDisjoint) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 4), BSON("" << 5), BSON("" << 6)}); BSONObjSet right = bsonCmp.makeBSONObjSet({BSON("" << 1), BSON("" << 2), BSON("" << 3)}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQUALS(3UL, diff.first.size()); ASSERT_EQUALS(3UL, diff.second.size()); for (auto&& obj : diff.first) { ASSERT(left.find(obj) != left.end()); } for (auto&& obj : diff.second) { ASSERT(right.find(obj) != right.end()); } } TEST(IndexAccessMethodSetDifference, ShouldNotReportOverlapsFromNonDisjointSets) { SimpleBSONObjComparator bsonCmp; BSONObjSet left = bsonCmp.makeBSONObjSet({BSON("" << 0), BSON("" << 1), BSON("" << 4), BSON("" << 6)}); BSONObjSet right = bsonCmp.makeBSONObjSet( {BSON("" << -1), BSON("" << 1), BSON("" << 3), BSON("" << 4), BSON("" << 7)}); auto diff = IndexAccessMethod::setDifference(left, right); ASSERT_EQUALS(2UL, diff.first.size()); // 0, 6. ASSERT_EQUALS(3UL, diff.second.size()); // -1, 3, 7. for (auto&& obj : diff.first) { ASSERT(left.find(obj) != left.end()); // Make sure it's not in the intersection. ASSERT_BSONOBJ_NE(obj, BSON("" << 1)); ASSERT_BSONOBJ_NE(obj, BSON("" << 4)); } for (auto&& obj : diff.second) { ASSERT(right.find(obj) != right.end()); // Make sure it's not in the intersection. ASSERT_BSONOBJ_NE(obj, BSON("" << 1)); ASSERT_BSONOBJ_NE(obj, BSON("" << 4)); } } } // namespace } // namespace mongo