diff options
Diffstat (limited to 'src/mongo/db/query/index_bounds_builder_test.cpp')
-rw-r--r-- | src/mongo/db/query/index_bounds_builder_test.cpp | 2686 |
1 files changed, 1353 insertions, 1333 deletions
diff --git a/src/mongo/db/query/index_bounds_builder_test.cpp b/src/mongo/db/query/index_bounds_builder_test.cpp index 1b349b0515a..cf8129c016d 100644 --- a/src/mongo/db/query/index_bounds_builder_test.cpp +++ b/src/mongo/db/query/index_bounds_builder_test.cpp @@ -42,1353 +42,1373 @@ using namespace mongo; namespace { - using std::unique_ptr; - using std::numeric_limits; - using std::string; - using std::vector; - - double numberMin = -numeric_limits<double>::max(); - double numberMax = numeric_limits<double>::max(); - double negativeInfinity = -numeric_limits<double>::infinity(); - double positiveInfinity = numeric_limits<double>::infinity(); - - /** - * Utility function to create MatchExpression - */ - MatchExpression* parseMatchExpression(const BSONObj& obj) { - StatusWithMatchExpression status = MatchExpressionParser::parse(obj); - ASSERT_TRUE(status.isOK()); - MatchExpression* expr(status.getValue()); - return expr; - } +using std::unique_ptr; +using std::numeric_limits; +using std::string; +using std::vector; - /** - * Given a list of queries in 'toUnion', translate into index bounds and return - * the union of these bounds in the out-parameter 'oilOut'. - */ - void testTranslateAndUnion(const vector<BSONObj>& toUnion, OrderedIntervalList* oilOut, - IndexBoundsBuilder::BoundsTightness* tightnessOut) { +double numberMin = -numeric_limits<double>::max(); +double numberMax = numeric_limits<double>::max(); +double negativeInfinity = -numeric_limits<double>::infinity(); +double positiveInfinity = numeric_limits<double>::infinity(); - IndexEntry testIndex = IndexEntry(BSONObj()); - - for (vector<BSONObj>::const_iterator it = toUnion.begin(); - it != toUnion.end(); - ++it) { - unique_ptr<MatchExpression> expr(parseMatchExpression(*it)); - BSONElement elt = it->firstElement(); - if (toUnion.begin() == it) { - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, oilOut, tightnessOut); - } - else { - IndexBoundsBuilder::translateAndUnion(expr.get(), elt, testIndex, oilOut, tightnessOut); - } - } - } +/** + * Utility function to create MatchExpression + */ +MatchExpression* parseMatchExpression(const BSONObj& obj) { + StatusWithMatchExpression status = MatchExpressionParser::parse(obj); + ASSERT_TRUE(status.isOK()); + MatchExpression* expr(status.getValue()); + return expr; +} - /** - * Given a list of queries in 'toUnion', translate into index bounds and return - * the intersection of these bounds in the out-parameter 'oilOut'. - */ - void testTranslateAndIntersect(const vector<BSONObj>& toIntersect, OrderedIntervalList* oilOut, - IndexBoundsBuilder::BoundsTightness* tightnessOut) { - - IndexEntry testIndex = IndexEntry(BSONObj()); - - for (vector<BSONObj>::const_iterator it = toIntersect.begin(); - it != toIntersect.end(); - ++it) { - unique_ptr<MatchExpression> expr(parseMatchExpression(*it)); - BSONElement elt = it->firstElement(); - if (toIntersect.begin() == it) { - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, oilOut, tightnessOut); - } - else { - IndexBoundsBuilder::translateAndIntersect(expr.get(), elt, testIndex, oilOut, tightnessOut); - } +/** + * Given a list of queries in 'toUnion', translate into index bounds and return + * the union of these bounds in the out-parameter 'oilOut'. + */ +void testTranslateAndUnion(const vector<BSONObj>& toUnion, + OrderedIntervalList* oilOut, + IndexBoundsBuilder::BoundsTightness* tightnessOut) { + IndexEntry testIndex = IndexEntry(BSONObj()); + + for (vector<BSONObj>::const_iterator it = toUnion.begin(); it != toUnion.end(); ++it) { + unique_ptr<MatchExpression> expr(parseMatchExpression(*it)); + BSONElement elt = it->firstElement(); + if (toUnion.begin() == it) { + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, oilOut, tightnessOut); + } else { + IndexBoundsBuilder::translateAndUnion(expr.get(), elt, testIndex, oilOut, tightnessOut); } } +} - /** - * 'constraints' is a vector of BSONObj's representing match expressions, where - * each filter is paired with a boolean. If the boolean is true, then the filter's - * index bounds should be intersected with the other constraints; if false, then - * they should be unioned. The resulting bounds are returned in the - * out-parameter 'oilOut'. - */ - void testTranslate(const vector< std::pair<BSONObj, bool> >& constraints, - OrderedIntervalList* oilOut, - IndexBoundsBuilder::BoundsTightness* tightnessOut) { - - IndexEntry testIndex = IndexEntry(BSONObj()); - - for (vector< std::pair<BSONObj, bool> >::const_iterator it = constraints.begin(); - it != constraints.end(); - ++it) { - BSONObj obj = it->first; - bool isIntersect = it->second; - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - if (constraints.begin() == it) { - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, oilOut, tightnessOut); - } - else if (isIntersect) { - IndexBoundsBuilder::translateAndIntersect(expr.get(), elt, testIndex, oilOut, tightnessOut); - } - else { - IndexBoundsBuilder::translateAndUnion(expr.get(), elt, testIndex, oilOut, tightnessOut); - } +/** + * Given a list of queries in 'toUnion', translate into index bounds and return + * the intersection of these bounds in the out-parameter 'oilOut'. + */ +void testTranslateAndIntersect(const vector<BSONObj>& toIntersect, + OrderedIntervalList* oilOut, + IndexBoundsBuilder::BoundsTightness* tightnessOut) { + IndexEntry testIndex = IndexEntry(BSONObj()); + + for (vector<BSONObj>::const_iterator it = toIntersect.begin(); it != toIntersect.end(); ++it) { + unique_ptr<MatchExpression> expr(parseMatchExpression(*it)); + BSONElement elt = it->firstElement(); + if (toIntersect.begin() == it) { + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, oilOut, tightnessOut); + } else { + IndexBoundsBuilder::translateAndIntersect( + expr.get(), elt, testIndex, oilOut, tightnessOut); } } +} - /** - * run isSingleInterval and return the result to calling test. - */ - bool testSingleInterval(IndexBounds bounds) { - BSONObj startKey; - bool startKeyIn; - BSONObj endKey; - bool endKeyIn; - return IndexBoundsBuilder::isSingleInterval( bounds, - &startKey, - &startKeyIn, - &endKey, - &endKeyIn ); - } - - // - // $elemMatch value - // Example: {a: {$elemMatch: {$gt: 2}}} - // - - TEST(IndexBoundsBuilderTest, TranslateElemMatchValue) { - IndexEntry testIndex = IndexEntry(BSONObj()); - // Bounds generated should be the same as the embedded expression - // except for the tightness. - BSONObj obj = fromjson("{a: {$elemMatch: {$gt: 2}}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 2, '': Infinity}"), false, true))); - ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); - } - - // - // Comparison operators ($lte, $lt, $gt, $gte, $eq) - // - - TEST(IndexBoundsBuilderTest, TranslateLteNumber) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lte: 1}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': -Infinity, '': 1}"), true, true))); - ASSERT(tightness == IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLteNumberMin) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = BSON("a" << BSON("$lte" << numberMin)); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(BSON("" << negativeInfinity << "" << numberMin), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLteNegativeInfinity) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lte: -Infinity}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': -Infinity, '': -Infinity}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLtNumber) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lt: 1}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': -Infinity, '': 1}"), true, false))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLtNumberMin) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = BSON("a" << BSON("$lt" << numberMin)); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(BSON("" << negativeInfinity << "" << numberMin), true, false))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLtNegativeInfinity) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lt: -Infinity}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 0U); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLtDate) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = BSON("a" << LT << Date_t::fromMillisSinceEpoch(5000)); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': true, '': new Date(5000)}"), false, false))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGtNumber) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gt: 1}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 1, '': Infinity}"), false, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGtNumberMax) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = BSON("a" << BSON("$gt" << numberMax)); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(BSON("" << numberMax << "" << positiveInfinity), false, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGtPositiveInfinity) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gt: Infinity}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 0U); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGteNumber) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gte: 1}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 1, '': Infinity}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGteNumberMax) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = BSON("a" << BSON("$gte" << numberMax)); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(BSON("" << numberMax << "" << positiveInfinity), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGtePositiveInfinity) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gte: Infinity}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': Infinity, '': Infinity}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGtString) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gt: 'abc'}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 'abc', '': {}}"), false, false))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateEqualNan) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: NaN}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': NaN, '': NaN}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLtNan) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lt: NaN}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 0U); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLteNan) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lte: NaN}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': NaN, '': NaN}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGtNan) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gt: NaN}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 0U); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGteNan) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gte: NaN}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': NaN, '': NaN}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateEqual) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = BSON("a" << 4); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 4, '': 4}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateArrayEqualBasic) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: [1, 2, 3]}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 1, '': 1}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': [1, 2, 3], '': [1, 2, 3]}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); - } - - TEST(IndexBoundsBuilderTest, TranslateIn) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$in: [8, 44, -1, -3]}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 4U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': -3, '': -3}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': -1, '': -1}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[2].compare( - Interval(fromjson("{'': 8, '': 8}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[3].compare( - Interval(fromjson("{'': 44, '': 44}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateInArray) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$in: [[1], 2]}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 3U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 1, '': 1}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': 2, '': 2}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[2].compare( - Interval(fromjson("{'': [1], '': [1]}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); - } - - TEST(IndexBoundsBuilderTest, TranslateLteBinData) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lte: {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA'," - "$type: '00'}}}"); - std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQ(oil.name, "a"); - ASSERT_EQ(oil.intervals.size(), 1U); - ASSERT_EQ(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': {$binary: '', $type: '00'}," - "'': {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"), - true, true))); - ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateLtBinData) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$lt: {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA'," - "$type: '00'}}}"); - std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQ(oil.name, "a"); - ASSERT_EQ(oil.intervals.size(), 1U); - ASSERT_EQ(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': {$binary: '', $type: '00'}," - "'': {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"), - true, false))); - ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGtBinData) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gt: {$binary: '////////////////////////////'," - "$type: '00'}}}"); - std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQ(oil.name, "a"); - ASSERT_EQ(oil.intervals.size(), 1U); - ASSERT_EQ(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': {$binary: '////////////////////////////', $type: '00'}," - "'': ObjectId('000000000000000000000000')}"), - false, false))); - ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, TranslateGteBinData) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$gte: {$binary: '////////////////////////////'," - "$type: '00'}}}"); - std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQ(oil.name, "a"); - ASSERT_EQ(oil.intervals.size(), 1U); - ASSERT_EQ(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': {$binary: '////////////////////////////', $type: '00'}," - "'': ObjectId('000000000000000000000000')}"), - true, false))); - ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); - } - - // - // $exists tests - // - - TEST(IndexBoundsBuilderTest, ExistsTrue) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$exists: true}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, - oil.intervals[0].compare(IndexBoundsBuilder::allValues())); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); - } - - TEST(IndexBoundsBuilderTest, ExistsFalse) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$exists: false}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': null, '': null}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); - } - - TEST(IndexBoundsBuilderTest, ExistsTrueSparse) { - IndexEntry testIndex = IndexEntry(BSONObj(), - false, // multikey - true, // sparse - false, // unique - "exists_true_sparse", - nullptr, // filterExpr - BSONObj()); - BSONObj obj = fromjson("{a: {$exists: true}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, - oil.intervals[0].compare(IndexBoundsBuilder::allValues())); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - // - // Union tests - // - - TEST(IndexBoundsBuilderTest, UnionTwoLt) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toUnion; - toUnion.push_back(fromjson("{a: {$lt: 1}}")); - toUnion.push_back(fromjson("{a: {$lt: 5}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndUnion(toUnion, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': -Infinity, '': 5}"), true, false))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, UnionDupEq) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toUnion; - toUnion.push_back(fromjson("{a: 1}")); - toUnion.push_back(fromjson("{a: 5}")); - toUnion.push_back(fromjson("{a: 1}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndUnion(toUnion, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 1, '': 1}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': 5, '': 5}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, UnionGtLt) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toUnion; - toUnion.push_back(fromjson("{a: {$gt: 1}}")); - toUnion.push_back(fromjson("{a: {$lt: 3}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndUnion(toUnion, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': -Infinity, '': Infinity}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, UnionTwoEmptyRanges) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector< std::pair<BSONObj, bool> > constraints; - constraints.push_back(std::make_pair(fromjson("{a: {$gt: 1}}"), true)); - constraints.push_back(std::make_pair(fromjson("{a: {$lte: 0}}"), true)); - constraints.push_back(std::make_pair(fromjson("{a: {$in:[]}}"), false)); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslate(constraints, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 0U); - } - - // - // Intersection tests - // - - TEST(IndexBoundsBuilderTest, IntersectTwoLt) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: {$lt: 1}}")); - toIntersect.push_back(fromjson("{a: {$lt: 5}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': -Infinity, '': 1}"), true, false))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, IntersectEqGte) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: 1}}")); - toIntersect.push_back(fromjson("{a: {$gte: 1}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 1, '': 1}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, IntersectGtLte) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: {$gt: 0}}")); - toIntersect.push_back(fromjson("{a: {$lte: 10}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 0, '': 10}"), false, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, IntersectGtIn) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: {$gt: 4}}")); - toIntersect.push_back(fromjson("{a: {$in: [1,2,3,4,5,6]}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 5, '': 5}"), true, true))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': 6, '': 6}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, IntersectionIsPointInterval) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: {$gte: 1}}")); - toIntersect.push_back(fromjson("{a: {$lte: 1}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 1, '': 1}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, IntersectFullyContained) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: {$gt: 5}}")); - toIntersect.push_back(fromjson("{a: {$lt: 15}}")); - toIntersect.push_back(fromjson("{a: {$gte: 6}}")); - toIntersect.push_back(fromjson("{a: {$lte: 13}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 6, '': 13}"), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, EmptyIntersection) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: 1}}")); - toIntersect.push_back(fromjson("{a: {$gte: 2}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 0U); - } - - // - // $mod - // - - TEST(IndexBoundsBuilderTest, TranslateMod) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$mod: [2, 0]}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(BSON("" << numberMin << "" << numberMax), true, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - // - // Test simpleRegex - // - - TEST(SimpleRegexTest, RootedLine) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("^foo", "", &tightness); - ASSERT_EQUALS(prefix, "foo"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedString) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("\\Afoo", "", &tightness); - ASSERT_EQUALS(prefix, "foo"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedOptionalFirstChar) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("^f?oo", "", &tightness); - ASSERT_EQUALS(prefix, ""); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(SimpleRegexTest, RootedOptionalSecondChar) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("^fz?oo", "", &tightness); - ASSERT_EQUALS(prefix, "f"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(SimpleRegexTest, RootedMultiline) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("^foo", "m", &tightness); - ASSERT_EQUALS(prefix, ""); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(SimpleRegexTest, RootedStringMultiline) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("\\Afoo", "m", &tightness); - ASSERT_EQUALS(prefix, "foo"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedCaseInsensitiveMulti) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("\\Afoo", "mi", &tightness); - ASSERT_EQUALS(prefix, ""); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(SimpleRegexTest, RootedComplex) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "\\Af \t\vo\n\ro \\ \\# #comment", "mx", &tightness); - ASSERT_EQUALS(prefix, "foo #"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(SimpleRegexTest, RootedLiteral) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^\\Qasdf\\E", "", &tightness); - ASSERT_EQUALS(prefix, "asdf"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedLiteralWithExtra) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^\\Qasdf\\E.*", "", &tightness); - ASSERT_EQUALS(prefix, "asdf"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(SimpleRegexTest, RootedLiteralNoEnd) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^\\Qasdf", "", &tightness); - ASSERT_EQUALS(prefix, "asdf"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedLiteralBackslash) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^\\Qasdf\\\\E", "", &tightness); - ASSERT_EQUALS(prefix, "asdf\\"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedLiteralDotStar) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^\\Qas.*df\\E", "", &tightness); - ASSERT_EQUALS(prefix, "as.*df"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedLiteralNestedEscape) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^\\Qas\\Q[df\\E", "", &tightness); - ASSERT_EQUALS(prefix, "as\\Q[df"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(SimpleRegexTest, RootedLiteralNestedEscapeEnd) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^\\Qas\\E\\\\E\\Q$df\\E", "", &tightness); - ASSERT_EQUALS(prefix, "as\\E$df"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - // A regular expression with the "|" character is not considered simple. See SERVER-15235. - TEST(SimpleRegexTest, PipeCharacterDisallowed) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^(a(a|$)|b", "", &tightness); - ASSERT_EQUALS(prefix, ""); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(SimpleRegexTest, PipeCharacterDisallowed2) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "^(a(a|$)|^b", "", &tightness); - ASSERT_EQUALS(prefix, ""); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - // SERVER-9035 - TEST(SimpleRegexTest, RootedSingleLineMode) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("^foo", "s", &tightness); - ASSERT_EQUALS(prefix, "foo"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - // SERVER-9035 - TEST(SimpleRegexTest, NonRootedSingleLineMode) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex("foo", "s", &tightness); - ASSERT_EQUALS(prefix, ""); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - // SERVER-9035 - TEST(SimpleRegexTest, RootedComplexSingleLineMode) { - IndexBoundsBuilder::BoundsTightness tightness; - string prefix = IndexBoundsBuilder::simpleRegex( - "\\Af \t\vo\n\ro \\ \\# #comment", "msx", &tightness); - ASSERT_EQUALS(prefix, "foo #"); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); - } - - // - // Regex bounds - // - - TEST(IndexBoundsBuilderTest, SimpleNonPrefixRegex) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: /foo/}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': '', '': {}}"), true, false))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': /foo/, '': /foo/}"), true, true))); - ASSERT(tightness == IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(IndexBoundsBuilderTest, NonSimpleRegexWithPipe) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: /^foo.*|bar/}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': '', '': {}}"), true, false))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': /^foo.*|bar/, '': /^foo.*|bar/}"), true, true))); - ASSERT(tightness == IndexBoundsBuilder::INEXACT_COVERED); - } - - TEST(IndexBoundsBuilderTest, SimpleRegexSingleLineMode) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: /^foo/s}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 'foo', '': 'fop'}"), true, false))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': /^foo/s, '': /^foo/s}"), true, true))); - ASSERT(tightness == IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, SimplePrefixRegex) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: /^foo/}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(fromjson("{'': 'foo', '': 'fop'}"), true, false))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(fromjson("{'': /^foo/, '': /^foo/}"), true, true))); - ASSERT(tightness == IndexBoundsBuilder::EXACT); - } - - // - // isSingleInterval - // - - TEST(IndexBoundsBuilderTest, SingleFieldEqualityInterval) { - // Equality on a single field is a single interval. - OrderedIntervalList oil("a"); - IndexBounds bounds; - oil.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); - bounds.fields.push_back(oil); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, SingleIntervalSingleFieldInterval) { - // Single interval on a single field is a single interval. - OrderedIntervalList oil("a"); - IndexBounds bounds; - oil.intervals.push_back(Interval(fromjson("{ '':5, '':Infinity }"), true, true)); - bounds.fields.push_back(oil); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, MultipleIntervalsSingleFieldInterval) { - // Multiple intervals on a single field is not a single interval. - OrderedIntervalList oil("a"); - IndexBounds bounds; - oil.intervals.push_back(Interval(fromjson( "{ '':4, '':5 }" ), true, true)); - oil.intervals.push_back(Interval(fromjson( "{ '':7, '':Infinity }" ), true, true)); - bounds.fields.push_back(oil); - ASSERT(!testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, EqualityTwoFieldsInterval) { - // Equality on two fields is a compound single interval. - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); - oil_b.intervals.push_back(Interval(BSON("" << 6 << "" << 6), true, true)); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, EqualityFirstFieldSingleIntervalSecondFieldInterval) { - // Equality on first field and single interval on second field - // is a compound single interval. - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); - oil_b.intervals.push_back(Interval(fromjson( "{ '':6, '':Infinity }" ), true, true)); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, SingleIntervalFirstAndSecondFieldsInterval) { - // Single interval on first field and single interval on second field is - // not a compound single interval. - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(fromjson( "{ '':-Infinity, '':5 }" ), true, true)); - oil_b.intervals.push_back(Interval(fromjson( "{ '':6, '':Infinity }" ), true, true)); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - ASSERT(!testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, MultipleIntervalsTwoFieldsInterval) { - // Multiple intervals on two fields is not a compound single interval. - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON( "" << 4 << "" << 4 ), true, true)); - oil_a.intervals.push_back(Interval(BSON( "" << 5 << "" << 5 ), true, true)); - oil_b.intervals.push_back(Interval(BSON( "" << 7 << "" << 7 ), true, true)); - oil_b.intervals.push_back(Interval(BSON( "" << 8 << "" << 8 ), true, true)); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - ASSERT(!testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, MissingSecondFieldInterval) { - // when second field is not specified, still a compound single interval - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON( "" << 5 << "" << 5 ), true, true)); - oil_b.intervals.push_back(IndexBoundsBuilder::allValues()); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, EqualityTwoFieldsIntervalThirdInterval) { - // Equality on first two fields and single interval on third is a - // compound single interval. - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - OrderedIntervalList oil_c("c"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON( "" << 5 << "" << 5 ), true, true)); - oil_b.intervals.push_back(Interval(BSON( "" << 6 << "" << 6 ), true, true)); - oil_c.intervals.push_back(Interval(fromjson( "{ '':7, '':Infinity }" ), true, true)); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - bounds.fields.push_back(oil_c); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, EqualitySingleIntervalMissingInterval) { - // Equality, then Single Interval, then missing is a compound single interval - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - OrderedIntervalList oil_c("c"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON( "" << 5 << "" << 5 ), true, true)); - oil_b.intervals.push_back(Interval(fromjson( "{ '':7, '':Infinity }" ), true, true)); - oil_c.intervals.push_back(IndexBoundsBuilder::allValues()); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - bounds.fields.push_back(oil_c); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, EqualitySingleMissingMissingInterval) { - // Equality, then single interval, then missing, then missing, - // is a compound single interval - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - OrderedIntervalList oil_c("c"); - OrderedIntervalList oil_d("d"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON( "" << 5 << "" << 5 ), true, true)); - oil_b.intervals.push_back(Interval(fromjson( "{ '':7, '':Infinity }" ), true, true)); - oil_c.intervals.push_back(IndexBoundsBuilder::allValues()); - oil_d.intervals.push_back(IndexBoundsBuilder::allValues()); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - bounds.fields.push_back(oil_c); - bounds.fields.push_back(oil_d); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, EqualitySingleMissingMissingMixedInterval) { - // Equality, then single interval, then missing, then missing, with mixed order - // fields is a compound single interval. - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - OrderedIntervalList oil_c("c"); - OrderedIntervalList oil_d("d"); - IndexBounds bounds; - Interval allValues = IndexBoundsBuilder::allValues(); - oil_a.intervals.push_back(Interval(BSON( "" << 5 << "" << 5 ), true, true)); - oil_b.intervals.push_back(Interval(fromjson( "{ '':7, '':Infinity }" ), true, true)); - oil_c.intervals.push_back(allValues); - IndexBoundsBuilder::reverseInterval(&allValues); - oil_d.intervals.push_back(allValues); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - bounds.fields.push_back(oil_c); - bounds.fields.push_back(oil_d); - ASSERT(testSingleInterval(bounds)); - } - - TEST(IndexBoundsBuilderTest, EqualitySingleMissingSingleInterval) { - // Equality, then single interval, then missing, then single interval is not - // a compound single interval. - OrderedIntervalList oil_a("a"); - OrderedIntervalList oil_b("b"); - OrderedIntervalList oil_c("c"); - OrderedIntervalList oil_d("d"); - IndexBounds bounds; - oil_a.intervals.push_back(Interval(BSON( "" << 5 << "" << 5 ), true, true)); - oil_b.intervals.push_back(Interval(fromjson( "{ '':7, '':Infinity }" ), true, true)); - oil_c.intervals.push_back(IndexBoundsBuilder::allValues()); - oil_d.intervals.push_back(Interval(fromjson( "{ '':1, '':Infinity }" ), true, true)); - bounds.fields.push_back(oil_a); - bounds.fields.push_back(oil_b); - bounds.fields.push_back(oil_c); - bounds.fields.push_back(oil_d); - ASSERT(!testSingleInterval(bounds)); - } - - // - // Complementing bounds for negations - // - - /** - * Get a BSONObj which represents the interval from - * MinKey to 'end'. - */ - BSONObj minKeyIntObj(int end) { - BSONObjBuilder bob; - bob.appendMinKey(""); - bob.appendNumber("", end); - return bob.obj(); - } - - /** - * Get a BSONObj which represents the interval from - * 'start' to MaxKey. - */ - BSONObj maxKeyIntObj(int start) { - BSONObjBuilder bob; - bob.appendNumber("", start); - bob.appendMaxKey(""); - return bob.obj(); - } - - // Expected oil: [MinKey, 3), (3, MaxKey] - TEST(IndexBoundsBuilderTest, SimpleNE) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = BSON("a" << BSON("$ne" << 3)); +/** + * 'constraints' is a vector of BSONObj's representing match expressions, where + * each filter is paired with a boolean. If the boolean is true, then the filter's + * index bounds should be intersected with the other constraints; if false, then + * they should be unioned. The resulting bounds are returned in the + * out-parameter 'oilOut'. + */ +void testTranslate(const vector<std::pair<BSONObj, bool>>& constraints, + OrderedIntervalList* oilOut, + IndexBoundsBuilder::BoundsTightness* tightnessOut) { + IndexEntry testIndex = IndexEntry(BSONObj()); + + for (vector<std::pair<BSONObj, bool>>::const_iterator it = constraints.begin(); + it != constraints.end(); + ++it) { + BSONObj obj = it->first; + bool isIntersect = it->second; unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); BSONElement elt = obj.firstElement(); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(minKeyIntObj(3), true, false))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(maxKeyIntObj(3), false, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, IntersectWithNE) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toIntersect; - toIntersect.push_back(fromjson("{a: {$gt: 1}}")); - toIntersect.push_back(fromjson("{a: {$ne: 2}}}")); - toIntersect.push_back(fromjson("{a: {$lte: 6}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndIntersect(toIntersect, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 2U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(BSON("" << 1 << "" << 2), false, false))); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[1].compare( - Interval(BSON("" << 2 << "" << 6), false, true))); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); - } - - TEST(IndexBoundsBuilderTest, UnionizeWithNE) { - IndexEntry testIndex = IndexEntry(BSONObj()); - vector<BSONObj> toUnionize; - toUnionize.push_back(fromjson("{a: {$ne: 3}}")); - toUnionize.push_back(fromjson("{a: {$ne: 4}}}")); - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - testTranslateAndUnion(toUnionize, &oil, &tightness); - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - IndexBoundsBuilder::allValues())); - ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); + if (constraints.begin() == it) { + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, oilOut, tightnessOut); + } else if (isIntersect) { + IndexBoundsBuilder::translateAndIntersect( + expr.get(), elt, testIndex, oilOut, tightnessOut); + } else { + IndexBoundsBuilder::translateAndUnion(expr.get(), elt, testIndex, oilOut, tightnessOut); + } } +} - // Test $type bounds for Code BSON type. - TEST(IndexBoundsBuilderTest, CodeTypeBounds) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$type: 13}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); - - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - - // Build the expected interval. - BSONObjBuilder bob; - bob.appendCode("", ""); - bob.appendCodeWScope("", "", BSONObj()); - BSONObj expectedInterval = bob.obj(); - - // Check the output of translate(). - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(expectedInterval, true, true))); - ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); - } +/** + * run isSingleInterval and return the result to calling test. + */ +bool testSingleInterval(IndexBounds bounds) { + BSONObj startKey; + bool startKeyIn; + BSONObj endKey; + bool endKeyIn; + return IndexBoundsBuilder::isSingleInterval(bounds, &startKey, &startKeyIn, &endKey, &endKeyIn); +} + +// +// $elemMatch value +// Example: {a: {$elemMatch: {$gt: 2}}} +// + +TEST(IndexBoundsBuilderTest, TranslateElemMatchValue) { + IndexEntry testIndex = IndexEntry(BSONObj()); + // Bounds generated should be the same as the embedded expression + // except for the tightness. + BSONObj obj = fromjson("{a: {$elemMatch: {$gt: 2}}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 2, '': Infinity}"), false, true))); + ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); +} + +// +// Comparison operators ($lte, $lt, $gt, $gte, $eq) +// + +TEST(IndexBoundsBuilderTest, TranslateLteNumber) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$lte: 1}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': -Infinity, '': 1}"), true, true))); + ASSERT(tightness == IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLteNumberMin) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = BSON("a" << BSON("$lte" << numberMin)); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(BSON("" << negativeInfinity << "" << numberMin), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLteNegativeInfinity) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$lte: -Infinity}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': -Infinity, '': -Infinity}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLtNumber) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$lt: 1}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': -Infinity, '': 1}"), true, false))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLtNumberMin) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = BSON("a" << BSON("$lt" << numberMin)); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(BSON("" << negativeInfinity << "" << numberMin), true, false))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLtNegativeInfinity) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$lt: -Infinity}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 0U); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLtDate) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = BSON("a" << LT << Date_t::fromMillisSinceEpoch(5000)); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(fromjson("{'': true, '': new Date(5000)}"), false, false))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGtNumber) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$gt: 1}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 1, '': Infinity}"), false, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGtNumberMax) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = BSON("a" << BSON("$gt" << numberMax)); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(BSON("" << numberMax << "" << positiveInfinity), false, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGtPositiveInfinity) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$gt: Infinity}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 0U); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGteNumber) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$gte: 1}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 1, '': Infinity}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGteNumberMax) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = BSON("a" << BSON("$gte" << numberMax)); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(BSON("" << numberMax << "" << positiveInfinity), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGtePositiveInfinity) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$gte: Infinity}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': Infinity, '': Infinity}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGtString) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$gt: 'abc'}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 'abc', '': {}}"), false, false))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateEqualNan) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: NaN}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': NaN, '': NaN}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLtNan) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$lt: NaN}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 0U); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLteNan) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$lte: NaN}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': NaN, '': NaN}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGtNan) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$gt: NaN}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 0U); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGteNan) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$gte: NaN}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': NaN, '': NaN}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateEqual) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = BSON("a" << 4); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 4, '': 4}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateArrayEqualBasic) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: [1, 2, 3]}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 1, '': 1}"), true, true))); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': [1, 2, 3], '': [1, 2, 3]}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); +} + +TEST(IndexBoundsBuilderTest, TranslateIn) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$in: [8, 44, -1, -3]}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 4U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': -3, '': -3}"), true, true))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': -1, '': -1}"), true, true))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[2].compare(Interval(fromjson("{'': 8, '': 8}"), true, true))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[3].compare(Interval(fromjson("{'': 44, '': 44}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateInArray) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$in: [[1], 2]}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 3U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 1, '': 1}"), true, true))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': 2, '': 2}"), true, true))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[2].compare(Interval(fromjson("{'': [1], '': [1]}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); +} + +TEST(IndexBoundsBuilderTest, TranslateLteBinData) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson( + "{a: {$lte: {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA'," + "$type: '00'}}}"); + std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQ(oil.name, "a"); + ASSERT_EQ(oil.intervals.size(), 1U); + ASSERT_EQ(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(fromjson( + "{'': {$binary: '', $type: '00'}," + "'': {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"), + true, + true))); + ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateLtBinData) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson( + "{a: {$lt: {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA'," + "$type: '00'}}}"); + std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQ(oil.name, "a"); + ASSERT_EQ(oil.intervals.size(), 1U); + ASSERT_EQ(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(fromjson( + "{'': {$binary: '', $type: '00'}," + "'': {$binary: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAA', $type: '00'}}"), + true, + false))); + ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGtBinData) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson( + "{a: {$gt: {$binary: '////////////////////////////'," + "$type: '00'}}}"); + std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQ(oil.name, "a"); + ASSERT_EQ(oil.intervals.size(), 1U); + ASSERT_EQ(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(fromjson( + "{'': {$binary: '////////////////////////////', $type: '00'}," + "'': ObjectId('000000000000000000000000')}"), + false, + false))); + ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, TranslateGteBinData) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson( + "{a: {$gte: {$binary: '////////////////////////////'," + "$type: '00'}}}"); + std::unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQ(oil.name, "a"); + ASSERT_EQ(oil.intervals.size(), 1U); + ASSERT_EQ(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare( + Interval(fromjson( + "{'': {$binary: '////////////////////////////', $type: '00'}," + "'': ObjectId('000000000000000000000000')}"), + true, + false))); + ASSERT_EQ(tightness, IndexBoundsBuilder::EXACT); +} + +// +// $exists tests +// + +TEST(IndexBoundsBuilderTest, ExistsTrue) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$exists: true}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(IndexBoundsBuilder::allValues())); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); +} + +TEST(IndexBoundsBuilderTest, ExistsFalse) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$exists: false}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': null, '': null}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_FETCH); +} + +TEST(IndexBoundsBuilderTest, ExistsTrueSparse) { + IndexEntry testIndex = IndexEntry(BSONObj(), + false, // multikey + true, // sparse + false, // unique + "exists_true_sparse", + nullptr, // filterExpr + BSONObj()); + BSONObj obj = fromjson("{a: {$exists: true}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(IndexBoundsBuilder::allValues())); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +// +// Union tests +// + +TEST(IndexBoundsBuilderTest, UnionTwoLt) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toUnion; + toUnion.push_back(fromjson("{a: {$lt: 1}}")); + toUnion.push_back(fromjson("{a: {$lt: 5}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndUnion(toUnion, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': -Infinity, '': 5}"), true, false))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, UnionDupEq) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toUnion; + toUnion.push_back(fromjson("{a: 1}")); + toUnion.push_back(fromjson("{a: 5}")); + toUnion.push_back(fromjson("{a: 1}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndUnion(toUnion, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 1, '': 1}"), true, true))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': 5, '': 5}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, UnionGtLt) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toUnion; + toUnion.push_back(fromjson("{a: {$gt: 1}}")); + toUnion.push_back(fromjson("{a: {$lt: 3}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndUnion(toUnion, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': -Infinity, '': Infinity}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, UnionTwoEmptyRanges) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<std::pair<BSONObj, bool>> constraints; + constraints.push_back(std::make_pair(fromjson("{a: {$gt: 1}}"), true)); + constraints.push_back(std::make_pair(fromjson("{a: {$lte: 0}}"), true)); + constraints.push_back(std::make_pair(fromjson("{a: {$in:[]}}"), false)); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslate(constraints, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 0U); +} + +// +// Intersection tests +// + +TEST(IndexBoundsBuilderTest, IntersectTwoLt) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: {$lt: 1}}")); + toIntersect.push_back(fromjson("{a: {$lt: 5}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': -Infinity, '': 1}"), true, false))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, IntersectEqGte) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: 1}}")); + toIntersect.push_back(fromjson("{a: {$gte: 1}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 1, '': 1}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, IntersectGtLte) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: {$gt: 0}}")); + toIntersect.push_back(fromjson("{a: {$lte: 10}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 0, '': 10}"), false, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, IntersectGtIn) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: {$gt: 4}}")); + toIntersect.push_back(fromjson("{a: {$in: [1,2,3,4,5,6]}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 5, '': 5}"), true, true))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': 6, '': 6}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, IntersectionIsPointInterval) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: {$gte: 1}}")); + toIntersect.push_back(fromjson("{a: {$lte: 1}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 1, '': 1}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, IntersectFullyContained) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: {$gt: 5}}")); + toIntersect.push_back(fromjson("{a: {$lt: 15}}")); + toIntersect.push_back(fromjson("{a: {$gte: 6}}")); + toIntersect.push_back(fromjson("{a: {$lte: 13}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 6, '': 13}"), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, EmptyIntersection) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: 1}}")); + toIntersect.push_back(fromjson("{a: {$gte: 2}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 0U); +} + +// +// $mod +// + +TEST(IndexBoundsBuilderTest, TranslateMod) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$mod: [2, 0]}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(BSON("" << numberMin << "" << numberMax), true, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +// +// Test simpleRegex +// + +TEST(SimpleRegexTest, RootedLine) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^foo", "", &tightness); + ASSERT_EQUALS(prefix, "foo"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedString) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("\\Afoo", "", &tightness); + ASSERT_EQUALS(prefix, "foo"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedOptionalFirstChar) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^f?oo", "", &tightness); + ASSERT_EQUALS(prefix, ""); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(SimpleRegexTest, RootedOptionalSecondChar) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^fz?oo", "", &tightness); + ASSERT_EQUALS(prefix, "f"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(SimpleRegexTest, RootedMultiline) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^foo", "m", &tightness); + ASSERT_EQUALS(prefix, ""); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(SimpleRegexTest, RootedStringMultiline) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("\\Afoo", "m", &tightness); + ASSERT_EQUALS(prefix, "foo"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedCaseInsensitiveMulti) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("\\Afoo", "mi", &tightness); + ASSERT_EQUALS(prefix, ""); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(SimpleRegexTest, RootedComplex) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = + IndexBoundsBuilder::simpleRegex("\\Af \t\vo\n\ro \\ \\# #comment", "mx", &tightness); + ASSERT_EQUALS(prefix, "foo #"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(SimpleRegexTest, RootedLiteral) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^\\Qasdf\\E", "", &tightness); + ASSERT_EQUALS(prefix, "asdf"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedLiteralWithExtra) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^\\Qasdf\\E.*", "", &tightness); + ASSERT_EQUALS(prefix, "asdf"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(SimpleRegexTest, RootedLiteralNoEnd) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^\\Qasdf", "", &tightness); + ASSERT_EQUALS(prefix, "asdf"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedLiteralBackslash) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^\\Qasdf\\\\E", "", &tightness); + ASSERT_EQUALS(prefix, "asdf\\"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedLiteralDotStar) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^\\Qas.*df\\E", "", &tightness); + ASSERT_EQUALS(prefix, "as.*df"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedLiteralNestedEscape) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^\\Qas\\Q[df\\E", "", &tightness); + ASSERT_EQUALS(prefix, "as\\Q[df"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(SimpleRegexTest, RootedLiteralNestedEscapeEnd) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^\\Qas\\E\\\\E\\Q$df\\E", "", &tightness); + ASSERT_EQUALS(prefix, "as\\E$df"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +// A regular expression with the "|" character is not considered simple. See SERVER-15235. +TEST(SimpleRegexTest, PipeCharacterDisallowed) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^(a(a|$)|b", "", &tightness); + ASSERT_EQUALS(prefix, ""); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(SimpleRegexTest, PipeCharacterDisallowed2) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^(a(a|$)|^b", "", &tightness); + ASSERT_EQUALS(prefix, ""); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +// SERVER-9035 +TEST(SimpleRegexTest, RootedSingleLineMode) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("^foo", "s", &tightness); + ASSERT_EQUALS(prefix, "foo"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +// SERVER-9035 +TEST(SimpleRegexTest, NonRootedSingleLineMode) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = IndexBoundsBuilder::simpleRegex("foo", "s", &tightness); + ASSERT_EQUALS(prefix, ""); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +// SERVER-9035 +TEST(SimpleRegexTest, RootedComplexSingleLineMode) { + IndexBoundsBuilder::BoundsTightness tightness; + string prefix = + IndexBoundsBuilder::simpleRegex("\\Af \t\vo\n\ro \\ \\# #comment", "msx", &tightness); + ASSERT_EQUALS(prefix, "foo #"); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::INEXACT_COVERED); +} + +// +// Regex bounds +// + +TEST(IndexBoundsBuilderTest, SimpleNonPrefixRegex) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: /foo/}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': '', '': {}}"), true, false))); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': /foo/, '': /foo/}"), true, true))); + ASSERT(tightness == IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(IndexBoundsBuilderTest, NonSimpleRegexWithPipe) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: /^foo.*|bar/}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': '', '': {}}"), true, false))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[1].compare( + Interval(fromjson("{'': /^foo.*|bar/, '': /^foo.*|bar/}"), true, true))); + ASSERT(tightness == IndexBoundsBuilder::INEXACT_COVERED); +} + +TEST(IndexBoundsBuilderTest, SimpleRegexSingleLineMode) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: /^foo/s}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 'foo', '': 'fop'}"), true, false))); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': /^foo/s, '': /^foo/s}"), true, true))); + ASSERT(tightness == IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, SimplePrefixRegex) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: /^foo/}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(fromjson("{'': 'foo', '': 'fop'}"), true, false))); + ASSERT_EQUALS( + Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(fromjson("{'': /^foo/, '': /^foo/}"), true, true))); + ASSERT(tightness == IndexBoundsBuilder::EXACT); +} + +// +// isSingleInterval +// + +TEST(IndexBoundsBuilderTest, SingleFieldEqualityInterval) { + // Equality on a single field is a single interval. + OrderedIntervalList oil("a"); + IndexBounds bounds; + oil.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + bounds.fields.push_back(oil); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, SingleIntervalSingleFieldInterval) { + // Single interval on a single field is a single interval. + OrderedIntervalList oil("a"); + IndexBounds bounds; + oil.intervals.push_back(Interval(fromjson("{ '':5, '':Infinity }"), true, true)); + bounds.fields.push_back(oil); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, MultipleIntervalsSingleFieldInterval) { + // Multiple intervals on a single field is not a single interval. + OrderedIntervalList oil("a"); + IndexBounds bounds; + oil.intervals.push_back(Interval(fromjson("{ '':4, '':5 }"), true, true)); + oil.intervals.push_back(Interval(fromjson("{ '':7, '':Infinity }"), true, true)); + bounds.fields.push_back(oil); + ASSERT(!testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, EqualityTwoFieldsInterval) { + // Equality on two fields is a compound single interval. + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(BSON("" << 6 << "" << 6), true, true)); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, EqualityFirstFieldSingleIntervalSecondFieldInterval) { + // Equality on first field and single interval on second field + // is a compound single interval. + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(fromjson("{ '':6, '':Infinity }"), true, true)); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, SingleIntervalFirstAndSecondFieldsInterval) { + // Single interval on first field and single interval on second field is + // not a compound single interval. + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(fromjson("{ '':-Infinity, '':5 }"), true, true)); + oil_b.intervals.push_back(Interval(fromjson("{ '':6, '':Infinity }"), true, true)); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + ASSERT(!testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, MultipleIntervalsTwoFieldsInterval) { + // Multiple intervals on two fields is not a compound single interval. + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 4 << "" << 4), true, true)); + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(BSON("" << 7 << "" << 7), true, true)); + oil_b.intervals.push_back(Interval(BSON("" << 8 << "" << 8), true, true)); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + ASSERT(!testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, MissingSecondFieldInterval) { + // when second field is not specified, still a compound single interval + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(IndexBoundsBuilder::allValues()); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, EqualityTwoFieldsIntervalThirdInterval) { + // Equality on first two fields and single interval on third is a + // compound single interval. + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + OrderedIntervalList oil_c("c"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(BSON("" << 6 << "" << 6), true, true)); + oil_c.intervals.push_back(Interval(fromjson("{ '':7, '':Infinity }"), true, true)); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + bounds.fields.push_back(oil_c); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, EqualitySingleIntervalMissingInterval) { + // Equality, then Single Interval, then missing is a compound single interval + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + OrderedIntervalList oil_c("c"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(fromjson("{ '':7, '':Infinity }"), true, true)); + oil_c.intervals.push_back(IndexBoundsBuilder::allValues()); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + bounds.fields.push_back(oil_c); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, EqualitySingleMissingMissingInterval) { + // Equality, then single interval, then missing, then missing, + // is a compound single interval + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + OrderedIntervalList oil_c("c"); + OrderedIntervalList oil_d("d"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(fromjson("{ '':7, '':Infinity }"), true, true)); + oil_c.intervals.push_back(IndexBoundsBuilder::allValues()); + oil_d.intervals.push_back(IndexBoundsBuilder::allValues()); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + bounds.fields.push_back(oil_c); + bounds.fields.push_back(oil_d); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, EqualitySingleMissingMissingMixedInterval) { + // Equality, then single interval, then missing, then missing, with mixed order + // fields is a compound single interval. + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + OrderedIntervalList oil_c("c"); + OrderedIntervalList oil_d("d"); + IndexBounds bounds; + Interval allValues = IndexBoundsBuilder::allValues(); + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(fromjson("{ '':7, '':Infinity }"), true, true)); + oil_c.intervals.push_back(allValues); + IndexBoundsBuilder::reverseInterval(&allValues); + oil_d.intervals.push_back(allValues); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + bounds.fields.push_back(oil_c); + bounds.fields.push_back(oil_d); + ASSERT(testSingleInterval(bounds)); +} + +TEST(IndexBoundsBuilderTest, EqualitySingleMissingSingleInterval) { + // Equality, then single interval, then missing, then single interval is not + // a compound single interval. + OrderedIntervalList oil_a("a"); + OrderedIntervalList oil_b("b"); + OrderedIntervalList oil_c("c"); + OrderedIntervalList oil_d("d"); + IndexBounds bounds; + oil_a.intervals.push_back(Interval(BSON("" << 5 << "" << 5), true, true)); + oil_b.intervals.push_back(Interval(fromjson("{ '':7, '':Infinity }"), true, true)); + oil_c.intervals.push_back(IndexBoundsBuilder::allValues()); + oil_d.intervals.push_back(Interval(fromjson("{ '':1, '':Infinity }"), true, true)); + bounds.fields.push_back(oil_a); + bounds.fields.push_back(oil_b); + bounds.fields.push_back(oil_c); + bounds.fields.push_back(oil_d); + ASSERT(!testSingleInterval(bounds)); +} + +// +// Complementing bounds for negations +// - // Test $type bounds for Code With Scoped BSON type. - TEST(IndexBoundsBuilderTest, CodeWithScopeTypeBounds) { - IndexEntry testIndex = IndexEntry(BSONObj()); - BSONObj obj = fromjson("{a: {$type: 15}}"); - unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - BSONElement elt = obj.firstElement(); +/** + * Get a BSONObj which represents the interval from + * MinKey to 'end'. + */ +BSONObj minKeyIntObj(int end) { + BSONObjBuilder bob; + bob.appendMinKey(""); + bob.appendNumber("", end); + return bob.obj(); +} - OrderedIntervalList oil; - IndexBoundsBuilder::BoundsTightness tightness; - IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); - - // Build the expected interval. - BSONObjBuilder bob; - bob.appendCodeWScope("", "", BSONObj()); - bob.appendMaxKey(""); - BSONObj expectedInterval = bob.obj(); - - // Check the output of translate(). - ASSERT_EQUALS(oil.name, "a"); - ASSERT_EQUALS(oil.intervals.size(), 1U); - ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare( - Interval(expectedInterval, true, true))); - ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); - } +/** + * Get a BSONObj which represents the interval from + * 'start' to MaxKey. + */ +BSONObj maxKeyIntObj(int start) { + BSONObjBuilder bob; + bob.appendNumber("", start); + bob.appendMaxKey(""); + return bob.obj(); +} + +// Expected oil: [MinKey, 3), (3, MaxKey] +TEST(IndexBoundsBuilderTest, SimpleNE) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = BSON("a" << BSON("$ne" << 3)); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(minKeyIntObj(3), true, false))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(maxKeyIntObj(3), false, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, IntersectWithNE) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toIntersect; + toIntersect.push_back(fromjson("{a: {$gt: 1}}")); + toIntersect.push_back(fromjson("{a: {$ne: 2}}}")); + toIntersect.push_back(fromjson("{a: {$lte: 6}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndIntersect(toIntersect, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 2U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(BSON("" << 1 << "" << 2), false, false))); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[1].compare(Interval(BSON("" << 2 << "" << 6), false, true))); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +TEST(IndexBoundsBuilderTest, UnionizeWithNE) { + IndexEntry testIndex = IndexEntry(BSONObj()); + vector<BSONObj> toUnionize; + toUnionize.push_back(fromjson("{a: {$ne: 3}}")); + toUnionize.push_back(fromjson("{a: {$ne: 4}}}")); + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + testTranslateAndUnion(toUnionize, &oil, &tightness); + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(IndexBoundsBuilder::allValues())); + ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT); +} + +// Test $type bounds for Code BSON type. +TEST(IndexBoundsBuilderTest, CodeTypeBounds) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$type: 13}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + + // Build the expected interval. + BSONObjBuilder bob; + bob.appendCode("", ""); + bob.appendCodeWScope("", "", BSONObj()); + BSONObj expectedInterval = bob.obj(); + + // Check the output of translate(). + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(expectedInterval, true, true))); + ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); +} + +// Test $type bounds for Code With Scoped BSON type. +TEST(IndexBoundsBuilderTest, CodeWithScopeTypeBounds) { + IndexEntry testIndex = IndexEntry(BSONObj()); + BSONObj obj = fromjson("{a: {$type: 15}}"); + unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); + BSONElement elt = obj.firstElement(); + + OrderedIntervalList oil; + IndexBoundsBuilder::BoundsTightness tightness; + IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness); + + // Build the expected interval. + BSONObjBuilder bob; + bob.appendCodeWScope("", "", BSONObj()); + bob.appendMaxKey(""); + BSONObj expectedInterval = bob.obj(); + + // Check the output of translate(). + ASSERT_EQUALS(oil.name, "a"); + ASSERT_EQUALS(oil.intervals.size(), 1U); + ASSERT_EQUALS(Interval::INTERVAL_EQUALS, + oil.intervals[0].compare(Interval(expectedInterval, true, true))); + ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH); +} } // namespace |