summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcoollog <qingyang.chen@gmail.com>2015-06-03 14:03:42 -0400
committerRamon Fernandez <ramon.fernandez@mongodb.com>2015-06-25 16:28:53 -0400
commit4fd4ba8c325a673e321edab73e75355e1d5f8e8a (patch)
treeff053e7493ffae4832248e67cd16e45baa9a5367
parentb7192873095cf50a4d39d65daac31d9de1db9598 (diff)
downloadmongo-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.js68
-rw-r--r--src/mongo/db/query/index_bounds_builder.cpp8
-rw-r--r--src/mongo/db/query/index_bounds_builder_test.cpp72
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
//