// testcount is a wrapper around runCount that // - sets up a countStage // - runs it // - asserts count is not trivial // - asserts nCounted is equal to expected_n // - asserts nSkipped is correct void testCount(const CountRequest& request, int expected_n = kDocuments, bool indexed = false) { setup(); getLocs(); unique_ptr<WorkingSet> ws(new WorkingSet); StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse(request.getQuery()); ASSERT(statusWithMatcher.isOK()); unique_ptr<MatchExpression> expression = std::move(statusWithMatcher.getValue()); PlanStage* scan; if (indexed) { scan = createIndexScan(expression.get(), ws.get()); } else { scan = createCollScan(expression.get(), ws.get()); } CountStage countStage(&_txn, _coll, request, ws.get(), scan); const CountStats* stats = runCount(countStage); ASSERT_FALSE(stats->trivialCount); ASSERT_EQUALS(stats->nCounted, expected_n); ASSERT_EQUALS(stats->nSkipped, request.getSkip()); }
Status ModifierPull::init(const BSONElement& modExpr, const Options& opts, bool* positional) { // Perform standard field name and updateable checks. _fieldRef.parse(modExpr.fieldName()); Status status = fieldchecker::isUpdatable(_fieldRef); if (!status.isOK()) { return status; } // If a $-positional operator was used, get the index in which it occurred // and ensure only one occurrence. size_t foundCount; bool foundDollar = fieldchecker::isPositional(_fieldRef, &_posDollar, &foundCount); if (positional) *positional = foundDollar; if (foundDollar && foundCount > 1) { return Status(ErrorCodes::BadValue, str::stream() << "Too many positional (i.e. '$') elements found in path '" << _fieldRef.dottedField() << "'"); } _exprElt = modExpr; _collator = opts.collator; // If the element in the mod is actually an object or a regular expression, we need to // build a matcher, instead of just doing an equality comparision. if ((_exprElt.type() == mongo::Object) || (_exprElt.type() == mongo::RegEx)) { if (_exprElt.type() == Object) { _exprObj = _exprElt.embeddedObject(); // If not is not a query operator, then it is a primitive. _matcherOnPrimitive = (MatchExpressionParser::parsePathAcceptingKeyword( _exprObj.firstElement(), PathAcceptingKeyword::EQUALITY) != PathAcceptingKeyword::EQUALITY); // If the object is primitive then wrap it up into an object. if (_matcherOnPrimitive) _exprObj = BSON("" << _exprObj); } else { // For a regex, we also need to wrap and treat like a primitive. _matcherOnPrimitive = true; _exprObj = _exprElt.wrap(""); } // Build the matcher around the object we built above. Currently, we do not allow $pull // operations to contain $text/$where clauses, so preserving this behaviour. StatusWithMatchExpression parseResult = MatchExpressionParser::parse( _exprObj, ExtensionsCallbackDisallowExtensions(), _collator); if (!parseResult.isOK()) { return parseResult.getStatus(); } _matchExpr = std::move(parseResult.getValue()); } return Status::OK(); }
void run() { ScopedTransaction transaction(&_txn, MODE_IX); Lock::DBLock lk(_txn.lockState(), nsToDatabaseSubstring(ns()), MODE_X); OldClientContext ctx(&_txn, ns()); Database* db = ctx.db(); Collection* coll = db->getCollection(ns()); if (!coll) { WriteUnitOfWork wuow(&_txn); coll = db->createCollection(&_txn, ns()); wuow.commit(); } WorkingSet ws; // Add an object to the DB. insert(BSON("foo" << 5)); set<RecordId> recordIds; getRecordIds(&recordIds, coll); ASSERT_EQUALS(size_t(1), recordIds.size()); // Create a mock stage that returns the WSM. auto mockStage = make_unique<QueuedDataStage>(&_txn, &ws); // Mock data. { WorkingSetID id = ws.allocate(); WorkingSetMember* mockMember = ws.get(id); mockMember->recordId = *recordIds.begin(); ws.transitionToRecordIdAndIdx(id); // State is RecordId and index, shouldn't be able to get the foo data inside. BSONElement elt; ASSERT_FALSE(mockMember->getFieldDotted("foo", &elt)); mockStage->pushBack(id); } // Make the filter. BSONObj filterObj = BSON("foo" << 6); const CollatorInterface* collator = nullptr; StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse( filterObj, ExtensionsCallbackDisallowExtensions(), collator); verify(statusWithMatcher.isOK()); unique_ptr<MatchExpression> filterExpr = std::move(statusWithMatcher.getValue()); // Matcher requires that foo==6 but we only have data with foo==5. unique_ptr<FetchStage> fetchStage( new FetchStage(&_txn, &ws, mockStage.release(), filterExpr.get(), coll)); // First call should return a fetch request as it's not in memory. WorkingSetID id = WorkingSet::INVALID_ID; PlanStage::StageState state; // Normally we'd return the object but we have a filter that prevents it. state = fetchStage->work(&id); ASSERT_EQUALS(PlanStage::NEED_TIME, state); // No more data to fetch, so, EOF. state = fetchStage->work(&id); ASSERT_EQUALS(PlanStage::IS_EOF, state); }
Status CanonicalQuery::init(LiteParsedQuery* lpq) { _pq.reset(lpq); // Build a parse tree from the BSONObj in the parsed query. StatusWithMatchExpression swme = MatchExpressionParser::parse(_pq->getFilter()); if (!swme.isOK()) { return swme.getStatus(); } MatchExpression* root = swme.getValue(); root = normalizeTree(root); Status validStatus = isValid(root); if (!validStatus.isOK()) { return validStatus; } _root.reset(root); if (!_pq->getProj().isEmpty()) { LiteProjection* liteProj = NULL; Status liteProjStatus = LiteProjection::make(_pq->getFilter(), _pq->getProj(), &liteProj); if (!liteProjStatus.isOK()) { return liteProjStatus; } _liteProj.reset(liteProj); } return Status::OK(); }
void IndexCatalogEntry::init(OperationContext* txn, IndexAccessMethod* accessMethod) { verify(_accessMethod == NULL); _accessMethod = accessMethod; _isReady = _catalogIsReady(txn); _head = _catalogHead(txn); _isMultikey = _catalogIsMultikey(txn); if (BSONElement filterElement = _descriptor->getInfoElement("partialFilterExpression")) { invariant(filterElement.isABSONObj()); BSONObj filter = filterElement.Obj(); // TODO SERVER-23618: pass the appropriate CollatorInterface* instead of nullptr. StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse(filter, ExtensionsCallbackDisallowExtensions(), nullptr); // this should be checked in create, so can blow up here invariantOK(statusWithMatcher.getStatus()); _filterExpression = std::move(statusWithMatcher.getValue()); LOG(2) << "have filter expression for " << _ns << " " << _descriptor->indexName() << " " << filter; } if (BSONElement collationElement = _descriptor->getInfoElement("collation")) { invariant(collationElement.isABSONObj()); BSONObj collation = collationElement.Obj(); auto statusWithCollator = CollatorFactoryInterface::get(txn->getServiceContext())->makeFromBSON(collation); invariantOK(statusWithCollator.getStatus()); _collator = std::move(statusWithCollator.getValue()); } }
Status AuthzManagerExternalStateMock::_queryVector( OperationContext* opCtx, const NamespaceString& collectionName, const BSONObj& query, std::vector<BSONObjCollection::iterator>* result) { const CollatorInterface* collator = nullptr; boost::intrusive_ptr<ExpressionContext> expCtx(new ExpressionContext(opCtx, collator)); StatusWithMatchExpression parseResult = MatchExpressionParser::parse(query, std::move(expCtx)); if (!parseResult.isOK()) { return parseResult.getStatus(); } const std::unique_ptr<MatchExpression> matcher = std::move(parseResult.getValue()); NamespaceDocumentMap::iterator mapIt = _documents.find(collectionName); if (mapIt == _documents.end()) return Status::OK(); for (BSONObjCollection::iterator vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt) { if (matcher->matchesBSON(*vecIt)) { result->push_back(vecIt); } } return Status::OK(); }
Status AuthzManagerExternalStateMock::_queryVector( const NamespaceString& collectionName, const BSONObj& query, std::vector<BSONObjCollection::iterator>* result) { StatusWithMatchExpression parseResult = MatchExpressionParser::parse(query); if (!parseResult.isOK()) { return parseResult.getStatus(); } MatchExpression* matcher = parseResult.getValue(); NamespaceDocumentMap::iterator mapIt = _documents.find(collectionName); if (mapIt == _documents.end()) return Status(ErrorCodes::NoMatchingDocument, "No collection named " + collectionName.ns()); for (BSONObjCollection::iterator vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt) { if (matcher->matchesBSON(*vecIt)) { result->push_back(vecIt); } } return Status::OK(); }
int countResults(CollectionScanParams::Direction direction, const BSONObj& filterObj) { AutoGetCollectionForRead ctx(&_txn, ns()); // Configure the scan. CollectionScanParams params; params.collection = ctx.getCollection(); params.direction = direction; params.tailable = false; // Make the filter. StatusWithMatchExpression swme = MatchExpressionParser::parse(filterObj); verify(swme.isOK()); unique_ptr<MatchExpression> filterExpr(swme.getValue()); // Make a scan and have the runner own it. unique_ptr<WorkingSet> ws = make_unique<WorkingSet>(); unique_ptr<PlanStage> ps = make_unique<CollectionScan>(&_txn, params, ws.get(), filterExpr.get()); auto statusWithPlanExecutor = PlanExecutor::make( &_txn, std::move(ws), std::move(ps), params.collection, PlanExecutor::YIELD_MANUAL); ASSERT_OK(statusWithPlanExecutor.getStatus()); unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue()); // Use the runner to count the number of objects scanned. int count = 0; for (BSONObj obj; PlanExecutor::ADVANCED == exec->getNext(&obj, NULL);) { ++count; } return count; }
int countResults(const IndexScanParams& params, BSONObj filterObj = BSONObj()) { AutoGetCollectionForReadCommand ctx(&_opCtx, NamespaceString(ns())); const CollatorInterface* collator = nullptr; const boost::intrusive_ptr<ExpressionContext> expCtx( new ExpressionContext(&_opCtx, collator)); StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse(filterObj, expCtx); verify(statusWithMatcher.isOK()); unique_ptr<MatchExpression> filterExpr = std::move(statusWithMatcher.getValue()); unique_ptr<WorkingSet> ws = stdx::make_unique<WorkingSet>(); unique_ptr<IndexScan> ix = stdx::make_unique<IndexScan>(&_opCtx, params, ws.get(), filterExpr.get()); auto statusWithPlanExecutor = PlanExecutor::make( &_opCtx, std::move(ws), std::move(ix), ctx.getCollection(), PlanExecutor::NO_YIELD); ASSERT_OK(statusWithPlanExecutor.getStatus()); auto exec = std::move(statusWithPlanExecutor.getValue()); int count = 0; PlanExecutor::ExecState state; for (RecordId dl; PlanExecutor::ADVANCED == (state = exec->getNext(NULL, &dl));) { ++count; } ASSERT_EQUALS(PlanExecutor::IS_EOF, state); return count; }
Status AuthzManagerExternalStateMock::_queryVector( const NamespaceString& collectionName, const BSONObj& query, std::vector<BSONObjCollection::iterator>* result) { StatusWithMatchExpression parseResult = MatchExpressionParser::parse(query, MatchExpressionParser::WhereCallback()); if (!parseResult.isOK()) { return parseResult.getStatus(); } const boost::scoped_ptr<MatchExpression> matcher(parseResult.getValue()); NamespaceDocumentMap::iterator mapIt = _documents.find(collectionName); if (mapIt == _documents.end()) return Status::OK(); for (BSONObjCollection::iterator vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt) { if (matcher->matchesBSON(*vecIt)) { result->push_back(vecIt); } } return Status::OK(); }
int countResults(CollectionScanParams::Direction direction, const BSONObj& filterObj) { AutoGetCollectionForRead ctx(&_txn, ns()); // Configure the scan. CollectionScanParams params; params.collection = ctx.getCollection(); params.direction = direction; params.tailable = false; // Make the filter. StatusWithMatchExpression swme = MatchExpressionParser::parse(filterObj); verify(swme.isOK()); auto_ptr<MatchExpression> filterExpr(swme.getValue()); // Make a scan and have the runner own it. WorkingSet* ws = new WorkingSet(); PlanStage* ps = new CollectionScan(&_txn, params, ws, filterExpr.get()); PlanExecutor* rawExec; Status status = PlanExecutor::make(&_txn, ws, ps, params.collection, PlanExecutor::YIELD_MANUAL, &rawExec); ASSERT_OK(status); boost::scoped_ptr<PlanExecutor> exec(rawExec); // Use the runner to count the number of objects scanned. int count = 0; for (BSONObj obj; PlanExecutor::ADVANCED == exec->getNext(&obj, NULL); ) { ++count; } return count; }
/** * Given a match expression, represented as the BSON object 'filterObj', * create a SingleSolutionRunner capable of executing a simple collection * scan. * * The caller takes ownership of the returned SingleSolutionRunner*. */ SingleSolutionRunner* makeCollScanRunner(Client::Context& ctx, BSONObj& filterObj) { CollectionScanParams csparams; csparams.collection = ctx.db()->getCollection( &_txn, ns() ); csparams.direction = CollectionScanParams::FORWARD; auto_ptr<WorkingSet> ws(new WorkingSet()); // Parse the filter. StatusWithMatchExpression swme = MatchExpressionParser::parse(filterObj); verify(swme.isOK()); auto_ptr<MatchExpression> filter(swme.getValue()); // Make the stage. auto_ptr<PlanStage> root(new CollectionScan(csparams, ws.get(), filter.release())); CanonicalQuery* cq; verify(CanonicalQuery::canonicalize(ns(), filterObj, &cq).isOK()); verify(NULL != cq); // Hand the plan off to the single solution runner. SingleSolutionRunner* ssr = new SingleSolutionRunner(ctx.db()->getCollection(&_txn, ns()), cq, new QuerySolution(), root.release(), ws.release()); return ssr; }
void run() { Client::WriteContext ctx(ns()); for (int i = 0; i < 50; ++i) { insert(BSON("foo" << 1 << "bar" << 1)); } addIndex(BSON("foo" << 1)); addIndex(BSON("bar" << 1)); WorkingSet ws; BSONObj filterObj = BSON("foo" << BSON("$ne" << 1)); StatusWithMatchExpression swme = MatchExpressionParser::parse(filterObj); verify(swme.isOK()); auto_ptr<MatchExpression> filterExpr(swme.getValue()); scoped_ptr<AndSortedStage> ah(new AndSortedStage(&ws, filterExpr.get())); // Scan over foo == 1 IndexScanParams params; params.descriptor = getIndex(BSON("foo" << 1)); params.bounds.isSimpleRange = true; params.bounds.startKey = BSON("" << 1); params.bounds.endKey = BSON("" << 1); params.bounds.endKeyInclusive = true; params.direction = 1; ah->addChild(new IndexScan(params, &ws, NULL)); // bar == 1 params.descriptor = getIndex(BSON("bar" << 1)); ah->addChild(new IndexScan(params, &ws, NULL)); // Filter drops everything. ASSERT_EQUALS(0, countResults(ah.get())); }
// $near must be the only field in the expression object. TEST( MatchExpressionParserGeoNear, ParseNearExtraField ) { BSONObj query = fromjson("{loc:{$near:{$maxDistance:100, " "$geometry:{type:\"Point\", coordinates:[0,0]}}, foo: 1}}"); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_FALSE( result.isOK() ); }
TEST(MatchExpressionParserGeoNear, ParseInvalidNearSphere) { { BSONObj query = fromjson("{loc: {$maxDistance: 100, $nearSphere: [0,0]}}"); StatusWithMatchExpression result = MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions()); ASSERT_FALSE(result.isOK()); } { BSONObj query = fromjson("{loc: {$minDistance: 100, $nearSphere: [0,0]}}"); StatusWithMatchExpression result = MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions()); ASSERT_FALSE(result.isOK()); } { BSONObj query = fromjson("{loc: {$nearSphere: [0,0], $maxDistance: {}}}"); ASSERT_THROWS(MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions()), UserException); } { BSONObj query = fromjson("{loc: {$nearSphere: [0,0], $minDistance: {}}}"); ASSERT_THROWS(MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions()), UserException); } { BSONObj query = fromjson("{loc: {$nearSphere: [0,0], $eq: 1}}"); ASSERT_THROWS(MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions()), UserException); } }
Status CanonicalQuery::init(LiteParsedQuery* lpq) { _pq.reset(lpq); // Build a parse tree from the BSONObj in the parsed query. StatusWithMatchExpression swme = MatchExpressionParser::parse(_pq->getFilter()); if (!swme.isOK()) { return swme.getStatus(); } MatchExpression* root = swme.getValue(); root = normalizeTree(root); Status validStatus = isValid(root); if (!validStatus.isOK()) { return validStatus; } _root.reset(root); // Validate the projection if there is one. if (!_pq->getProj().isEmpty()) { ParsedProjection* pp; Status projStatus = ParsedProjection::make(_pq->getProj(), root, &pp); if (!projStatus.isOK()) { return projStatus; } _proj.reset(pp); } return Status::OK(); }
int countResults(const IndexScanParams& params, BSONObj filterObj = BSONObj()) { AutoGetCollectionForRead ctx(&_txn, NamespaceString(ns())); const CollatorInterface* collator = nullptr; StatusWithMatchExpression statusWithMatcher = MatchExpressionParser::parse( filterObj, ExtensionsCallbackDisallowExtensions(), collator); verify(statusWithMatcher.isOK()); unique_ptr<MatchExpression> filterExpr = std::move(statusWithMatcher.getValue()); unique_ptr<WorkingSet> ws = stdx::make_unique<WorkingSet>(); unique_ptr<IndexScan> ix = stdx::make_unique<IndexScan>(&_txn, params, ws.get(), filterExpr.get()); auto statusWithPlanExecutor = PlanExecutor::make( &_txn, std::move(ws), std::move(ix), ctx.getCollection(), PlanExecutor::YIELD_MANUAL); ASSERT_OK(statusWithPlanExecutor.getStatus()); unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue()); int count = 0; PlanExecutor::ExecState state; for (RecordId dl; PlanExecutor::ADVANCED == (state = exec->getNext(NULL, &dl));) { ++count; } ASSERT_EQUALS(PlanExecutor::IS_EOF, state); return count; }
bool run(OperationContext* txn, const string& dbname, BSONObj& jsobj, int, string& errmsg, BSONObjBuilder& result, bool /*fromRepl*/) { AutoGetDb autoDb(txn, dbname, MODE_S); const Database* d = autoDb.getDb(); const DatabaseCatalogEntry* dbEntry = NULL; list<string> names; if ( d ) { dbEntry = d->getDatabaseCatalogEntry(); dbEntry->getCollectionNamespaces( &names ); names.sort(); } scoped_ptr<MatchExpression> matcher; if ( jsobj["filter"].isABSONObj() ) { StatusWithMatchExpression parsed = MatchExpressionParser::parse( jsobj["filter"].Obj() ); if ( !parsed.isOK() ) { return appendCommandStatus( result, parsed.getStatus() ); } matcher.reset( parsed.getValue() ); } BSONArrayBuilder arr; for ( list<string>::const_iterator i = names.begin(); i != names.end(); ++i ) { string ns = *i; StringData collection = nsToCollectionSubstring( ns ); if ( collection == "system.namespaces" ) { continue; } BSONObjBuilder b; b.append( "name", collection ); CollectionOptions options = dbEntry->getCollectionCatalogEntry( txn, ns )->getCollectionOptions(txn); b.append( "options", options.toBSON() ); BSONObj maybe = b.obj(); if ( matcher && !matcher->matchesBSON( maybe ) ) { continue; } arr.append( maybe ); } result.append( "collections", arr.arr() ); return true; }
TEST( MatchExpressionParserTest, SimpleEQ1 ) { BSONObj query = BSON( "x" << 2 ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << 2 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 3 ) ) ); }
TEST( MatchExpressionParserTreeTest, NOT1 ) { BSONObj query = BSON( "x" << BSON( "$not" << BSON( "$gt" << 5 ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << 2 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 8 ) ) ); }
TEST(MatchExpressionParserTest, SimpleEQ1) { BSONObj query = BSON("x" << 2); const CollatorInterface* collator = nullptr; StatusWithMatchExpression result = MatchExpressionParser::parse(query, collator); ASSERT_TRUE(result.isOK()); ASSERT(result.getValue()->matchesBSON(BSON("x" << 2))); ASSERT(!result.getValue()->matchesBSON(BSON("x" << 3))); }
void run() { ScopedTransaction transaction(&_txn, MODE_IX); Lock::DBLock lk(_txn.lockState(), nsToDatabaseSubstring(ns()), MODE_X); Client::Context ctx(&_txn, ns()); Database* db = ctx.db(); Collection* coll = db->getCollection(&_txn, ns()); if (!coll) { WriteUnitOfWork wuow(&_txn); coll = db->createCollection(&_txn, ns()); wuow.commit(); } WorkingSet ws; // Add an object to the DB. insert(BSON("foo" << 5)); set<DiskLoc> locs; getLocs(&locs, coll); ASSERT_EQUALS(size_t(1), locs.size()); // Create a mock stage that returns the WSM. auto_ptr<MockStage> mockStage(new MockStage(&ws)); // Mock data. { WorkingSetMember mockMember; mockMember.state = WorkingSetMember::LOC_AND_IDX; mockMember.loc = *locs.begin(); // State is loc and index, shouldn't be able to get the foo data inside. BSONElement elt; ASSERT_FALSE(mockMember.getFieldDotted("foo", &elt)); mockStage->pushBack(mockMember); } // Make the filter. BSONObj filterObj = BSON("foo" << 6); StatusWithMatchExpression swme = MatchExpressionParser::parse(filterObj); verify(swme.isOK()); auto_ptr<MatchExpression> filterExpr(swme.getValue()); // Matcher requires that foo==6 but we only have data with foo==5. auto_ptr<FetchStage> fetchStage( new FetchStage(&_txn, &ws, mockStage.release(), filterExpr.get(), coll)); // First call should return a fetch request as it's not in memory. WorkingSetID id = WorkingSet::INVALID_ID; PlanStage::StageState state; // Normally we'd return the object but we have a filter that prevents it. state = fetchStage->work(&id); ASSERT_EQUALS(PlanStage::NEED_TIME, state); // No more data to fetch, so, EOF. state = fetchStage->work(&id); ASSERT_EQUALS(PlanStage::IS_EOF, state); }
TEST(MatchExpressionParserTreeTest, NOT1) { BSONObj query = BSON("x" << BSON("$not" << BSON("$gt" << 5))); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); StatusWithMatchExpression result = MatchExpressionParser::parse(query, expCtx); ASSERT_TRUE(result.isOK()); ASSERT(result.getValue()->matchesBSON(BSON("x" << 2))); ASSERT(!result.getValue()->matchesBSON(BSON("x" << 8))); }
std::unique_ptr<MatchExpression> QueryPlannerTest::parseMatchExpression(const BSONObj& obj) { StatusWithMatchExpression status = MatchExpressionParser::parse(obj, ExtensionsCallbackDisallowExtensions()); if (!status.isOK()) { FAIL(str::stream() << "failed to parse query: " << obj.toString() << ". Reason: " << status.getStatus().toString()); } return std::move(status.getValue()); }
TEST(MatchExpressionParserTreeTest, NOT1) { BSONObj query = BSON("x" << BSON("$not" << BSON("$gt" << 5))); const CollatorInterface* collator = nullptr; StatusWithMatchExpression result = MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator); ASSERT_TRUE(result.isOK()); ASSERT(result.getValue()->matchesBSON(BSON("x" << 2))); ASSERT(!result.getValue()->matchesBSON(BSON("x" << 8))); }
Status CanonicalQuery::init(LiteParsedQuery* lpq) { _pq.reset(lpq); // Build a parse tree from the BSONObj in the parsed query. StatusWithMatchExpression swme = MatchExpressionParser::parse(_pq->getFilter()); if (!swme.isOK()) { return swme.getStatus(); } _root.reset(swme.getValue()); return Status::OK(); }
std::unique_ptr<MatchExpression> QueryPlannerTest::parseMatchExpression( const BSONObj& obj, const CollatorInterface* collator) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); expCtx->setCollator(collator); StatusWithMatchExpression status = MatchExpressionParser::parse(obj, std::move(expCtx)); if (!status.isOK()) { FAIL(str::stream() << "failed to parse query: " << obj.toString() << ". Reason: " << status.getStatus().toString()); } return std::move(status.getValue()); }
TEST( MatchExpressionParserTreeTest, NOREmbedded ) { BSONObj query = BSON( "$nor" << BSON_ARRAY( BSON( "x" << 1 ) << BSON( "y" << 2 ) ) ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << 1 ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "y" << 2 ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << 3 ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "y" << 1 ) ) ); }
TEST( MatchExpressionParserLeafTest, NotRegex1 ) { BSONObjBuilder b; b.appendRegex( "$not", "abc", "i" ); BSONObj query = BSON( "x" << b.obj() ); StatusWithMatchExpression result = MatchExpressionParser::parse( query ); ASSERT_TRUE( result.isOK() ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "abc" ) ) ); ASSERT( !result.getValue()->matchesBSON( BSON( "x" << "ABC" ) ) ); ASSERT( result.getValue()->matchesBSON( BSON( "x" << "AC" ) ) ); }
StatusWithMatchExpression MatchExpressionParser::_parseAll( const char* name, const BSONElement& e ) { if ( e.type() != Array ) return StatusWithMatchExpression( ErrorCodes::BadValue, "$all needs an array" ); BSONObj arr = e.Obj(); if ( arr.firstElement().type() == Object && mongoutils::str::equals( "$elemMatch", arr.firstElement().Obj().firstElement().fieldName() ) ) { // $all : [ { $elemMatch : {} } ... ] std::auto_ptr<AllElemMatchOp> temp( new AllElemMatchOp() ); Status s = temp->init( name ); if ( !s.isOK() ) return StatusWithMatchExpression( s ); BSONObjIterator i( arr ); while ( i.more() ) { BSONElement hopefullyElemMatchElemennt = i.next(); if ( hopefullyElemMatchElemennt.type() != Object ) { // $all : [ { $elemMatch : ... }, 5 ] return StatusWithMatchExpression( ErrorCodes::BadValue, "$all/$elemMatch has to be consistent" ); } BSONObj hopefullyElemMatchObj = hopefullyElemMatchElemennt.Obj(); if ( !mongoutils::str::equals( "$elemMatch", hopefullyElemMatchObj.firstElement().fieldName() ) ) { // $all : [ { $elemMatch : ... }, { x : 5 } ] return StatusWithMatchExpression( ErrorCodes::BadValue, "$all/$elemMatch has to be consistent" ); } StatusWithMatchExpression inner = _parseElemMatch( "", hopefullyElemMatchObj.firstElement() ); if ( !inner.isOK() ) return inner; temp->add( static_cast<ArrayMatchingMatchExpression*>( inner.getValue() ) ); } return StatusWithMatchExpression( temp.release() ); } std::auto_ptr<AllMatchExpression> temp( new AllMatchExpression() ); Status s = temp->init( name ); if ( !s.isOK() ) return StatusWithMatchExpression( s ); s = _parseArrayFilterEntries( temp->getArrayFilterEntries(), arr ); if ( !s.isOK() ) return StatusWithMatchExpression( s ); return StatusWithMatchExpression( temp.release() ); }