StatusWith<ParsedDistinct> ParsedDistinct::parse(OperationContext* txn, const NamespaceString& nss, const BSONObj& cmdObj, const ExtensionsCallback& extensionsCallback, bool isExplain) { // Extract the key field. BSONElement keyElt; auto statusKey = bsonExtractTypedField(cmdObj, kKeyField, BSONType::String, &keyElt); if (!statusKey.isOK()) { return {statusKey}; } auto key = keyElt.valuestrsafe(); auto qr = stdx::make_unique<QueryRequest>(nss); // Extract the query field. If the query field is nonexistent, an empty query is used. if (BSONElement queryElt = cmdObj[kQueryField]) { if (queryElt.type() == BSONType::Object) { qr->setFilter(queryElt.embeddedObject()); } else if (queryElt.type() != BSONType::jstNULL) { return Status(ErrorCodes::TypeMismatch, str::stream() << "\"" << kQueryField << "\" had the wrong type. Expected " << typeName(BSONType::Object) << " or " << typeName(BSONType::jstNULL) << ", found " << typeName(queryElt.type())); } } // Extract the collation field, if it exists. if (BSONElement collationElt = cmdObj[kCollationField]) { if (collationElt.type() != BSONType::Object) { return Status(ErrorCodes::TypeMismatch, str::stream() << "\"" << kCollationField << "\" had the wrong type. Expected " << typeName(BSONType::Object) << ", found " << typeName(collationElt.type())); } qr->setCollation(collationElt.embeddedObject()); } qr->setExplain(isExplain); auto cq = CanonicalQuery::canonicalize(txn, std::move(qr), extensionsCallback); if (!cq.isOK()) { return cq.getStatus(); } return ParsedDistinct(std::move(cq.getValue()), std::move(key)); }
StatusWith<ParsedDistinct> ParsedDistinct::parse(OperationContext* opCtx, const NamespaceString& nss, const BSONObj& cmdObj, const ExtensionsCallback& extensionsCallback, bool isExplain, const CollatorInterface* defaultCollator) { IDLParserErrorContext ctx("distinct"); DistinctCommand parsedDistinct(nss); try { parsedDistinct = DistinctCommand::parse(ctx, cmdObj); } catch (...) { return exceptionToStatus(); } auto qr = stdx::make_unique<QueryRequest>(nss); if (parsedDistinct.getKey().find('\0') != std::string::npos) { return Status(ErrorCodes::Error(31032), "Key field cannot contain an embedded null byte"); } // Create a projection on the fields needed by the distinct command, so that the query planner // will produce a covered plan if possible. qr->setProj(getDistinctProjection(std::string(parsedDistinct.getKey()))); if (auto query = parsedDistinct.getQuery()) { qr->setFilter(query.get()); } if (auto collation = parsedDistinct.getCollation()) { qr->setCollation(collation.get()); } if (auto comment = parsedDistinct.getComment()) { qr->setComment(comment.get().toString()); } // The IDL parser above does not handle generic command arguments. Since the underlying query // request requires the following options, manually parse and verify them here. if (auto readConcernElt = cmdObj[repl::ReadConcernArgs::kReadConcernFieldName]) { if (readConcernElt.type() != BSONType::Object) { return Status(ErrorCodes::TypeMismatch, str::stream() << "\"" << repl::ReadConcernArgs::kReadConcernFieldName << "\" had the wrong type. Expected " << typeName(BSONType::Object) << ", found " << typeName(readConcernElt.type())); } qr->setReadConcern(readConcernElt.embeddedObject()); } if (auto queryOptionsElt = cmdObj[QueryRequest::kUnwrappedReadPrefField]) { if (queryOptionsElt.type() != BSONType::Object) { return Status(ErrorCodes::TypeMismatch, str::stream() << "\"" << QueryRequest::kUnwrappedReadPrefField << "\" had the wrong type. Expected " << typeName(BSONType::Object) << ", found " << typeName(queryOptionsElt.type())); } qr->setUnwrappedReadPref(queryOptionsElt.embeddedObject()); } if (auto maxTimeMSElt = cmdObj[QueryRequest::cmdOptionMaxTimeMS]) { auto maxTimeMS = QueryRequest::parseMaxTimeMS(maxTimeMSElt); if (!maxTimeMS.isOK()) { return maxTimeMS.getStatus(); } qr->setMaxTimeMS(static_cast<unsigned int>(maxTimeMS.getValue())); } qr->setExplain(isExplain); const boost::intrusive_ptr<ExpressionContext> expCtx; auto cq = CanonicalQuery::canonicalize(opCtx, std::move(qr), expCtx, extensionsCallback, MatchExpressionParser::kAllowAllSpecialFeatures); if (!cq.isOK()) { return cq.getStatus(); } if (cq.getValue()->getQueryRequest().getCollation().isEmpty() && defaultCollator) { cq.getValue()->setCollator(defaultCollator->clone()); } return ParsedDistinct(std::move(cq.getValue()), parsedDistinct.getKey().toString()); }