TEST(FTSSpec, Extra3) { BSONObj user = BSON("key" << BSON("x" << 1 << "data" << "text")); BSONObj fixed = assertGet(FTSSpec::fixSpec(user)); ASSERT_BSONOBJ_EQ(BSON("x" << 1 << "_fts" << "text" << "_ftsx" << 1), fixed["key"].Obj()); ASSERT_BSONOBJ_EQ(BSON("data" << 1), fixed["weights"].Obj()); BSONObj fixed2 = assertGet(FTSSpec::fixSpec(fixed)); ASSERT_BSONOBJ_EQ(fixed, fixed2); FTSSpec spec(fixed); ASSERT_EQUALS(1U, spec.numExtraBefore()); ASSERT_EQUALS(StringData("x"), spec.extraBefore(0)); ASSERT_EQUALS(0U, spec.numExtraAfter()); BSONObj prefix; ASSERT(spec.getIndexPrefix(BSON("x" << 2), &prefix).isOK()); ASSERT_BSONOBJ_EQ(BSON("x" << 2), prefix); ASSERT(spec.getIndexPrefix(BSON("x" << 3 << "y" << 4), &prefix).isOK()); ASSERT_BSONOBJ_EQ(BSON("x" << 3), prefix); ASSERT(!spec.getIndexPrefix(BSON("x" << BSON("$gt" << 5)), &prefix).isOK()); ASSERT(!spec.getIndexPrefix(BSON("y" << 4), &prefix).isOK()); ASSERT(!spec.getIndexPrefix(BSONObj(), &prefix).isOK()); }
TEST_F(UpdateArrayNodeTest, UpdateIsAppliedToAllMatchingElements) { auto update = fromjson("{$set: {'a.$[i]': 2}}"); auto arrayFilter = fromjson("{i: 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$[i]"], expCtx, arrayFilters, foundIdentifiers)); mutablebson::Document doc(fromjson("{a: [0, 1, 0]}")); addIndexedPath("a"); auto result = root.apply(getApplyParams(doc.root())); ASSERT_TRUE(result.indexesAffected); ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [2, 1, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [2, 1, 2]}}"), getLogDoc()); ASSERT_EQUALS("{a.0, a.2}", getModifiedPaths()); }
/** * Test the way cache entries are added (either "active" or "inactive") to the plan cache. */ TEST_F(QueryStageCachedPlan, QueryStageCachedPlanAddsActiveCacheEntries) { AutoGetCollectionForReadCommand ctx(&_opCtx, nss); Collection* collection = ctx.getCollection(); ASSERT(collection); // Never run - just used as a key for the cache's get() functions, since all of the other // CanonicalQueries created in this test will have this shape. const auto shapeCq = canonicalQueryFromFilterObj(opCtx(), nss, fromjson("{a: {$gte: 123}, b: {$gte: 123}}")); // Query can be answered by either index on "a" or index on "b". const auto noResultsCq = canonicalQueryFromFilterObj(opCtx(), nss, fromjson("{a: {$gte: 11}, b: {$gte: 11}}")); // We shouldn't have anything in the plan cache for this shape yet. PlanCache* cache = collection->infoCache()->getPlanCache(); ASSERT(cache); ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kNotPresent); // Run the CachedPlanStage with a long-running child plan. Replanning should be // triggered and an inactive entry will be added. forceReplanning(collection, noResultsCq.get()); // Check for an inactive cache entry. ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kPresentInactive); // The works should be 1 for the entry since the query we ran should not have any results. auto entry = assertGet(cache->getEntry(*shapeCq)); size_t works = 1U; ASSERT_EQ(entry->works, works); const size_t kExpectedNumWorks = 10; for (int i = 0; i < std::ceil(std::log(kExpectedNumWorks) / std::log(2)); ++i) { works *= 2; // Run another query of the same shape, which is less selective, and therefore takes // longer). auto someResultsCq = canonicalQueryFromFilterObj(opCtx(), nss, fromjson("{a: {$gte: 1}, b: {$gte: 0}}")); forceReplanning(collection, someResultsCq.get()); ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kPresentInactive); // The works on the cache entry should have doubled. entry = assertGet(cache->getEntry(*shapeCq)); ASSERT_EQ(entry->works, works); } // Run another query which takes less time, and be sure an active entry is created. auto fewResultsCq = canonicalQueryFromFilterObj(opCtx(), nss, fromjson("{a: {$gte: 6}, b: {$gte: 0}}")); forceReplanning(collection, fewResultsCq.get()); // Now there should be an active cache entry. ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kPresentActive); entry = assertGet(cache->getEntry(*shapeCq)); // This will query will match {a: 6} through {a:9} (4 works), plus one for EOF = 5 works. ASSERT_EQ(entry->works, 5U); }
TEST_F(QueryStageCachedPlan, DeactivatesEntriesOnReplan) { AutoGetCollectionForReadCommand ctx(&_opCtx, nss); Collection* collection = ctx.getCollection(); ASSERT(collection); // Never run - just used as a key for the cache's get() functions, since all of the other // CanonicalQueries created in this test will have this shape. const auto shapeCq = canonicalQueryFromFilterObj(opCtx(), nss, fromjson("{a: {$gte: 123}, b: {$gte: 123}}")); // Query can be answered by either index on "a" or index on "b". const auto noResultsCq = canonicalQueryFromFilterObj(opCtx(), nss, fromjson("{a: {$gte: 11}, b: {$gte: 11}}")); // We shouldn't have anything in the plan cache for this shape yet. PlanCache* cache = collection->infoCache()->getPlanCache(); ASSERT(cache); ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kNotPresent); // Run the CachedPlanStage with a long-running child plan. Replanning should be // triggered and an inactive entry will be added. forceReplanning(collection, noResultsCq.get()); // Check for an inactive cache entry. ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kPresentInactive); // Run the plan again, to create an active entry. forceReplanning(collection, noResultsCq.get()); // The works should be 1 for the entry since the query we ran should not have any results. ASSERT_EQ(cache->get(*noResultsCq.get()).state, PlanCache::CacheEntryState::kPresentActive); auto entry = assertGet(cache->getEntry(*shapeCq)); size_t works = 1U; ASSERT_EQ(entry->works, works); // Run another query which takes long enough to evict the active cache entry. The current // cache entry's works value is a very low number. When replanning is triggered, the cache // entry will be deactivated, but the new plan will not overwrite it, since the new plan will // have a higher works. Therefore, we will be left in an inactive entry which has had its works // value doubled from 1 to 2. auto highWorksCq = canonicalQueryFromFilterObj(opCtx(), nss, fromjson("{a: {$gte: 0}, b: {$gte:0}}")); forceReplanning(collection, highWorksCq.get()); ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kPresentInactive); ASSERT_EQ(assertGet(cache->getEntry(*shapeCq))->works, 2U); // Again, force replanning. This time run the initial query which finds no results. The multi // planner will choose a plan with works value lower than the existing inactive // entry. Replanning will thus deactivate the existing entry (it's already // inactive so this is a noop), then create a new entry with a works value of 1. forceReplanning(collection, noResultsCq.get()); ASSERT_EQ(cache->get(*shapeCq).state, PlanCache::CacheEntryState::kPresentActive); ASSERT_EQ(assertGet(cache->getEntry(*shapeCq))->works, 1U); }
TEST(FTSSpec, Extra2) { BSONObj user = BSON("key" << BSON("data" << "text" << "x" << 1)); BSONObj fixed = assertGet(FTSSpec::fixSpec(user)); FTSSpec spec(fixed); ASSERT_EQUALS(0U, spec.numExtraBefore()); ASSERT_EQUALS(1U, spec.numExtraAfter()); ASSERT_EQUALS(StringData("x"), spec.extraAfter(0)); BSONObj fixed2 = assertGet(FTSSpec::fixSpec(fixed)); ASSERT_BSONOBJ_EQ(fixed, fixed2); }
/** * Assert that fixSpec() accepts the provided text index spec. */ void assertFixSuccess(const std::string& s) { BSONObj user = fromjson(s); try { // fixSpec() should not throw on a valid spec. BSONObj fixed = assertGet(FTSSpec::fixSpec(user)); // fixSpec() on an already-fixed spec shouldn't change it. BSONObj fixed2 = assertGet(FTSSpec::fixSpec(fixed)); ASSERT_BSONOBJ_EQ(fixed, fixed2); } catch (UserException&) { ASSERT(false); } }
TEST(FTSSpec, ScoreMultipleField1) { BSONObj user = BSON("key" << BSON("title" << "text" << "text" << "text") << "weights" << BSON("title" << 10)); FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); TermFrequencyMap m; spec.scoreDocument(BSON("title" << "cat sat run" << "text" << "cat book"), &m); ASSERT_EQUALS(4U, m.size()); ASSERT_EQUALS(m["sat"], m["run"]); ASSERT(m["sat"] > 0); ASSERT(m["cat"] > m["sat"]); ASSERT(m["cat"] > m["book"]); ASSERT(m["book"] > 0); ASSERT(m["book"] < m["sat"]); }
TEST(QueryRequestTest, DefaultQueryParametersCorrect) { BSONObj cmdObj = fromjson("{find: 'testns'}"); const NamespaceString nss("test.testns"); std::unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, false))); ASSERT_FALSE(qr->getSkip()); ASSERT_FALSE(qr->getLimit()); ASSERT_EQUALS(true, qr->wantMore()); ASSERT_FALSE(qr->getNToReturn()); ASSERT_EQUALS(false, qr->isExplain()); ASSERT_EQUALS(0, qr->getMaxTimeMS()); ASSERT_EQUALS(false, qr->returnKey()); ASSERT_EQUALS(false, qr->showRecordId()); ASSERT_EQUALS(false, qr->hasReadPref()); ASSERT_EQUALS(false, qr->isTailable()); ASSERT_EQUALS(false, qr->isSlaveOk()); ASSERT_EQUALS(false, qr->isOplogReplay()); ASSERT_EQUALS(false, qr->isNoCursorTimeout()); ASSERT_EQUALS(false, qr->isTailableAndAwaitData()); ASSERT_EQUALS(false, qr->isExhaust()); ASSERT_EQUALS(false, qr->isAllowPartialResults()); }
// Multi-language test_6: test wildcard spec with override TEST(FTSSpec, NestedLanguages_WildcardOverride) { BSONObj indexSpec = BSON("key" << BSON("$**" << "text") << "weights" << BSON("d.e.f" << 20)); FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( "{ language : \"english\"," " b : \"walking\"," " c : { e: \"walked\" }," " d : " " { language : \"danish\"," " e :" " [ { f : \"foredrag\" }," " { f : \"foredragsholder\" }," " { f : \"lector\" } ]" " }" "}"); spec.scoreDocument(obj, &tfm); set<string> hits; hits.insert("foredrag"); hits.insert("foredragshold"); hits.insert("lector"); hits.insert("walk"); for (TermFrequencyMap::const_iterator i = tfm.begin(); i != tfm.end(); ++i) { string term = i->first; ASSERT_EQUALS(1U, hits.count(term)); } }
TEST(QueryRequestTest, ParseFromLegacyQuery) { const auto kSkip = 1; const auto kNToReturn = 2; BSONObj queryObj = fromjson(R"({ query: {query: 1}, orderby: {sort: 1}, $hint: {hint: 1}, $explain: false, $min: {x: 'min'}, $max: {x: 'max'} })"); const NamespaceString nss("test.testns"); unique_ptr<QueryRequest> qr(assertGet(QueryRequest::fromLegacyQuery( nss, queryObj, BSON("proj" << 1), kSkip, kNToReturn, QueryOption_Exhaust))); ASSERT_EQ(qr->nss(), nss); ASSERT_BSONOBJ_EQ(qr->getFilter(), fromjson("{query: 1}")); ASSERT_BSONOBJ_EQ(qr->getProj(), fromjson("{proj: 1}")); ASSERT_BSONOBJ_EQ(qr->getSort(), fromjson("{sort: 1}")); ASSERT_BSONOBJ_EQ(qr->getHint(), fromjson("{hint: 1}")); ASSERT_BSONOBJ_EQ(qr->getMin(), fromjson("{x: 'min'}")); ASSERT_BSONOBJ_EQ(qr->getMax(), fromjson("{x: 'max'}")); ASSERT_EQ(qr->getSkip(), boost::optional<long long>(kSkip)); ASSERT_EQ(qr->getNToReturn(), boost::optional<long long>(kNToReturn)); ASSERT_EQ(qr->wantMore(), true); ASSERT_EQ(qr->isExplain(), false); ASSERT_EQ(qr->isSlaveOk(), false); ASSERT_EQ(qr->isOplogReplay(), false); ASSERT_EQ(qr->isNoCursorTimeout(), false); ASSERT_EQ(qr->isTailable(), false); ASSERT_EQ(qr->isExhaust(), true); ASSERT_EQ(qr->isAllowPartialResults(), false); ASSERT_EQ(qr->getOptions(), QueryOption_Exhaust); }
TEST(FTSSpec, Extra1) { BSONObj user = BSON("key" << BSON("data" << "text")); FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); ASSERT_EQUALS(0U, spec.numExtraBefore()); ASSERT_EQUALS(0U, spec.numExtraAfter()); }
TEST(QueryRequestTest, ParseFromCommandAllFlagsTrue) { BSONObj cmdObj = fromjson( "{find: 'testns'," "tailable: true," "oplogReplay: true," "noCursorTimeout: true," "awaitData: true," "allowPartialResults: true," "readOnce: true," "allowSpeculativeMajorityRead: true}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); // Test that all the flags got set to true. ASSERT(qr->isTailable()); ASSERT(!qr->isSlaveOk()); ASSERT(qr->isOplogReplay()); ASSERT(qr->isNoCursorTimeout()); ASSERT(qr->isTailableAndAwaitData()); ASSERT(qr->isAllowPartialResults()); ASSERT(qr->isReadOnce()); ASSERT(qr->allowSpeculativeMajorityRead()); }
// Multi-language test_4: test pruning TEST(FTSSpec, NestedLanguages_PathPruning) { BSONObj indexSpec = BSON("key" << BSON("a.b.c" << "text")); FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( "{ language : \"english\"," " a : " " { language : \"danish\"," " bc : \"foo\"," " b : { d: \"bar\" }," " b :" " [ { c : \"foredrag\" }," " { c : \"foredragsholder\" }," " { c : \"lector\" } ]" " }" "}"); spec.scoreDocument(obj, &tfm); set<string> hits; hits.insert("foredrag"); hits.insert("foredragshold"); hits.insert("lector"); for (TermFrequencyMap::const_iterator i = tfm.begin(); i != tfm.end(); ++i) { string term = i->first; ASSERT_EQUALS(1U, hits.count(term)); } }
// Multi-language test_1: test independent stemming per sub-document TEST(FTSSpec, NestedLanguages_PerArrayItemStemming) { BSONObj indexSpec = BSON("key" << BSON("a.b.c" << "text")); FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; BSONObj obj = fromjson( "{ a :" " { b :" " [ { c : \"walked\", language : \"english\" }," " { c : \"camminato\", language : \"italian\" }," " { c : \"ging\", language : \"german\" } ]" " }" " }"); spec.scoreDocument(obj, &tfm); set<string> hits; hits.insert("walk"); hits.insert("cammin"); hits.insert("ging"); for (TermFrequencyMap::const_iterator i = tfm.begin(); i != tfm.end(); ++i) { string term = i->first; ASSERT_EQUALS(1U, hits.count(term)); } }
TEST(QueryRequestTest, ParseFromCommandReadOnceDefaultsToFalse) { BSONObj cmdObj = fromjson("{find: 'testns'}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); ASSERT(!qr->isReadOnce()); }
TEST(QueryRequestTest, ParseCommandIsFromFindCommand) { BSONObj cmdObj = fromjson("{find: 'testns'}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); ASSERT_FALSE(qr->getNToReturn()); }
TEST(QueryRequestTest, ParseFromLegacyStringMetaOpComment) { BSONObj queryObj = fromjson( "{$query: {a: 1}," "$comment: 'ParseFromLegacyStringMetaOpComment'}"); const NamespaceString nss("test.testns"); unique_ptr<QueryRequest> qr( assertGet(QueryRequest::fromLegacyQuery(nss, queryObj, BSONObj(), 0, 0, 0))); ASSERT_EQ(qr->getComment(), "ParseFromLegacyStringMetaOpComment"); ASSERT_BSONOBJ_EQ(qr->getFilter(), fromjson("{a: 1}")); }
TEST(FTSSpec, NestedArraysPos1) { BSONObj user = BSON("key" << BSON("a.b" << "text")); FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); // The following document matches {"a.b": {$type: 2}}, so "term" should be indexed. BSONObj obj = fromjson("{a: [{b: ['term']}]}"); // indirectly nested arrays TermFrequencyMap m; spec.scoreDocument(obj, &m); ASSERT_EQUALS(1U, m.size()); }
TEST(FTSSpec, NestedArraysPos2) { BSONObj user = BSON("key" << BSON("$**" << "text")); FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); // The wildcard spec implies a full recursive traversal, so "term" should be indexed. BSONObj obj = fromjson("{a: {b: [['term']]}}"); // directly nested arrays TermFrequencyMap m; spec.scoreDocument(obj, &m); ASSERT_EQUALS(1U, m.size()); }
/** Test differences across textIndexVersion values in handling of nested arrays. */ TEST(FTSSpec, TextIndexLegacyNestedArrays) { BSONObj obj = fromjson("{a: [{b: ['hello']}]}"); // textIndexVersion=1 FTSSpec objects do not index nested arrays. { BSONObj indexSpec = fromjson("{key: {'a.b': 'text'}, textIndexVersion: 1}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 0U); } // textIndexVersion=2 FTSSpec objects do index nested arrays. { BSONObj indexSpec = fromjson("{key: {'a.b': 'text'}, textIndexVersion: 2}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 1U); } }
TEST(QueryRequestTest, ParseFromLegacyObjMetaOpComment) { BSONObj queryObj = fromjson( "{$query: {a: 1}," "$comment: {b: 2, c: {d: 'ParseFromLegacyObjMetaOpComment'}}}"); const NamespaceString nss("test.testns"); unique_ptr<QueryRequest> qr( assertGet(QueryRequest::fromLegacyQuery(nss, queryObj, BSONObj(), 0, 0, 0))); // Ensure that legacy comment meta-operator is parsed to a string comment ASSERT_EQ(qr->getComment(), "{ b: 2, c: { d: \"ParseFromLegacyObjMetaOpComment\" } }"); ASSERT_BSONOBJ_EQ(qr->getFilter(), fromjson("{a: 1}")); }
TEST(QueryRequestTest, ParseFromCommandBatchSizeZero) { BSONObj cmdObj = fromjson("{find: 'testns', batchSize: 0}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); ASSERT(qr->getBatchSize()); ASSERT_EQ(0, *qr->getBatchSize()); ASSERT(!qr->getLimit()); }
TEST(QueryRequestTest, ParseFromCommandLimitIsZero) { BSONObj cmdObj = fromjson( "{find: 'testns'," "limit: 0," "filter: {a: 3}}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); ASSERT_BSONOBJ_EQ(BSON("a" << 3), qr->getFilter()); ASSERT_FALSE(qr->getLimit()); }
TEST(QueryRequestTest, ParseFromCommandLargeSkip) { BSONObj cmdObj = fromjson( "{find: 'testns'," "filter: {a: 1}," "skip: 8000000000}"); // 8 * 1000 * 1000 * 1000 const NamespaceString nss("test.testns"); const bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); ASSERT_EQUALS(8LL * 1000 * 1000 * 1000, *qr->getSkip()); }
/** Test differences across textIndexVersion values in handling of language annotations. */ TEST(FTSSpec, TextIndexLegacyLanguageRecognition) { BSONObj obj = fromjson("{a: 'the', language: 'EN'}"); // textIndexVersion=1 FTSSpec objects treat two-letter language annotations as "none" // for purposes of stopword processing. { BSONObj indexSpec = fromjson("{key: {'a': 'text'}, textIndexVersion: 1}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 1U); // "the" not recognized as stopword } // textIndexVersion=2 FTSSpec objects recognize two-letter codes. { BSONObj indexSpec = fromjson("{key: {'a': 'text'}, textIndexVersion: 2}"); FTSSpec spec(assertGet(FTSSpec::fixSpec(indexSpec))); TermFrequencyMap tfm; spec.scoreDocument(obj, &tfm); ASSERT_EQUALS(tfm.size(), 0U); // "the" recognized as stopword } }
TEST(FTSSpec, ScoreMultipleField2) { // Test where one indexed field is a parent component of another indexed field. BSONObj user = BSON("key" << BSON("a" << "text" << "a.b" << "text")); FTSSpec spec(assertGet(FTSSpec::fixSpec(user))); TermFrequencyMap m; spec.scoreDocument(BSON("a" << BSON("b" << "term")), &m); ASSERT_EQUALS(1U, m.size()); }
TEST(QueryRequestTest, ParseFromCommandWithOptions) { BSONObj cmdObj = fromjson( "{find: 'testns'," "filter: {a: 3}," "sort: {a: 1}," "projection: {_id: 0, a: 1}," "showRecordId: true}}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); // Make sure the values from the command BSON are reflected in the QR. ASSERT(qr->showRecordId()); }
TEST(QueryRequestTest, ParseFromCommandHintAsString) { BSONObj cmdObj = fromjson( "{find: 'testns'," "filter: {a: 1}," "hint: 'foo_1'}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); BSONObj hintObj = qr->getHint(); ASSERT_BSONOBJ_EQ(BSON("$hint" << "foo_1"), hintObj); }
TEST(QueryRequestTest, ParseFromCommandCommentWithValidMinMax) { BSONObj cmdObj = fromjson( "{find: 'testns'," "comment: 'the comment'," "min: {a: 1}," "max: {a: 2}}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); ASSERT_EQUALS("the comment", qr->getComment()); BSONObj expectedMin = BSON("a" << 1); ASSERT_EQUALS(0, expectedMin.woCompare(qr->getMin())); BSONObj expectedMax = BSON("a" << 2); ASSERT_EQUALS(0, expectedMax.woCompare(qr->getMax())); }
TEST(QueryRequestTest, AsFindCommandAllNonOptionFields) { BSONObj cmdObj = fromjson( "{find: 'testns'," "filter: {a: 1}," "projection: {c: 1}," "sort: {b: 1}," "hint: {d: 1}," "readConcern: {e: 1}," "collation: {f: 1}," "skip: 5," "limit: 3," "batchSize: 90," "singleBatch: true}"); const NamespaceString nss("test.testns"); bool isExplain = false; unique_ptr<QueryRequest> qr( assertGet(QueryRequest::makeFromFindCommand(nss, cmdObj, isExplain))); ASSERT_BSONOBJ_EQ(cmdObj, qr->asFindCommand()); }