summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTess Avitabile <tess.avitabile@mongodb.com>2016-04-08 15:07:20 -0400
committerTess Avitabile <tess.avitabile@mongodb.com>2016-04-13 10:05:36 -0400
commit4c1e3619a67041f589eb65da29a146629e88e280 (patch)
treee02298ac73578110888c90a492e9e0d63f54b0b8 /src
parent56a903eb64f5b9865f1b54b90334f02cb5adf6dd (diff)
downloadmongo-4c1e3619a67041f589eb65da29a146629e88e280.tar.gz
SERVER-23348 Add a collator to ComparisonMatchExpression, InMatchExpression
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/matcher/SConscript2
-rw-r--r--src/mongo/db/matcher/expression_algo.cpp3
-rw-r--r--src/mongo/db/matcher/expression_array_test.cpp63
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp20
-rw-r--r--src/mongo/db/matcher/expression_leaf.h56
-rw-r--r--src/mongo/db/matcher/expression_leaf_test.cpp297
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp38
-rw-r--r--src/mongo/db/matcher/expression_test.cpp15
-rw-r--r--src/mongo/db/matcher/expression_tree_test.cpp64
-rw-r--r--src/mongo/db/query/SConscript1
-rw-r--r--src/mongo/db/query/canonical_query.cpp2
-rw-r--r--src/mongo/db/query/canonical_query_test.cpp14
-rw-r--r--src/mongo/db/query/collation/collator_interface.h13
-rw-r--r--src/mongo/db/query/collation/collator_interface_mock_test.cpp22
-rw-r--r--src/mongo/db/query/planner_ixselect.cpp12
15 files changed, 446 insertions, 176 deletions
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript
index 462113742d1..5edaa348843 100644
--- a/src/mongo/db/matcher/SConscript
+++ b/src/mongo/db/matcher/SConscript
@@ -49,6 +49,7 @@ env.Library(
'$BUILD_DIR/mongo/bson/util/bson_extract',
'$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/db/fts/fts_query_noop',
+ '$BUILD_DIR/mongo/db/query/collation/collator_interface',
'$BUILD_DIR/third_party/shim_pcrecpp',
'path',
],
@@ -63,6 +64,7 @@ env.CppUnitTest(
'expression_tree_test.cpp',
],
LIBDEPS=[
+ '$BUILD_DIR/mongo/db/query/collation/collator_interface_mock',
'expressions',
],
)
diff --git a/src/mongo/db/matcher/expression_algo.cpp b/src/mongo/db/matcher/expression_algo.cpp
index 79059c204d1..39f22830480 100644
--- a/src/mongo/db/matcher/expression_algo.cpp
+++ b/src/mongo/db/matcher/expression_algo.cpp
@@ -154,7 +154,8 @@ bool _isSubsetOf(const MatchExpression* lhs, const ComparisonMatchExpression* rh
}
for (BSONElement elem : arrayEntries.equalities()) {
// Each element in the $in-array represents an equality predicate.
- EqualityMatchExpression equality;
+ // TODO SERVER-23618: pass the appropriate collator to EqualityMatchExpression().
+ EqualityMatchExpression equality(nullptr);
equality.init(lhs->path(), elem);
if (!_isSubsetOf(&equality, rhs)) {
return false;
diff --git a/src/mongo/db/matcher/expression_array_test.cpp b/src/mongo/db/matcher/expression_array_test.cpp
index 201d11fbed4..e3ef241ef87 100644
--- a/src/mongo/db/matcher/expression_array_test.cpp
+++ b/src/mongo/db/matcher/expression_array_test.cpp
@@ -44,7 +44,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementSingle) {
BSONObj baseOperand = BSON("b" << 5);
BSONObj match = BSON("a" << BSON_ARRAY(BSON("b" << 5.0)));
BSONObj notMatch = BSON("a" << BSON_ARRAY(BSON("b" << 6)));
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("b", baseOperand["b"]).isOK());
ElemMatchObjectMatchExpression op;
ASSERT(op.init("a", eq.release()).isOK());
@@ -56,7 +57,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementArray) {
BSONObj baseOperand = BSON("1" << 5);
BSONObj match = BSON("a" << BSON_ARRAY(BSON_ARRAY('s' << 5.0)));
BSONObj notMatch = BSON("a" << BSON_ARRAY(BSON_ARRAY(5 << 6)));
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("1", baseOperand["1"]).isOK());
ElemMatchObjectMatchExpression op;
ASSERT(op.init("a", eq.release()).isOK());
@@ -72,11 +74,12 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementMultiple) {
BSONObj notMatch2 = BSON("a" << BSON_ARRAY(BSON("b" << 6 << "c" << 7)));
BSONObj notMatch3 = BSON("a" << BSON_ARRAY(BSON("b" << BSON_ARRAY(5 << 6))));
BSONObj match = BSON("a" << BSON_ARRAY(BSON("b" << BSON_ARRAY(5 << 6) << "c" << 7)));
- unique_ptr<ComparisonMatchExpression> eq1(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq1(new EqualityMatchExpression(collator));
ASSERT(eq1->init("b", baseOperand1["b"]).isOK());
- unique_ptr<ComparisonMatchExpression> eq2(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> eq2(new EqualityMatchExpression(collator));
ASSERT(eq2->init("b", baseOperand2["b"]).isOK());
- unique_ptr<ComparisonMatchExpression> eq3(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> eq3(new EqualityMatchExpression(collator));
ASSERT(eq3->init("c", baseOperand3["c"]).isOK());
unique_ptr<AndMatchExpression> andOp(new AndMatchExpression());
@@ -94,7 +97,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesElementMultiple) {
TEST(ElemMatchObjectMatchExpression, MatchesNonArray) {
BSONObj baseOperand = BSON("b" << 5);
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("b", baseOperand["b"]).isOK());
ElemMatchObjectMatchExpression op;
ASSERT(op.init("a", eq.release()).isOK());
@@ -107,7 +111,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesNonArray) {
TEST(ElemMatchObjectMatchExpression, MatchesArrayObject) {
BSONObj baseOperand = BSON("b" << 5);
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("b", baseOperand["b"]).isOK());
ElemMatchObjectMatchExpression op;
ASSERT(op.init("a", eq.release()).isOK());
@@ -119,7 +124,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesArrayObject) {
TEST(ElemMatchObjectMatchExpression, MatchesMultipleNamedValues) {
BSONObj baseOperand = BSON("c" << 5);
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("c", baseOperand["c"]).isOK());
ElemMatchObjectMatchExpression op;
ASSERT(op.init("a.b", eq.release()).isOK());
@@ -131,7 +137,8 @@ TEST(ElemMatchObjectMatchExpression, MatchesMultipleNamedValues) {
TEST(ElemMatchObjectMatchExpression, ElemMatchKey) {
BSONObj baseOperand = BSON("c" << 6);
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("c", baseOperand["c"]).isOK());
ElemMatchObjectMatchExpression op;
ASSERT(op.init("a.b", eq.release()).isOK());
@@ -171,7 +178,8 @@ TEST(ElemMatchValueMatchExpression, MatchesElementSingle) {
BSONObj baseOperand = BSON("$gt" << 5);
BSONObj match = BSON("a" << BSON_ARRAY(6));
BSONObj notMatch = BSON("a" << BSON_ARRAY(4));
- unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator));
ASSERT(gt->init("", baseOperand["$gt"]).isOK());
ElemMatchValueMatchExpression op;
ASSERT(op.init("a", gt.release()).isOK());
@@ -185,9 +193,10 @@ TEST(ElemMatchValueMatchExpression, MatchesElementMultiple) {
BSONObj notMatch1 = BSON("a" << BSON_ARRAY(0 << 1));
BSONObj notMatch2 = BSON("a" << BSON_ARRAY(10 << 11));
BSONObj match = BSON("a" << BSON_ARRAY(0 << 5 << 11));
- unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator));
ASSERT(gt->init("", baseOperand1["$gt"]).isOK());
- unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator));
ASSERT(lt->init("", baseOperand2["$lt"]).isOK());
ElemMatchValueMatchExpression op;
@@ -202,7 +211,8 @@ TEST(ElemMatchValueMatchExpression, MatchesElementMultiple) {
TEST(ElemMatchValueMatchExpression, MatchesNonArray) {
BSONObj baseOperand = BSON("$gt" << 5);
- unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator));
ASSERT(gt->init("", baseOperand["$gt"]).isOK());
ElemMatchObjectMatchExpression op;
ASSERT(op.init("a", gt.release()).isOK());
@@ -214,7 +224,8 @@ TEST(ElemMatchValueMatchExpression, MatchesNonArray) {
TEST(ElemMatchValueMatchExpression, MatchesArrayScalar) {
BSONObj baseOperand = BSON("$gt" << 5);
- unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator));
ASSERT(gt->init("", baseOperand["$gt"]).isOK());
ElemMatchValueMatchExpression op;
ASSERT(op.init("a", gt.release()).isOK());
@@ -225,7 +236,8 @@ TEST(ElemMatchValueMatchExpression, MatchesArrayScalar) {
TEST(ElemMatchValueMatchExpression, MatchesMultipleNamedValues) {
BSONObj baseOperand = BSON("$gt" << 5);
- unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator));
ASSERT(gt->init("", baseOperand["$gt"]).isOK());
ElemMatchValueMatchExpression op;
ASSERT(op.init("a.b", gt.release()).isOK());
@@ -237,7 +249,8 @@ TEST(ElemMatchValueMatchExpression, MatchesMultipleNamedValues) {
TEST(ElemMatchValueMatchExpression, ElemMatchKey) {
BSONObj baseOperand = BSON("$gt" << 6);
- unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> gt(new GTMatchExpression(collator));
ASSERT(gt->init("", baseOperand["$gt"]).isOK());
ElemMatchValueMatchExpression op;
ASSERT(op.init("a.b", gt.release()).isOK());
@@ -274,11 +287,12 @@ TEST( ElemMatchValueMatchExpression, MatchesIndexKey ) {
TEST(AndOfElemMatch, MatchesElement) {
BSONObj baseOperanda1 = BSON("a" << 1);
- unique_ptr<ComparisonMatchExpression> eqa1(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eqa1(new EqualityMatchExpression(collator));
ASSERT(eqa1->init("a", baseOperanda1["a"]).isOK());
BSONObj baseOperandb1 = BSON("b" << 1);
- unique_ptr<ComparisonMatchExpression> eqb1(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> eqb1(new EqualityMatchExpression(collator));
ASSERT(eqb1->init("b", baseOperandb1["b"]).isOK());
unique_ptr<AndMatchExpression> and1(new AndMatchExpression());
@@ -291,11 +305,11 @@ TEST(AndOfElemMatch, MatchesElement) {
// elemMatch1 = { x : { $elemMatch : { a : 1, b : 1 } } }
BSONObj baseOperanda2 = BSON("a" << 2);
- unique_ptr<ComparisonMatchExpression> eqa2(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> eqa2(new EqualityMatchExpression(collator));
ASSERT(eqa2->init("a", baseOperanda2["a"]).isOK());
BSONObj baseOperandb2 = BSON("b" << 2);
- unique_ptr<ComparisonMatchExpression> eqb2(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> eqb2(new EqualityMatchExpression(collator));
ASSERT(eqb2->init("b", baseOperandb2["b"]).isOK());
unique_ptr<AndMatchExpression> and2(new AndMatchExpression());
@@ -331,11 +345,12 @@ TEST(AndOfElemMatch, MatchesElement) {
TEST(AndOfElemMatch, Matches) {
BSONObj baseOperandgt1 = BSON("$gt" << 1);
- unique_ptr<ComparisonMatchExpression> gt1(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> gt1(new GTMatchExpression(collator));
ASSERT(gt1->init("", baseOperandgt1["$gt"]).isOK());
BSONObj baseOperandlt1 = BSON("$lt" << 10);
- unique_ptr<ComparisonMatchExpression> lt1(new LTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> lt1(new LTMatchExpression(collator));
ASSERT(lt1->init("", baseOperandlt1["$lt"]).isOK());
unique_ptr<ElemMatchValueMatchExpression> elemMatch1(new ElemMatchValueMatchExpression());
@@ -345,11 +360,11 @@ TEST(AndOfElemMatch, Matches) {
// elemMatch1 = { x : { $elemMatch : { $gt : 1 , $lt : 10 } } }
BSONObj baseOperandgt2 = BSON("$gt" << 101);
- unique_ptr<ComparisonMatchExpression> gt2(new GTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> gt2(new GTMatchExpression(collator));
ASSERT(gt2->init("", baseOperandgt2["$gt"]).isOK());
BSONObj baseOperandlt2 = BSON("$lt" << 110);
- unique_ptr<ComparisonMatchExpression> lt2(new LTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> lt2(new LTMatchExpression(collator));
ASSERT(lt2->init("", baseOperandlt2["$lt"]).isOK());
unique_ptr<ElemMatchValueMatchExpression> elemMatch2(new ElemMatchValueMatchExpression());
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index 491643ef6ec..a59028078ba 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -40,6 +40,7 @@
#include "mongo/db/field_ref.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/path.h"
+#include "mongo/db/query/collation/collator_interface.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/mongoutils/str.h"
@@ -73,6 +74,10 @@ bool ComparisonMatchExpression::equivalent(const MatchExpression* other) const {
const ComparisonMatchExpression* realOther =
static_cast<const ComparisonMatchExpression*>(other);
+ if (!CollatorInterface::collatorsMatch(_collator, realOther->_collator)) {
+ return false;
+ }
+
return path() == realOther->path() && _rhs.valuesEqual(realOther->_rhs);
}
@@ -143,7 +148,7 @@ bool ComparisonMatchExpression::matchesSingleElement(const BSONElement& e) const
}
}
- int x = compareElementValues(e, _rhs);
+ int x = compareElementValues(e, _rhs, _collator);
// log() << "\t\t" << x << endl;
@@ -519,10 +524,8 @@ bool TypeMatchExpression::equivalent(const MatchExpression* other) const {
// --------
-ArrayFilterEntries::ArrayFilterEntries() {
- _hasNull = false;
- _hasEmptyArray = false;
-}
+ArrayFilterEntries::ArrayFilterEntries(CollatorInterface* collator)
+ : _hasNull(false), _hasEmptyArray(false), _equalities(collator), _collator(collator) {}
ArrayFilterEntries::~ArrayFilterEntries() {
for (unsigned i = 0; i < _regexes.size(); i++)
@@ -564,6 +567,10 @@ bool ArrayFilterEntries::equivalent(const ArrayFilterEntries& other) const {
if (!_regexes[i]->equivalent(other._regexes[i]))
return false;
+ if (!CollatorInterface::collatorsMatch(_collator, other._collator)) {
+ return false;
+ }
+
return _equalities == other._equalities;
}
@@ -666,7 +673,8 @@ bool InMatchExpression::equivalent(const MatchExpression* other) const {
}
std::unique_ptr<MatchExpression> InMatchExpression::shallowClone() const {
- std::unique_ptr<InMatchExpression> next = stdx::make_unique<InMatchExpression>();
+ std::unique_ptr<InMatchExpression> next =
+ stdx::make_unique<InMatchExpression>(_arrayEntries.getCollator());
copyTo(next.get());
if (getTag()) {
next->setTag(getTag()->clone());
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index 1f1b305e141..957f328f1b1 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -43,6 +43,8 @@ class RE;
namespace mongo {
+class CollatorInterface;
+
/**
* This file contains leaves in the parse tree that are not array-based.
*
@@ -83,7 +85,11 @@ private:
*/
class ComparisonMatchExpression : public LeafMatchExpression {
public:
- ComparisonMatchExpression(MatchType type) : LeafMatchExpression(type) {}
+ /**
+ * 'collator' must outlive the ComparisonMatchExpression and any clones made of it.
+ */
+ ComparisonMatchExpression(MatchType type, CollatorInterface* collator)
+ : LeafMatchExpression(type), _collator(collator) {}
Status init(StringData path, const BSONElement& rhs);
@@ -105,8 +111,13 @@ public:
return _rhs;
}
+ CollatorInterface* getCollator() const {
+ return _collator;
+ }
+
protected:
BSONElement _rhs;
+ CollatorInterface* _collator;
};
//
@@ -115,9 +126,11 @@ protected:
class EqualityMatchExpression : public ComparisonMatchExpression {
public:
- EqualityMatchExpression() : ComparisonMatchExpression(EQ) {}
+ EqualityMatchExpression(CollatorInterface* collator)
+ : ComparisonMatchExpression(EQ, collator) {}
virtual std::unique_ptr<MatchExpression> shallowClone() const {
- std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<EqualityMatchExpression>();
+ std::unique_ptr<ComparisonMatchExpression> e =
+ stdx::make_unique<EqualityMatchExpression>(_collator);
e->init(path(), _rhs);
if (getTag()) {
e->setTag(getTag()->clone());
@@ -128,9 +141,10 @@ public:
class LTEMatchExpression : public ComparisonMatchExpression {
public:
- LTEMatchExpression() : ComparisonMatchExpression(LTE) {}
+ LTEMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(LTE, collator) {}
virtual std::unique_ptr<MatchExpression> shallowClone() const {
- std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<LTEMatchExpression>();
+ std::unique_ptr<ComparisonMatchExpression> e =
+ stdx::make_unique<LTEMatchExpression>(_collator);
e->init(path(), _rhs);
if (getTag()) {
e->setTag(getTag()->clone());
@@ -141,9 +155,10 @@ public:
class LTMatchExpression : public ComparisonMatchExpression {
public:
- LTMatchExpression() : ComparisonMatchExpression(LT) {}
+ LTMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(LT, collator) {}
virtual std::unique_ptr<MatchExpression> shallowClone() const {
- std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<LTMatchExpression>();
+ std::unique_ptr<ComparisonMatchExpression> e =
+ stdx::make_unique<LTMatchExpression>(_collator);
e->init(path(), _rhs);
if (getTag()) {
e->setTag(getTag()->clone());
@@ -154,9 +169,10 @@ public:
class GTMatchExpression : public ComparisonMatchExpression {
public:
- GTMatchExpression() : ComparisonMatchExpression(GT) {}
+ GTMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(GT, collator) {}
virtual std::unique_ptr<MatchExpression> shallowClone() const {
- std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<GTMatchExpression>();
+ std::unique_ptr<ComparisonMatchExpression> e =
+ stdx::make_unique<GTMatchExpression>(_collator);
e->init(path(), _rhs);
if (getTag()) {
e->setTag(getTag()->clone());
@@ -167,9 +183,10 @@ public:
class GTEMatchExpression : public ComparisonMatchExpression {
public:
- GTEMatchExpression() : ComparisonMatchExpression(GTE) {}
+ GTEMatchExpression(CollatorInterface* collator) : ComparisonMatchExpression(GTE, collator) {}
virtual std::unique_ptr<MatchExpression> shallowClone() const {
- std::unique_ptr<ComparisonMatchExpression> e = stdx::make_unique<GTEMatchExpression>();
+ std::unique_ptr<ComparisonMatchExpression> e =
+ stdx::make_unique<GTEMatchExpression>(_collator);
e->init(path(), _rhs);
if (getTag()) {
e->setTag(getTag()->clone());
@@ -298,7 +315,7 @@ class ArrayFilterEntries {
MONGO_DISALLOW_COPYING(ArrayFilterEntries);
public:
- ArrayFilterEntries();
+ ArrayFilterEntries(CollatorInterface* collator);
~ArrayFilterEntries();
Status addEquality(const BSONElement& e);
@@ -331,6 +348,10 @@ public:
return _equalities.size() + _regexes.size();
}
+ CollatorInterface* getCollator() const {
+ return _collator;
+ }
+
bool equivalent(const ArrayFilterEntries& other) const;
void copyTo(ArrayFilterEntries& toFillIn) const;
@@ -344,6 +365,7 @@ private:
bool _hasEmptyArray;
BSONElementSet _equalities;
std::vector<RegexMatchExpression*> _regexes;
+ CollatorInterface* _collator;
};
/**
@@ -351,7 +373,11 @@ private:
*/
class InMatchExpression : public LeafMatchExpression {
public:
- InMatchExpression() : LeafMatchExpression(MATCH_IN) {}
+ /**
+ * 'collator' must outlive the InMatchExpression and any clones made of it.
+ */
+ InMatchExpression(CollatorInterface* collator)
+ : LeafMatchExpression(MATCH_IN), _arrayEntries(collator) {}
Status init(StringData path);
virtual std::unique_ptr<MatchExpression> shallowClone() const;
@@ -374,6 +400,10 @@ public:
return _arrayEntries;
}
+ CollatorInterface* getCollator() const {
+ return _arrayEntries.getCollator();
+ }
+
private:
bool _matchesRealElement(const BSONElement& e) const;
ArrayFilterEntries _arrayEntries;
diff --git a/src/mongo/db/matcher/expression_leaf_test.cpp b/src/mongo/db/matcher/expression_leaf_test.cpp
index b2946f5f546..68935fbd8b9 100644
--- a/src/mongo/db/matcher/expression_leaf_test.cpp
+++ b/src/mongo/db/matcher/expression_leaf_test.cpp
@@ -35,17 +35,57 @@
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_leaf.h"
+#include "mongo/db/query/collation/collator_interface_mock.h"
namespace mongo {
using std::string;
+TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithUnequalCollatorsAreUnequal) {
+ CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString);
+ EqualityMatchExpression eq1(&collator1);
+ CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ EqualityMatchExpression eq2(&collator2);
+ ASSERT(!eq1.equivalent(&eq2));
+}
+
+TEST(ComparisonMatchExpression, ComparisonMatchExpressionsWithEqualCollatorsAreEqual) {
+ CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ EqualityMatchExpression eq1(&collator1);
+ CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ EqualityMatchExpression eq2(&collator2);
+ ASSERT(eq1.equivalent(&eq2));
+}
+
+TEST(ComparisonMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) {
+ BSONObj operand = BSON("a"
+ << "string");
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
+ ASSERT(eq.init("a", operand["a"]).isOK());
+ ASSERT(!eq.matchesBSON(BSON("a"
+ << "string2"),
+ NULL));
+}
+
+TEST(ComparisonMatchExpression, StringMatchingRespectsCollation) {
+ BSONObj operand = BSON("a"
+ << "string");
+ CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ EqualityMatchExpression eq(&collator);
+ ASSERT(eq.init("a", operand["a"]).isOK());
+ ASSERT(eq.matchesBSON(BSON("a"
+ << "string2"),
+ NULL));
+}
+
TEST(EqOp, MatchesElement) {
BSONObj operand = BSON("a" << 5);
BSONObj match = BSON("a" << 5.0);
BSONObj notMatch = BSON("a" << 6);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("", operand["a"]);
ASSERT(eq.matchesSingleElement(match.firstElement()));
ASSERT(!eq.matchesSingleElement(notMatch.firstElement()));
@@ -55,13 +95,15 @@ TEST(EqOp, MatchesElement) {
TEST(EqOp, InvalidEooOperand) {
BSONObj operand;
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
ASSERT(!eq.init("", operand.firstElement()).isOK());
}
TEST(EqOp, MatchesScalar) {
BSONObj operand = BSON("a" << 5);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a", operand["a"]);
ASSERT(eq.matchesBSON(BSON("a" << 5.0), NULL));
ASSERT(!eq.matchesBSON(BSON("a" << 4), NULL));
@@ -69,7 +111,8 @@ TEST(EqOp, MatchesScalar) {
TEST(EqOp, MatchesArrayValue) {
BSONObj operand = BSON("a" << 5);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a", operand["a"]);
ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(5.0 << 6)), NULL));
ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL));
@@ -77,7 +120,8 @@ TEST(EqOp, MatchesArrayValue) {
TEST(EqOp, MatchesReferencedObjectValue) {
BSONObj operand = BSON("a.b" << 5);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a.b", operand["a.b"]);
ASSERT(eq.matchesBSON(BSON("a" << BSON("b" << 5)), NULL));
ASSERT(eq.matchesBSON(BSON("a" << BSON("b" << BSON_ARRAY(5))), NULL));
@@ -86,7 +130,8 @@ TEST(EqOp, MatchesReferencedObjectValue) {
TEST(EqOp, MatchesReferencedArrayValue) {
BSONObj operand = BSON("a.0" << 5);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a.0", operand["a.0"]);
ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL));
ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(5))), NULL));
@@ -94,7 +139,8 @@ TEST(EqOp, MatchesReferencedArrayValue) {
TEST(EqOp, MatchesNull) {
BSONObj operand = BSON("a" << BSONNULL);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a", operand["a"]);
ASSERT(eq.matchesBSON(BSONObj(), NULL));
ASSERT(eq.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -107,7 +153,8 @@ TEST(EqOp, MatchesNull) {
// not necessarily how it should work ideally.
TEST(EqOp, MatchesNestedNull) {
BSONObj operand = BSON("a.b" << BSONNULL);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a.b", operand["a.b"]);
// null matches any empty object that is on a subpath of a.b
ASSERT(eq.matchesBSON(BSONObj(), NULL));
@@ -127,7 +174,8 @@ TEST(EqOp, MatchesNestedNull) {
TEST(EqOp, MatchesMinKey) {
BSONObj operand = BSON("a" << MinKey);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a", operand["a"]);
ASSERT(eq.matchesBSON(BSON("a" << MinKey), NULL));
ASSERT(!eq.matchesBSON(BSON("a" << MaxKey), NULL));
@@ -137,7 +185,8 @@ TEST(EqOp, MatchesMinKey) {
TEST(EqOp, MatchesMaxKey) {
BSONObj operand = BSON("a" << MaxKey);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
ASSERT(eq.init("a", operand["a"]).isOK());
ASSERT(eq.matchesBSON(BSON("a" << MaxKey), NULL));
ASSERT(!eq.matchesBSON(BSON("a" << MinKey), NULL));
@@ -146,7 +195,8 @@ TEST(EqOp, MatchesMaxKey) {
TEST(EqOp, MatchesFullArray) {
BSONObj operand = BSON("a" << BSON_ARRAY(1 << 2));
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
ASSERT(eq.init("a", operand["a"]).isOK());
ASSERT(eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL));
ASSERT(!eq.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3)), NULL));
@@ -156,7 +206,8 @@ TEST(EqOp, MatchesFullArray) {
TEST(EqOp, MatchesThroughNestedArray) {
BSONObj operand = BSON("a.b.c.d" << 3);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
eq.init("a.b.c.d", operand["a.b.c.d"]);
BSONObj obj = fromjson("{a:{b:[{c:[{d:1},{d:2}]},{c:[{d:3}]}]}}");
ASSERT(eq.matchesBSON(obj, NULL));
@@ -164,7 +215,8 @@ TEST(EqOp, MatchesThroughNestedArray) {
TEST(EqOp, ElemMatchKey) {
BSONObj operand = BSON("a" << 5);
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
ASSERT(eq.init("a", operand["a"]).isOK());
MatchDetails details;
details.requestElemMatchKey();
@@ -183,7 +235,8 @@ TEST(EqOp, ElemMatchKey) {
TEST(EqOp, ElemMatchKeyWithImplicitAndExplicitTraversal) {
BSONObj operand = BSON("a.0.b" << 3);
BSONElement operandFirstElt = operand.firstElement();
- EqualityMatchExpression eq;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq(collator);
ASSERT(eq.init(operandFirstElt.fieldName(), operandFirstElt).isOK());
MatchDetails details;
details.requestElemMatchKey();
@@ -194,9 +247,10 @@ TEST(EqOp, ElemMatchKeyWithImplicitAndExplicitTraversal) {
}
TEST(EqOp, Equality1) {
- EqualityMatchExpression eq1;
- EqualityMatchExpression eq2;
- EqualityMatchExpression eq3;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression eq1(collator);
+ EqualityMatchExpression eq2(collator);
+ EqualityMatchExpression eq3(collator);
BSONObj operand = BSON("a" << 5 << "b" << 5 << "c" << 4);
@@ -266,7 +320,8 @@ TEST(LtOp, MatchesElement) {
BSONObj notMatchEqual = BSON("a" << 5);
BSONObj notMatchWrongType = BSON("a"
<< "foo");
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("", operand["$lt"]).isOK());
ASSERT(lt.matchesSingleElement(match.firstElement()));
ASSERT(!lt.matchesSingleElement(notMatch.firstElement()));
@@ -276,13 +331,15 @@ TEST(LtOp, MatchesElement) {
TEST(LtOp, InvalidEooOperand) {
BSONObj operand;
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(!lt.init("", operand.firstElement()).isOK());
}
TEST(LtOp, MatchesScalar) {
BSONObj operand = BSON("$lt" << 5);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a", operand["$lt"]).isOK());
ASSERT(lt.matchesBSON(BSON("a" << 4.5), NULL));
ASSERT(!lt.matchesBSON(BSON("a" << 6), NULL));
@@ -290,7 +347,8 @@ TEST(LtOp, MatchesScalar) {
TEST(LtOp, MatchesScalarEmptyKey) {
BSONObj operand = BSON("$lt" << 5);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("", operand["$lt"]).isOK());
ASSERT(lt.matchesBSON(BSON("" << 4.5), NULL));
ASSERT(!lt.matchesBSON(BSON("" << 6), NULL));
@@ -298,7 +356,8 @@ TEST(LtOp, MatchesScalarEmptyKey) {
TEST(LtOp, MatchesArrayValue) {
BSONObj operand = BSON("$lt" << 5);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a", operand["$lt"]).isOK());
ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(6 << 4.5)), NULL));
ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL));
@@ -306,7 +365,8 @@ TEST(LtOp, MatchesArrayValue) {
TEST(LtOp, MatchesWholeArray) {
BSONObj operand = BSON("$lt" << BSON_ARRAY(5));
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a", operand["$lt"]).isOK());
ASSERT(lt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL));
ASSERT(!lt.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL));
@@ -319,7 +379,8 @@ TEST(LtOp, MatchesWholeArray) {
TEST(LtOp, MatchesNull) {
BSONObj operand = BSON("$lt" << BSONNULL);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a", operand["$lt"]).isOK());
ASSERT(!lt.matchesBSON(BSONObj(), NULL));
ASSERT(!lt.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -330,7 +391,8 @@ TEST(LtOp, MatchesNull) {
TEST(LtOp, MatchesDotNotationNull) {
BSONObj operand = BSON("$lt" << BSONNULL);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a.b", operand["$lt"]).isOK());
ASSERT(!lt.matchesBSON(BSONObj(), NULL));
ASSERT(!lt.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -344,7 +406,8 @@ TEST(LtOp, MatchesDotNotationNull) {
TEST(LtOp, MatchesMinKey) {
BSONObj operand = BSON("a" << MinKey);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a", operand["a"]).isOK());
ASSERT(!lt.matchesBSON(BSON("a" << MinKey), NULL));
ASSERT(!lt.matchesBSON(BSON("a" << MaxKey), NULL));
@@ -353,7 +416,8 @@ TEST(LtOp, MatchesMinKey) {
TEST(LtOp, MatchesMaxKey) {
BSONObj operand = BSON("a" << MaxKey);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a", operand["a"]).isOK());
ASSERT(!lt.matchesBSON(BSON("a" << MaxKey), NULL));
ASSERT(lt.matchesBSON(BSON("a" << MinKey), NULL));
@@ -362,7 +426,8 @@ TEST(LtOp, MatchesMaxKey) {
TEST(LtOp, ElemMatchKey) {
BSONObj operand = BSON("$lt" << 5);
- LTMatchExpression lt;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression lt(collator);
ASSERT(lt.init("a", operand["$lt"]).isOK());
MatchDetails details;
details.requestElemMatchKey();
@@ -432,7 +497,8 @@ TEST(LteOp, MatchesElement) {
BSONObj notMatch = BSON("a" << 6);
BSONObj notMatchWrongType = BSON("a"
<< "foo");
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("", operand["$lte"]).isOK());
ASSERT(lte.matchesSingleElement(match.firstElement()));
ASSERT(lte.matchesSingleElement(equalMatch.firstElement()));
@@ -442,13 +508,15 @@ TEST(LteOp, MatchesElement) {
TEST(LteOp, InvalidEooOperand) {
BSONObj operand;
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(!lte.init("", operand.firstElement()).isOK());
}
TEST(LteOp, MatchesScalar) {
BSONObj operand = BSON("$lte" << 5);
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a", operand["$lte"]).isOK());
ASSERT(lte.matchesBSON(BSON("a" << 4.5), NULL));
ASSERT(!lte.matchesBSON(BSON("a" << 6), NULL));
@@ -456,7 +524,8 @@ TEST(LteOp, MatchesScalar) {
TEST(LteOp, MatchesArrayValue) {
BSONObj operand = BSON("$lte" << 5);
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a", operand["$lte"]).isOK());
ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(6 << 4.5)), NULL));
ASSERT(!lte.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL));
@@ -464,7 +533,8 @@ TEST(LteOp, MatchesArrayValue) {
TEST(LteOp, MatchesWholeArray) {
BSONObj operand = BSON("$lte" << BSON_ARRAY(5));
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a", operand["$lte"]).isOK());
ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL));
ASSERT(lte.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL));
@@ -477,7 +547,8 @@ TEST(LteOp, MatchesWholeArray) {
TEST(LteOp, MatchesNull) {
BSONObj operand = BSON("$lte" << BSONNULL);
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a", operand["$lte"]).isOK());
ASSERT(lte.matchesBSON(BSONObj(), NULL));
ASSERT(lte.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -488,7 +559,8 @@ TEST(LteOp, MatchesNull) {
TEST(LteOp, MatchesDotNotationNull) {
BSONObj operand = BSON("$lte" << BSONNULL);
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a.b", operand["$lte"]).isOK());
ASSERT(lte.matchesBSON(BSONObj(), NULL));
ASSERT(lte.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -502,7 +574,8 @@ TEST(LteOp, MatchesDotNotationNull) {
TEST(LteOp, MatchesMinKey) {
BSONObj operand = BSON("a" << MinKey);
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a", operand["a"]).isOK());
ASSERT(lte.matchesBSON(BSON("a" << MinKey), NULL));
ASSERT(!lte.matchesBSON(BSON("a" << MaxKey), NULL));
@@ -511,7 +584,8 @@ TEST(LteOp, MatchesMinKey) {
TEST(LteOp, MatchesMaxKey) {
BSONObj operand = BSON("a" << MaxKey);
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a", operand["a"]).isOK());
ASSERT(lte.matchesBSON(BSON("a" << MaxKey), NULL));
ASSERT(lte.matchesBSON(BSON("a" << MinKey), NULL));
@@ -521,7 +595,8 @@ TEST(LteOp, MatchesMaxKey) {
TEST(LteOp, ElemMatchKey) {
BSONObj operand = BSON("$lte" << 5);
- LTEMatchExpression lte;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression lte(collator);
ASSERT(lte.init("a", operand["$lte"]).isOK());
MatchDetails details;
details.requestElemMatchKey();
@@ -601,13 +676,15 @@ TEST(LteOp, ElemMatchKey) {
TEST(GtOp, InvalidEooOperand) {
BSONObj operand;
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(!gt.init("", operand.firstElement()).isOK());
}
TEST(GtOp, MatchesScalar) {
BSONObj operand = BSON("$gt" << 5);
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a", operand["$gt"]).isOK());
ASSERT(gt.matchesBSON(BSON("a" << 5.5), NULL));
ASSERT(!gt.matchesBSON(BSON("a" << 4), NULL));
@@ -615,7 +692,8 @@ TEST(GtOp, MatchesScalar) {
TEST(GtOp, MatchesArrayValue) {
BSONObj operand = BSON("$gt" << 5);
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a", operand["$gt"]).isOK());
ASSERT(gt.matchesBSON(BSON("a" << BSON_ARRAY(3 << 5.5)), NULL));
ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(2 << 4)), NULL));
@@ -623,7 +701,8 @@ TEST(GtOp, MatchesArrayValue) {
TEST(GtOp, MatchesWholeArray) {
BSONObj operand = BSON("$gt" << BSON_ARRAY(5));
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a", operand["$gt"]).isOK());
ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL));
ASSERT(!gt.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL));
@@ -638,7 +717,8 @@ TEST(GtOp, MatchesWholeArray) {
TEST(GtOp, MatchesNull) {
BSONObj operand = BSON("$gt" << BSONNULL);
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a", operand["$gt"]).isOK());
ASSERT(!gt.matchesBSON(BSONObj(), NULL));
ASSERT(!gt.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -649,7 +729,8 @@ TEST(GtOp, MatchesNull) {
TEST(GtOp, MatchesDotNotationNull) {
BSONObj operand = BSON("$gt" << BSONNULL);
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a.b", operand["$gt"]).isOK());
ASSERT(!gt.matchesBSON(BSONObj(), NULL));
ASSERT(!gt.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -663,7 +744,8 @@ TEST(GtOp, MatchesDotNotationNull) {
TEST(GtOp, MatchesMinKey) {
BSONObj operand = BSON("a" << MinKey);
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a", operand["a"]).isOK());
ASSERT(!gt.matchesBSON(BSON("a" << MinKey), NULL));
ASSERT(gt.matchesBSON(BSON("a" << MaxKey), NULL));
@@ -672,7 +754,8 @@ TEST(GtOp, MatchesMinKey) {
TEST(GtOp, MatchesMaxKey) {
BSONObj operand = BSON("a" << MaxKey);
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a", operand["a"]).isOK());
ASSERT(!gt.matchesBSON(BSON("a" << MaxKey), NULL));
ASSERT(!gt.matchesBSON(BSON("a" << MinKey), NULL));
@@ -681,7 +764,8 @@ TEST(GtOp, MatchesMaxKey) {
TEST(GtOp, ElemMatchKey) {
BSONObj operand = BSON("$gt" << 5);
- GTMatchExpression gt;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression gt(collator);
ASSERT(gt.init("a", operand["$gt"]).isOK());
MatchDetails details;
details.requestElemMatchKey();
@@ -745,14 +829,15 @@ TEST(GtOp, ElemMatchKey) {
}
*/
-TEST(ComparisonMatchExpression, MatchesElement) {
+TEST(GteOp, MatchesElement) {
BSONObj operand = BSON("$gte" << 5);
BSONObj match = BSON("a" << 5.5);
BSONObj equalMatch = BSON("a" << 5);
BSONObj notMatch = BSON("a" << 4);
BSONObj notMatchWrongType = BSON("a"
<< "foo");
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("", operand["$gte"]).isOK());
ASSERT(gte.matchesSingleElement(match.firstElement()));
ASSERT(gte.matchesSingleElement(equalMatch.firstElement()));
@@ -760,31 +845,35 @@ TEST(ComparisonMatchExpression, MatchesElement) {
ASSERT(!gte.matchesSingleElement(notMatchWrongType.firstElement()));
}
-TEST(ComparisonMatchExpression, InvalidEooOperand) {
+TEST(GteOp, InvalidEooOperand) {
BSONObj operand;
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(!gte.init("", operand.firstElement()).isOK());
}
-TEST(ComparisonMatchExpression, MatchesScalar) {
+TEST(GteOp, MatchesScalar) {
BSONObj operand = BSON("$gte" << 5);
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a", operand["$gte"]).isOK());
ASSERT(gte.matchesBSON(BSON("a" << 5.5), NULL));
ASSERT(!gte.matchesBSON(BSON("a" << 4), NULL));
}
-TEST(ComparisonMatchExpression, MatchesArrayValue) {
+TEST(GteOp, MatchesArrayValue) {
BSONObj operand = BSON("$gte" << 5);
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a", operand["$gte"]).isOK());
ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(4 << 5.5)), NULL));
ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL));
}
-TEST(ComparisonMatchExpression, MatchesWholeArray) {
+TEST(GteOp, MatchesWholeArray) {
BSONObj operand = BSON("$gte" << BSON_ARRAY(5));
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a", operand["$gte"]).isOK());
ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(4)), NULL));
ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(5)), NULL));
@@ -796,9 +885,10 @@ TEST(ComparisonMatchExpression, MatchesWholeArray) {
ASSERT(gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON_ARRAY(6))), NULL));
}
-TEST(ComparisonMatchExpression, MatchesNull) {
+TEST(GteOp, MatchesNull) {
BSONObj operand = BSON("$gte" << BSONNULL);
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a", operand["$gte"]).isOK());
ASSERT(gte.matchesBSON(BSONObj(), NULL));
ASSERT(gte.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -807,9 +897,10 @@ TEST(ComparisonMatchExpression, MatchesNull) {
ASSERT(gte.matchesBSON(BSON("b" << 4), NULL));
}
-TEST(ComparisonMatchExpression, MatchesDotNotationNull) {
+TEST(GteOp, MatchesDotNotationNull) {
BSONObj operand = BSON("$gte" << BSONNULL);
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a.b", operand["$gte"]).isOK());
ASSERT(gte.matchesBSON(BSONObj(), NULL));
ASSERT(gte.matchesBSON(BSON("a" << BSONNULL), NULL));
@@ -821,27 +912,30 @@ TEST(ComparisonMatchExpression, MatchesDotNotationNull) {
ASSERT(!gte.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 4))), NULL));
}
-TEST(ComparisonMatchExpression, MatchesMinKey) {
+TEST(GteOp, MatchesMinKey) {
BSONObj operand = BSON("a" << MinKey);
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a", operand["a"]).isOK());
ASSERT(gte.matchesBSON(BSON("a" << MinKey), NULL));
ASSERT(gte.matchesBSON(BSON("a" << MaxKey), NULL));
ASSERT(gte.matchesBSON(BSON("a" << 4), NULL));
}
-TEST(ComparisonMatchExpression, MatchesMaxKey) {
+TEST(GteOp, MatchesMaxKey) {
BSONObj operand = BSON("a" << MaxKey);
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a", operand["a"]).isOK());
ASSERT(gte.matchesBSON(BSON("a" << MaxKey), NULL));
ASSERT(!gte.matchesBSON(BSON("a" << MinKey), NULL));
ASSERT(!gte.matchesBSON(BSON("a" << 4), NULL));
}
-TEST(ComparisonMatchExpression, ElemMatchKey) {
+TEST(GteOp, ElemMatchKey) {
BSONObj operand = BSON("$gte" << 5);
- GTEMatchExpression gte;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression gte(collator);
ASSERT(gte.init("a", operand["$gte"]).isOK());
MatchDetails details;
details.requestElemMatchKey();
@@ -1501,14 +1595,16 @@ TEST(InMatchExpression, MatchesElementSingle) {
BSONArray operand = BSON_ARRAY(1);
BSONObj match = BSON("a" << 1);
BSONObj notMatch = BSON("a" << 2);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.getArrayFilterEntries()->addEquality(operand.firstElement());
ASSERT(in.matchesSingleElement(match["a"]));
ASSERT(!in.matchesSingleElement(notMatch["a"]));
}
TEST(InMatchExpression, MatchesEmpty) {
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
BSONObj notMatch = BSON("a" << 2);
@@ -1519,7 +1615,8 @@ TEST(InMatchExpression, MatchesEmpty) {
TEST(InMatchExpression, MatchesElementMultiple) {
BSONObj operand = BSON_ARRAY(1 << "r" << true << 1);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.getArrayFilterEntries()->addEquality(operand[0]);
in.getArrayFilterEntries()->addEquality(operand[1]);
in.getArrayFilterEntries()->addEquality(operand[2]);
@@ -1539,7 +1636,8 @@ TEST(InMatchExpression, MatchesElementMultiple) {
TEST(InMatchExpression, MatchesScalar) {
BSONObj operand = BSON_ARRAY(5);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
in.getArrayFilterEntries()->addEquality(operand.firstElement());
@@ -1549,7 +1647,8 @@ TEST(InMatchExpression, MatchesScalar) {
TEST(InMatchExpression, MatchesArrayValue) {
BSONObj operand = BSON_ARRAY(5);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
in.getArrayFilterEntries()->addEquality(operand.firstElement());
@@ -1561,7 +1660,8 @@ TEST(InMatchExpression, MatchesArrayValue) {
TEST(InMatchExpression, MatchesNull) {
BSONObj operand = BSON_ARRAY(BSONNULL);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
in.getArrayFilterEntries()->addEquality(operand.firstElement());
@@ -1575,7 +1675,8 @@ TEST(InMatchExpression, MatchesNull) {
TEST(InMatchExpression, MatchesUndefined) {
BSONObj operand = BSON_ARRAY(BSONUndefined);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
Status s = in.getArrayFilterEntries()->addEquality(operand.firstElement());
ASSERT_NOT_OK(s);
@@ -1583,7 +1684,8 @@ TEST(InMatchExpression, MatchesUndefined) {
TEST(InMatchExpression, MatchesMinKey) {
BSONObj operand = BSON_ARRAY(MinKey);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
in.getArrayFilterEntries()->addEquality(operand.firstElement());
@@ -1594,7 +1696,8 @@ TEST(InMatchExpression, MatchesMinKey) {
TEST(InMatchExpression, MatchesMaxKey) {
BSONObj operand = BSON_ARRAY(MaxKey);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
in.getArrayFilterEntries()->addEquality(operand.firstElement());
@@ -1605,7 +1708,8 @@ TEST(InMatchExpression, MatchesMaxKey) {
TEST(InMatchExpression, MatchesFullArray) {
BSONObj operand = BSON_ARRAY(BSON_ARRAY(1 << 2) << 4 << 5);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
in.getArrayFilterEntries()->addEquality(operand[0]);
in.getArrayFilterEntries()->addEquality(operand[1]);
@@ -1619,7 +1723,8 @@ TEST(InMatchExpression, MatchesFullArray) {
TEST(InMatchExpression, ElemMatchKey) {
BSONObj operand = BSON_ARRAY(5 << 2);
- InMatchExpression in;
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
in.init("a");
in.getArrayFilterEntries()->addEquality(operand[0]);
in.getArrayFilterEntries()->addEquality(operand[1]);
@@ -1635,6 +1740,42 @@ TEST(InMatchExpression, ElemMatchKey) {
ASSERT_EQUALS("1", details.elemMatchKey());
}
+TEST(InMatchExpression, InMatchExpressionsWithUnequalCollatorsAreUnequal) {
+ CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kReverseString);
+ InMatchExpression eq1(&collator1);
+ CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ InMatchExpression eq2(&collator2);
+ ASSERT(!eq1.equivalent(&eq2));
+}
+
+TEST(InMatchExpression, InMatchExpressionsWithEqualCollatorsAreEqual) {
+ CollatorInterfaceMock collator1(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ InMatchExpression eq1(&collator1);
+ CollatorInterfaceMock collator2(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ InMatchExpression eq2(&collator2);
+ ASSERT(eq1.equivalent(&eq2));
+}
+
+TEST(InMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) {
+ BSONArray operand = BSON_ARRAY("string");
+ BSONObj notMatch = BSON("a"
+ << "string2");
+ CollatorInterface* collator = nullptr;
+ InMatchExpression in(collator);
+ in.getArrayFilterEntries()->addEquality(operand.firstElement());
+ ASSERT(!in.matchesSingleElement(notMatch["a"]));
+}
+
+TEST(InMatchExpression, StringMatchingRespectsCollation) {
+ BSONArray operand = BSON_ARRAY("string");
+ BSONObj match = BSON("a"
+ << "string2");
+ CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ InMatchExpression in(&collator);
+ in.getArrayFilterEntries()->addEquality(operand.firstElement());
+ ASSERT(in.matchesSingleElement(match["a"]));
+}
+
/**
TEST( InMatchExpression, MatchesIndexKeyScalar ) {
BSONObj operand = BSON( "$in" << BSON_ARRAY( 6 << 5 ) );
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index fd6b8802524..f86ba0f55e4 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -95,7 +95,8 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
// TODO: these should move to getGtLtOp, or its replacement
if (mongoutils::str::equals("$eq", e.fieldName()))
- return _parseComparison(name, new EqualityMatchExpression(), e);
+ // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression().
+ return _parseComparison(name, new EqualityMatchExpression(nullptr), e);
if (mongoutils::str::equals("$not", e.fieldName())) {
return _parseNot(name, e, level);
@@ -112,20 +113,26 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {Status(ErrorCodes::BadValue,
mongoutils::str::stream() << "unknown operator: " << e.fieldName())};
case BSONObj::LT:
- return _parseComparison(name, new LTMatchExpression(), e);
+ // TODO SERVER-23608: Pass our CollatorInterface* to LTMatchExpression().
+ return _parseComparison(name, new LTMatchExpression(nullptr), e);
case BSONObj::LTE:
- return _parseComparison(name, new LTEMatchExpression(), e);
+ // TODO SERVER-23608: Pass our CollatorInterface* to LTEMatchExpression().
+ return _parseComparison(name, new LTEMatchExpression(nullptr), e);
case BSONObj::GT:
- return _parseComparison(name, new GTMatchExpression(), e);
+ // TODO SERVER-23608: Pass our CollatorInterface* to GTMatchExpression().
+ return _parseComparison(name, new GTMatchExpression(nullptr), e);
case BSONObj::GTE:
- return _parseComparison(name, new GTEMatchExpression(), e);
+ // TODO SERVER-23608: Pass our CollatorInterface* to GTEMatchExpression().
+ return _parseComparison(name, new GTEMatchExpression(nullptr), e);
case BSONObj::NE: {
if (RegEx == e.type()) {
// Just because $ne can be rewritten as the negation of an
// equality does not mean that $ne of a regex is allowed. See SERVER-1705.
return {Status(ErrorCodes::BadValue, "Can't have regex as arg to $ne.")};
}
- StatusWithMatchExpression s = _parseComparison(name, new EqualityMatchExpression(), e);
+ // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression().
+ StatusWithMatchExpression s =
+ _parseComparison(name, new EqualityMatchExpression(nullptr), e);
if (!s.isOK())
return s;
std::unique_ptr<NotMatchExpression> n = stdx::make_unique<NotMatchExpression>();
@@ -135,12 +142,14 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {std::move(n)};
}
case BSONObj::Equality:
- return _parseComparison(name, new EqualityMatchExpression(), e);
+ // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression().
+ return _parseComparison(name, new EqualityMatchExpression(nullptr), e);
case BSONObj::opIN: {
if (e.type() != Array)
return {Status(ErrorCodes::BadValue, "$in needs an array")};
- std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>();
+ // TODO SERVER-23608: Pass our CollatorInterface* to InMatchExpression().
+ std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>(nullptr);
Status s = temp->init(name);
if (!s.isOK())
return s;
@@ -153,7 +162,8 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
case BSONObj::NIN: {
if (e.type() != Array)
return {Status(ErrorCodes::BadValue, "$nin needs an array")};
- std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>();
+ // TODO SERVER-23608: Pass our CollatorInterface* to InMatchExpression().
+ std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>(nullptr);
Status s = temp->init(name);
if (!s.isOK())
return s;
@@ -344,8 +354,10 @@ StatusWithMatchExpression MatchExpressionParser::_parse(const BSONObj& obj, int
} else if (mongoutils::str::equals("ref", rest) ||
mongoutils::str::equals("id", rest) || mongoutils::str::equals("db", rest)) {
// DBRef fields.
+ // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression() in
+ // the "id" case.
std::unique_ptr<ComparisonMatchExpression> eq =
- stdx::make_unique<EqualityMatchExpression>();
+ stdx::make_unique<EqualityMatchExpression>(nullptr);
Status s = eq->init(e.fieldName(), e);
if (!s.isOK())
return s;
@@ -375,8 +387,9 @@ StatusWithMatchExpression MatchExpressionParser::_parse(const BSONObj& obj, int
continue;
}
+ // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression().
std::unique_ptr<ComparisonMatchExpression> eq =
- stdx::make_unique<EqualityMatchExpression>();
+ stdx::make_unique<EqualityMatchExpression>(nullptr);
Status s = eq->init(e.fieldName(), e);
if (!s.isOK())
return s;
@@ -794,8 +807,9 @@ StatusWithMatchExpression MatchExpressionParser::_parseAll(const char* name,
} else if (e.type() == Object && e.Obj().firstElement().getGtLtOp(-1) != -1) {
return {Status(ErrorCodes::BadValue, "no $ expressions in $all")};
} else {
+ // TODO SERVER-23608: Pass our CollatorInterface* to EqualityMatchExpression().
std::unique_ptr<EqualityMatchExpression> x =
- stdx::make_unique<EqualityMatchExpression>();
+ stdx::make_unique<EqualityMatchExpression>(nullptr);
Status s = x->init(name, e);
if (!s.isOK())
return s;
diff --git a/src/mongo/db/matcher/expression_test.cpp b/src/mongo/db/matcher/expression_test.cpp
index 90d50cf5f47..57e8f1c8f7f 100644
--- a/src/mongo/db/matcher/expression_test.cpp
+++ b/src/mongo/db/matcher/expression_test.cpp
@@ -47,7 +47,8 @@ TEST(MatchExpressionTest, Parse1) {
TEST(LeafMatchExpressionTest, Equal1) {
BSONObj temp = BSON("x" << 5);
- EqualityMatchExpression e;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression e(collator);
e.init("x", temp["x"]);
ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 5 }")));
@@ -66,7 +67,8 @@ TEST(LeafMatchExpressionTest, Comp1) {
BSONObj temp = BSON("x" << 5);
{
- LTEMatchExpression e;
+ CollatorInterface* collator = nullptr;
+ LTEMatchExpression e(collator);
e.init("x", temp["x"]);
ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 5 }")));
ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 4 }")));
@@ -75,7 +77,8 @@ TEST(LeafMatchExpressionTest, Comp1) {
}
{
- LTMatchExpression e;
+ CollatorInterface* collator = nullptr;
+ LTMatchExpression e(collator);
e.init("x", temp["x"]);
ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 5 }")));
ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 4 }")));
@@ -84,7 +87,8 @@ TEST(LeafMatchExpressionTest, Comp1) {
}
{
- GTEMatchExpression e;
+ CollatorInterface* collator = nullptr;
+ GTEMatchExpression e(collator);
e.init("x", temp["x"]);
ASSERT_TRUE(e.matchesBSON(fromjson("{ x : 5 }")));
ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 4 }")));
@@ -93,7 +97,8 @@ TEST(LeafMatchExpressionTest, Comp1) {
}
{
- GTMatchExpression e;
+ CollatorInterface* collator = nullptr;
+ GTMatchExpression e(collator);
e.init("x", temp["x"]);
ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 5 }")));
ASSERT_FALSE(e.matchesBSON(fromjson("{ x : 4 }")));
diff --git a/src/mongo/db/matcher/expression_tree_test.cpp b/src/mongo/db/matcher/expression_tree_test.cpp
index 17ec2f9e2ac..e8aa987ab32 100644
--- a/src/mongo/db/matcher/expression_tree_test.cpp
+++ b/src/mongo/db/matcher/expression_tree_test.cpp
@@ -42,7 +42,8 @@ using std::unique_ptr;
TEST(NotMatchExpression, MatchesScalar) {
BSONObj baseOperand = BSON("$lt" << 5);
- unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator));
ASSERT(lt->init("a", baseOperand["$lt"]).isOK());
NotMatchExpression notOp;
ASSERT(notOp.init(lt.release()).isOK());
@@ -52,7 +53,8 @@ TEST(NotMatchExpression, MatchesScalar) {
TEST(NotMatchExpression, MatchesArray) {
BSONObj baseOperand = BSON("$lt" << 5);
- unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator));
ASSERT(lt->init("a", baseOperand["$lt"]).isOK());
NotMatchExpression notOp;
ASSERT(notOp.init(lt.release()).isOK());
@@ -64,7 +66,8 @@ TEST(NotMatchExpression, MatchesArray) {
TEST(NotMatchExpression, ElemMatchKey) {
BSONObj baseOperand = BSON("$lt" << 5);
- unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> lt(new LTMatchExpression(collator));
ASSERT(lt->init("a", baseOperand["$lt"]).isOK());
NotMatchExpression notOp;
ASSERT(notOp.init(lt.release()).isOK());
@@ -127,9 +130,10 @@ TEST(AndOp, MatchesElementThreeClauses) {
BSONObj notMatch3 = BSON("a"
<< "r");
- unique_ptr<ComparisonMatchExpression> sub1(new LTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> sub1(new LTMatchExpression(collator));
ASSERT(sub1->init("a", baseOperand1["$lt"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub2(new GTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub2(new GTMatchExpression(collator));
ASSERT(sub2->init("a", baseOperand2["$gt"]).isOK());
unique_ptr<RegexMatchExpression> sub3(new RegexMatchExpression());
ASSERT(sub3->init("a", "1", "").isOK());
@@ -147,7 +151,8 @@ TEST(AndOp, MatchesElementThreeClauses) {
TEST(AndOp, MatchesSingleClause) {
BSONObj baseOperand = BSON("$ne" << 5);
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("a", baseOperand["$ne"]).isOK());
unique_ptr<NotMatchExpression> ne(new NotMatchExpression());
ASSERT(ne->init(eq.release()).isOK());
@@ -166,13 +171,14 @@ TEST(AndOp, MatchesThreeClauses) {
BSONObj baseOperand2 = BSON("$lt" << 10);
BSONObj baseOperand3 = BSON("$lt" << 100);
- unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression(collator));
ASSERT(sub1->init("a", baseOperand1["$gt"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression(collator));
ASSERT(sub2->init("a", baseOperand2["$lt"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub3(new LTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub3(new LTMatchExpression(collator));
ASSERT(sub3->init("b", baseOperand3["$lt"]).isOK());
AndMatchExpression andOp;
@@ -191,10 +197,11 @@ TEST(AndOp, ElemMatchKey) {
BSONObj baseOperand1 = BSON("a" << 1);
BSONObj baseOperand2 = BSON("b" << 2);
- unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression(collator));
ASSERT(sub1->init("a", baseOperand1["a"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression(collator));
ASSERT(sub2->init("b", baseOperand2["b"]).isOK());
AndMatchExpression andOp;
@@ -311,7 +318,8 @@ TEST( OrOp, MatchesElementThreeClauses ) {
*/
TEST(OrOp, MatchesSingleClause) {
BSONObj baseOperand = BSON("$ne" << 5);
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("a", baseOperand["$ne"]).isOK());
unique_ptr<NotMatchExpression> ne(new NotMatchExpression());
ASSERT(ne->init(eq.release()).isOK());
@@ -329,11 +337,12 @@ TEST(OrOp, MatchesThreeClauses) {
BSONObj baseOperand1 = BSON("$gt" << 10);
BSONObj baseOperand2 = BSON("$lt" << 0);
BSONObj baseOperand3 = BSON("b" << 100);
- unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression(collator));
ASSERT(sub1->init("a", baseOperand1["$gt"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression(collator));
ASSERT(sub2->init("a", baseOperand2["$lt"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression(collator));
ASSERT(sub3->init("b", baseOperand3["b"]).isOK());
OrMatchExpression orOp;
@@ -353,9 +362,10 @@ TEST(OrOp, MatchesThreeClauses) {
TEST(OrOp, ElemMatchKey) {
BSONObj baseOperand1 = BSON("a" << 1);
BSONObj baseOperand2 = BSON("b" << 2);
- unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression(collator));
ASSERT(sub1->init("a", baseOperand1["a"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression(collator));
ASSERT(sub2->init("b", baseOperand2["b"]).isOK());
OrMatchExpression orOp;
@@ -472,7 +482,8 @@ TEST( NorOp, MatchesElementThreeClauses ) {
TEST(NorOp, MatchesSingleClause) {
BSONObj baseOperand = BSON("$ne" << 5);
- unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> eq(new EqualityMatchExpression(collator));
ASSERT(eq->init("a", baseOperand["$ne"]).isOK());
unique_ptr<NotMatchExpression> ne(new NotMatchExpression());
ASSERT(ne->init(eq.release()).isOK());
@@ -491,11 +502,12 @@ TEST(NorOp, MatchesThreeClauses) {
BSONObj baseOperand2 = BSON("$lt" << 0);
BSONObj baseOperand3 = BSON("b" << 100);
- unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> sub1(new GTMatchExpression(collator));
ASSERT(sub1->init("a", baseOperand1["$gt"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub2(new LTMatchExpression(collator));
ASSERT(sub2->init("a", baseOperand2["$lt"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub3(new EqualityMatchExpression(collator));
ASSERT(sub3->init("b", baseOperand3["b"]).isOK());
NorMatchExpression norOp;
@@ -515,9 +527,10 @@ TEST(NorOp, MatchesThreeClauses) {
TEST(NorOp, ElemMatchKey) {
BSONObj baseOperand1 = BSON("a" << 1);
BSONObj baseOperand2 = BSON("b" << 2);
- unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression());
+ CollatorInterface* collator = nullptr;
+ unique_ptr<ComparisonMatchExpression> sub1(new EqualityMatchExpression(collator));
ASSERT(sub1->init("a", baseOperand1["a"]).isOK());
- unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression());
+ unique_ptr<ComparisonMatchExpression> sub2(new EqualityMatchExpression(collator));
ASSERT(sub2->init("b", baseOperand2["b"]).isOK());
NorMatchExpression norOp;
@@ -539,9 +552,10 @@ TEST(NorOp, ElemMatchKey) {
TEST(NorOp, Equivalent) {
BSONObj baseOperand1 = BSON("a" << 1);
BSONObj baseOperand2 = BSON("b" << 2);
- EqualityMatchExpression sub1;
+ CollatorInterface* collator = nullptr;
+ EqualityMatchExpression sub1(collator);
ASSERT(sub1.init("a", baseOperand1["a"]).isOK());
- EqualityMatchExpression sub2;
+ EqualityMatchExpression sub2(collator);
ASSERT(sub2.init("b", baseOperand2["b"]).isOK());
NorMatchExpression e1;
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index 7c6834a63e4..9c536d41f6b 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -208,6 +208,7 @@ env.CppUnitTest(
"canonical_query_test.cpp"
],
LIBDEPS=[
+ "collation/collator_interface_mock",
"query_planner",
],
)
diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp
index 95aa24b31ec..5e3f5a2b1a1 100644
--- a/src/mongo/db/query/canonical_query.cpp
+++ b/src/mongo/db/query/canonical_query.cpp
@@ -457,7 +457,7 @@ MatchExpression* CanonicalQuery::normalizeTree(MatchExpression* root) {
return normalizeTree(re.release());
}
- auto eq = stdx::make_unique<EqualityMatchExpression>();
+ auto eq = stdx::make_unique<EqualityMatchExpression>(in->getCollator());
eq->init(in->path(), *(inArrayEntries->equalities().begin()));
if (in->getTag()) {
eq->setTag(in->getTag()->clone());
diff --git a/src/mongo/db/query/canonical_query_test.cpp b/src/mongo/db/query/canonical_query_test.cpp
index f597fa6e0ce..50574135059 100644
--- a/src/mongo/db/query/canonical_query_test.cpp
+++ b/src/mongo/db/query/canonical_query_test.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
#include "mongo/db/matcher/extensions_callback_noop.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/db/query/index_tag.h"
#include "mongo/unittest/unittest.h"
@@ -636,6 +637,19 @@ TEST(CanonicalQueryTest, NormalizeWithInAndRegexPreservesTags) {
ASSERT_EQ(2U, tag->index);
}
+TEST(CanonicalQueryTest, NormalizeWithInPreservesCollator) {
+ CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
+ auto inMatchExpression = stdx::make_unique<InMatchExpression>(&collator);
+ BSONObj obj = fromjson("{'': 'string'}");
+ inMatchExpression->getArrayFilterEntries()->addEquality(obj.firstElement());
+ unique_ptr<MatchExpression> matchExpression(
+ CanonicalQuery::normalizeTree(inMatchExpression.release()));
+ ASSERT(matchExpression->matchType() == MatchExpression::MatchType::EQ);
+ EqualityMatchExpression* eqMatchExpression =
+ static_cast<EqualityMatchExpression*>(matchExpression.get());
+ ASSERT_EQ(eqMatchExpression->getCollator(), &collator);
+}
+
TEST(CanonicalQueryTest, CanonicalizeFromBaseQuery) {
const bool isExplain = true;
const std::string cmdStr =
diff --git a/src/mongo/db/query/collation/collator_interface.h b/src/mongo/db/query/collation/collator_interface.h
index 1dad55a1b67..962ccadb718 100644
--- a/src/mongo/db/query/collation/collator_interface.h
+++ b/src/mongo/db/query/collation/collator_interface.h
@@ -125,6 +125,19 @@ public:
return _spec;
}
+ /**
+ * Returns true if lhs and rhs are both nullptr, or if they point to equivalent collators.
+ */
+ static bool collatorsMatch(const CollatorInterface* lhs, const CollatorInterface* rhs) {
+ if (lhs == nullptr && rhs == nullptr) {
+ return true;
+ }
+ if (lhs == nullptr || rhs == nullptr) {
+ return false;
+ }
+ return (*lhs == *rhs);
+ }
+
protected:
static ComparisonKey makeComparisonKey(std::string key) {
return ComparisonKey(std::move(key));
diff --git a/src/mongo/db/query/collation/collator_interface_mock_test.cpp b/src/mongo/db/query/collation/collator_interface_mock_test.cpp
index 39e4a76d46c..843685806c6 100644
--- a/src/mongo/db/query/collation/collator_interface_mock_test.cpp
+++ b/src/mongo/db/query/collation/collator_interface_mock_test.cpp
@@ -54,6 +54,28 @@ TEST(CollatorInterfaceMockSelfTest, MocksOfDifferentTypesAreNotEqual) {
ASSERT(reverseMock != alwaysEqualMock);
}
+TEST(CollatorInterfaceMockSelfTest, NullMockPointersMatch) {
+ ASSERT(CollatorInterface::collatorsMatch(nullptr, nullptr));
+}
+
+TEST(CollatorInterfaceMockSelfTest, NullMockPointerDoesNotMatchNonNullMockPointer) {
+ CollatorInterfaceMock reverseMock(CollatorInterfaceMock::MockType::kReverseString);
+ ASSERT(!CollatorInterface::collatorsMatch(nullptr, &reverseMock));
+ ASSERT(!CollatorInterface::collatorsMatch(&reverseMock, nullptr));
+}
+
+TEST(CollatorInterfaceMockSelfTest, PointersToMocksOfSameTypeMatch) {
+ CollatorInterfaceMock reverseMock1(CollatorInterfaceMock::MockType::kReverseString);
+ CollatorInterfaceMock reverseMock2(CollatorInterfaceMock::MockType::kReverseString);
+ ASSERT(CollatorInterface::collatorsMatch(&reverseMock1, &reverseMock2));
+}
+
+TEST(CollatorInterfaceMockSelfTest, PointersToMocksOfDifferentTypesDoNotMatch) {
+ CollatorInterfaceMock reverseMock(CollatorInterfaceMock::MockType::kReverseString);
+ CollatorInterfaceMock alwaysEqualMock(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ ASSERT(!CollatorInterface::collatorsMatch(&reverseMock, &alwaysEqualMock));
+}
+
TEST(CollatorInterfaceMockSelfTest, ReverseMockComparesInReverse) {
CollatorInterfaceMock reverseMock(CollatorInterfaceMock::MockType::kReverseString);
ASSERT_EQ(reverseMock.compare("abc", "abc"), 0);
diff --git a/src/mongo/db/query/planner_ixselect.cpp b/src/mongo/db/query/planner_ixselect.cpp
index 506b1b24836..3e0da4422bf 100644
--- a/src/mongo/db/query/planner_ixselect.cpp
+++ b/src/mongo/db/query/planner_ixselect.cpp
@@ -73,16 +73,6 @@ static bool twoDWontWrap(const Circle& circle, const IndexEntry& index) {
return ret;
}
-static bool collatorsMatch(const CollatorInterface* lhs, const CollatorInterface* rhs) {
- if (lhs == nullptr && rhs == nullptr) {
- return true;
- }
- if (lhs == nullptr || rhs == nullptr) {
- return false;
- }
- return (*lhs == *rhs);
-}
-
// Checks whether 'node' contains any comparison to an element of type 'type'. Nested objects and
// arrays are not checked recursively. We assume 'node' is bounds-generating or is a recursive child
// of a bounds-generating node, i.e. it does not contain AND, OR, ELEM_MATCH_OBJECT, or NOR.
@@ -188,7 +178,7 @@ bool QueryPlannerIXSelect::compatible(const BSONElement& elt,
// String comparisons require the collators to match.
if (boundsGeneratingNodeContainsComparisonToType(node, BSONType::String) &&
- !collatorsMatch(collator, index.collator)) {
+ !CollatorInterface::collatorsMatch(collator, index.collator)) {
return false;
}