// matchertests.cpp : matcher unit tests
//
/**
* Copyright (C) 2008 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#include "mongo/db/json.h"
#include "mongo/db/matcher/matcher.h"
#include "mongo/db/operation_context_impl.h"
#include "mongo/dbtests/dbtests.h"
#include "mongo/util/timer.h"
namespace MatcherTests {
class CollectionBase {
public:
CollectionBase() { }
virtual ~CollectionBase() { }
};
template
class Basic {
public:
void run() {
BSONObj query = fromjson( "{\"a\":\"b\"}" );
M m(query, MatchExpressionParser::WhereCallback());
ASSERT( m.matches( fromjson( "{\"a\":\"b\"}" ) ) );
}
};
template
class DoubleEqual {
public:
void run() {
BSONObj query = fromjson( "{\"a\":5}" );
M m(query, MatchExpressionParser::WhereCallback());
ASSERT( m.matches( fromjson( "{\"a\":5}" ) ) );
}
};
template
class MixedNumericEqual {
public:
void run() {
BSONObjBuilder query;
query.append( "a", 5 );
M m(query.done(), MatchExpressionParser::WhereCallback());
ASSERT( m.matches( fromjson( "{\"a\":5}" ) ) );
}
};
template
class MixedNumericGt {
public:
void run() {
BSONObj query = fromjson( "{\"a\":{\"$gt\":4}}" );
M m(query, MatchExpressionParser::WhereCallback());
BSONObjBuilder b;
b.append( "a", 5 );
ASSERT( m.matches( b.done() ) );
}
};
template
class MixedNumericIN {
public:
void run() {
BSONObj query = fromjson( "{ a : { $in : [4,6] } }" );
ASSERT_EQUALS( 4 , query["a"].embeddedObject()["$in"].embeddedObject()["0"].number() );
ASSERT_EQUALS( NumberInt , query["a"].embeddedObject()["$in"].embeddedObject()["0"].type() );
M m(query, MatchExpressionParser::WhereCallback());
{
BSONObjBuilder b;
b.append( "a" , 4.0 );
ASSERT( m.matches( b.done() ) );
}
{
BSONObjBuilder b;
b.append( "a" , 5 );
ASSERT( ! m.matches( b.done() ) );
}
{
BSONObjBuilder b;
b.append( "a" , 4 );
ASSERT( m.matches( b.done() ) );
}
}
};
template
class MixedNumericEmbedded {
public:
void run() {
M m(BSON("a" << BSON("x" << 1)), MatchExpressionParser::WhereCallback());
ASSERT( m.matches( BSON( "a" << BSON( "x" << 1 ) ) ) );
ASSERT( m.matches( BSON( "a" << BSON( "x" << 1.0 ) ) ) );
}
};
template
class Size {
public:
void run() {
M m(fromjson("{a:{$size:4}}"), MatchExpressionParser::WhereCallback());
ASSERT( m.matches( fromjson( "{a:[1,2,3,4]}" ) ) );
ASSERT( !m.matches( fromjson( "{a:[1,2,3]}" ) ) );
ASSERT( !m.matches( fromjson( "{a:[1,2,3,'a','b']}" ) ) );
ASSERT( !m.matches( fromjson( "{a:[[1,2,3,4]]}" ) ) );
}
};
template
class WithinBox {
public:
void run() {
M m(fromjson("{loc:{$within:{$box:[{x: 4, y:4},[6,6]]}}}"),
MatchExpressionParser::WhereCallback());
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
ASSERT(m.matches(fromjson("{loc: [5,5]}")));
ASSERT(m.matches(fromjson("{loc: [5,5.1]}")));
ASSERT(m.matches(fromjson("{loc: {x: 5, y:5.1}}")));
}
};
template
class WithinPolygon {
public:
void run() {
M m(fromjson("{loc:{$within:{$polygon:[{x:0,y:0},[0,5],[5,5],[5,0]]}}}"),
MatchExpressionParser::WhereCallback());
ASSERT(m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
ASSERT(m.matches(fromjson("{loc: {x:5,y:5}}")));
ASSERT(!m.matches(fromjson("{loc: [5,5.1]}")));
ASSERT(!m.matches(fromjson("{loc: {}}")));
}
};
template
class WithinCenter {
public:
void run() {
M m(fromjson("{loc:{$within:{$center:[{x:30,y:30},10]}}}"),
MatchExpressionParser::WhereCallback());
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: {x:30,y:30}}")));
ASSERT(m.matches(fromjson("{loc: [20,30]}")));
ASSERT(m.matches(fromjson("{loc: [30,20]}")));
ASSERT(m.matches(fromjson("{loc: [40,30]}")));
ASSERT(m.matches(fromjson("{loc: [30,40]}")));
ASSERT(!m.matches(fromjson("{loc: [31,40]}")));
}
};
/** Test that MatchDetails::elemMatchKey() is set correctly after a match. */
template
class ElemMatchKey {
public:
void run() {
M matcher(BSON("a.b" << 1),
MatchExpressionParser::WhereCallback());
MatchDetails details;
details.requestElemMatchKey();
ASSERT( !details.hasElemMatchKey() );
ASSERT( matcher.matches( fromjson( "{ a:[ { b:1 } ] }" ), &details ) );
// The '0' entry of the 'a' array is matched.
ASSERT( details.hasElemMatchKey() );
ASSERT_EQUALS( string( "0" ), details.elemMatchKey() );
}
};
template
class WhereSimple1 {
public:
void run() {
OperationContextImpl txn;
AutoGetCollectionForRead ctx(&txn, "unittests.matchertests");
M m(BSON("$where" << "function(){ return this.a == 1; }"),
WhereCallbackReal(&txn, StringData("unittests")));
ASSERT( m.matches( BSON( "a" << 1 ) ) );
ASSERT( !m.matches( BSON( "a" << 2 ) ) );
}
};
template< typename M >
class TimingBase {
public:
long dotime( const BSONObj& patt , const BSONObj& obj ) {
M m(patt, MatchExpressionParser::WhereCallback());
Timer t;
for ( int i=0; i<900000; i++ ) {
if ( !m.matches( obj ) ) {
ASSERT( 0 );
}
}
return t.millis();
}
};
template< typename M >
class AllTiming : public TimingBase {
public:
void run() {
long normal = TimingBase::dotime( BSON( "x" << 5 ),
BSON( "x" << 5 ) );
long all = TimingBase::dotime( BSON( "x" << BSON( "$all" << BSON_ARRAY( 5 ) ) ),
BSON( "x" << 5 ) );
cout << "AllTiming " << demangleName(typeid(M))
<< " normal: " << normal << " all: " << all << endl;
}
};
class All : public Suite {
public:
All() : Suite( "matcher" ) {
}
#define ADD_BOTH(TEST) \
add< TEST >();
void setupTests() {
ADD_BOTH(Basic);
ADD_BOTH(DoubleEqual);
ADD_BOTH(MixedNumericEqual);
ADD_BOTH(MixedNumericGt);
ADD_BOTH(MixedNumericIN);
ADD_BOTH(Size);
ADD_BOTH(MixedNumericEmbedded);
ADD_BOTH(ElemMatchKey);
ADD_BOTH(WhereSimple1);
ADD_BOTH(AllTiming);
ADD_BOTH(WithinBox);
ADD_BOTH(WithinCenter);
ADD_BOTH(WithinPolygon);
}
} dball;
} // namespace MatcherTests