diff options
author | coollog <qingyang.chen@gmail.com> | 2015-06-03 14:03:42 -0400 |
---|---|---|
committer | Ramon Fernandez <ramon.fernandez@mongodb.com> | 2015-06-25 16:28:53 -0400 |
commit | 4fd4ba8c325a673e321edab73e75355e1d5f8e8a (patch) | |
tree | ff053e7493ffae4832248e67cd16e45baa9a5367 | |
parent | b7192873095cf50a4d39d65daac31d9de1db9598 (diff) | |
download | mongo-4fd4ba8c325a673e321edab73e75355e1d5f8e8a.tar.gz |
SERVER-18461 cover range predicates comparing BinData in building index bounds
Closes #973
Signed-off-by: David Storch <david.storch@10gen.com>
(cherry picked from commit a641576b7d811577e28eefc6322e93e3e4a27511)
-rw-r--r-- | jstests/core/bindata_indexonly.js | 68 | ||||
-rw-r--r-- | src/mongo/db/query/index_bounds_builder.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/query/index_bounds_builder_test.cpp | 72 |
3 files changed, 144 insertions, 4 deletions
diff --git a/jstests/core/bindata_indexonly.js b/jstests/core/bindata_indexonly.js new file mode 100644 index 00000000000..ece4a1c82eb --- /dev/null +++ b/jstests/core/bindata_indexonly.js @@ -0,0 +1,68 @@ +/** + * This test ensures that range predicates with a BinData value: + * 1) Return the correct documents. + * 2) Can perform index-only data access. + */ +(function() { + 'use strict'; + + load("jstests/libs/analyze_plan.js"); + + var coll = db.jstests_bindata_indexonly; + + coll.drop(); + assert.writeOK(coll.insert({_id: BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA"), a: 1})); + assert.writeOK(coll.insert({_id: BinData(0, "AQAAAAEBAAVlbl9VSwAAAAAAAAhv"), a: 2})); + assert.writeOK(coll.insert({_id: BinData(0, "AQAAAAEBAAVlbl9VSwAAAAAAAAhz"), a: 3})); + assert.writeOK(coll.insert({_id: BinData(0, "////////////////////////////"), a: 4})); + assert.commandWorked(coll.createIndex({_id: 1, a: 1})); + + function testIndexOnlyBinData(blob) { + var explain = coll.find({$and: [{_id: {$lte: BinData(0, blob)}}, + {_id: {$gte: BinData(0, blob)}}]}, {_id: 1, a: 1}) + .hint({_id: 1, a: 1}) + .explain("executionStats"); + + assert(isIndexOnly(explain.queryPlanner.winningPlan), + "indexonly.BinData(0, " + blob + ") - must be index-only"); + assert.eq(1, explain.executionStats.nReturned, + "EXACTone.BinData(0, " + blob + ") - should only return one in unique set"); + } + + testIndexOnlyBinData("AAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + testIndexOnlyBinData("AQAAAAEBAAVlbl9VSwAAAAAAAAhv"); + testIndexOnlyBinData("AQAAAAEBAAVlbl9VSwAAAAAAAAhz"); + testIndexOnlyBinData("////////////////////////////"); + + var explain; + + explain = coll.find({_id: {$lt: BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA")}}, {_id: 1, a: 1}) + .hint({_id: 1, a: 1}) + .explain("executionStats"); + assert(isIndexOnly(explain), "indexonly.$lt.1 - must be index-only"); + assert.eq(0, explain.executionStats.nReturned, + "correctcount.$lt.1 - not returning correct documents"); + + explain = coll.find({_id: {$gt: BinData(0, "////////////////////////////")}}, {_id: 1, a: 1}) + .hint({_id: 1, a: 1}) + .explain("executionStats"); + assert(isIndexOnly(explain), "indexonly.$gt.2 - must be index-only"); + assert.eq(0, explain.executionStats.nReturned, + "correctcount.$gt.2 - not returning correct documents"); + + explain = coll.find({_id: {$lte: BinData(0, "AQAAAAEBAAVlbl9VSwAAAAAAAAhv")}}, {_id: 1, a: 1}) + .hint({_id: 1, a: 1}) + .explain("executionStats"); + assert(isIndexOnly(explain), "indexonly.$lte.3 - must be index-only"); + assert.eq(2, explain.executionStats.nReturned, + "correctcount.$lte.3 - not returning correct documents"); + + explain = coll.find({_id: {$gte: BinData(0, "AQAAAAEBAAVlbl9VSwAAAAAAAAhz")}}, {_id: 1, a: 1}) + .hint({_id: 1, a: 1}) + .explain("executionStats"); + assert(isIndexOnly(explain), "indexonly.$gte.3 - must be index-only"); + assert.eq(2, explain.executionStats.nReturned, + "correctcount.$gte.3 - not returning correct documents"); + + coll.drop(); +})();
\ No newline at end of file diff --git a/src/mongo/db/query/index_bounds_builder.cpp b/src/mongo/db/query/index_bounds_builder.cpp index b22d5c70158..3df71501269 100644 --- a/src/mongo/db/query/index_bounds_builder.cpp +++ b/src/mongo/db/query/index_bounds_builder.cpp @@ -379,7 +379,7 @@ namespace mongo { verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, typeMatch(dataObj), true)); - if (dataElt.isSimpleType()) { + if (dataElt.isSimpleType() || dataElt.type() == BSONType::BinData) { *tightnessOut = IndexBoundsBuilder::EXACT; } else { @@ -422,7 +422,7 @@ namespace mongo { oilOut->intervals.push_back(interval); } - if (dataElt.isSimpleType()) { + if (dataElt.isSimpleType() || dataElt.type() == BSONType::BinData) { *tightnessOut = IndexBoundsBuilder::EXACT; } else { @@ -464,7 +464,7 @@ namespace mongo { oilOut->intervals.push_back(interval); } - if (dataElt.isSimpleType()) { + if (dataElt.isSimpleType() || dataElt.type() == BSONType::BinData) { *tightnessOut = IndexBoundsBuilder::EXACT; } else { @@ -502,7 +502,7 @@ namespace mongo { verify(dataObj.isOwned()); oilOut->intervals.push_back(makeRangeInterval(dataObj, true, typeMatch(dataObj))); - if (dataElt.isSimpleType()) { + if (dataElt.isSimpleType() || dataElt.type() == BSONType::BinData) { *tightnessOut = IndexBoundsBuilder::EXACT; } else { diff --git a/src/mongo/db/query/index_bounds_builder_test.cpp b/src/mongo/db/query/index_bounds_builder_test.cpp index e82c130b278..ee0ec7b7c83 100644 --- a/src/mongo/db/query/index_bounds_builder_test.cpp +++ b/src/mongo/db/query/index_bounds_builder_test.cpp @@ -459,6 +459,78 @@ namespace { 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 // |