void ParsedExclusionProjection::parse(const boost::intrusive_ptr<ExpressionContext>& expCtx, const BSONObj& spec, ExclusionNode* node, size_t depth) { for (auto elem : spec) { const auto fieldName = elem.fieldNameStringData().toString(); // A $ should have been detected in ParsedAggregationProjection's parsing before we get // here. invariant(fieldName[0] != '$'); switch (elem.type()) { case BSONType::Bool: case BSONType::NumberInt: case BSONType::NumberLong: case BSONType::NumberDouble: case BSONType::NumberDecimal: { // We have already verified this is an exclusion projection. invariant(!elem.trueValue()); node->excludePath(FieldPath(fieldName)); break; } case BSONType::Object: { // This object represents a nested projection specification, like the sub-object in // {a: {b: 0, c: 0}} or {"a.b": {c: 0}}. ExclusionNode* child; if (elem.fieldNameStringData().find('.') == std::string::npos) { child = node->addOrGetChild(fieldName); } else { // A dotted field is not allowed in a sub-object, and should have been detected // in ParsedAggregationProjection's parsing before we get here. invariant(depth == 0); // We need to keep adding children to our tree until we create a child that // represents this dotted path. child = node; auto fullPath = FieldPath(fieldName); while (fullPath.getPathLength() > 1) { child = child->addOrGetChild(fullPath.getFieldName(0)); fullPath = fullPath.tail(); } // It is illegal to construct an empty FieldPath, so the above loop ends one // iteration too soon. Add the last path here. child = child->addOrGetChild(fullPath.fullPath()); } parse(expCtx, elem.Obj(), child, depth + 1); break; } default: { MONGO_UNREACHABLE; } } } }
void ProjectionSpecValidator::validate() { if (_rawObj.isEmpty()) { uasserted(40177, "specification must have at least one field"); } for (auto&& elem : _rawObj) { parseElement(elem, FieldPath(elem.fieldName())); } }
intrusive_ptr<DocumentSource> DocumentSourceUnwind::createFromBson( BSONElement *pBsonElement, const intrusive_ptr<ExpressionContext> &pExpCtx) { /* The value of $unwind should just be a field path. */ uassert(15981, str::stream() << "the " << unwindName << " field path must be specified as a string", pBsonElement->type() == String); string prefixedPathString(pBsonElement->str()); string pathString(Expression::removeFieldPrefix(prefixedPathString)); intrusive_ptr<DocumentSourceUnwind> pUnwind(new DocumentSourceUnwind(pExpCtx)); pUnwind->unwindPath(FieldPath(pathString)); return pUnwind; }
void ParsedExclusionProjection::parse(const BSONObj& spec, ExclusionNode* node, size_t depth) { bool idSpecified = false; for (auto elem : spec) { const auto fieldName = elem.fieldNameStringData(); // A $ should have been detected by ParsedAggregationProjection before we get here. invariant(fieldName[0] != '$'); // Track whether the projection spec specifies a desired behavior for the _id field. idSpecified = idSpecified || fieldName == "_id"_sd || fieldName.startsWith("_id."_sd); switch (elem.type()) { case BSONType::Bool: case BSONType::NumberInt: case BSONType::NumberLong: case BSONType::NumberDouble: case BSONType::NumberDecimal: { // We have already verified this is an exclusion projection. _id is the only field // which is permitted to be explicitly included here. invariant(!elem.trueValue() || elem.fieldNameStringData() == "_id"_sd); if (!elem.trueValue()) { node->addProjectionForPath(FieldPath(fieldName)); } break; } case BSONType::Object: { // This object represents a nested projection specification, like the sub-object in // {a: {b: 0, c: 0}} or {"a.b": {c: 0}}. ExclusionNode* child; if (elem.fieldNameStringData().find('.') == std::string::npos) { child = node->addOrGetChild(fieldName.toString()); } else { // A dotted field is not allowed in a sub-object, and should have been detected // in ParsedAggregationProjection's parsing before we get here. invariant(depth == 0); // We need to keep adding children to our tree until we create a child that // represents this dotted path. child = node; auto fullPath = FieldPath(fieldName); while (fullPath.getPathLength() > 1) { child = child->addOrGetChild(fullPath.getFieldName(0).toString()); fullPath = fullPath.tail(); } // It is illegal to construct an empty FieldPath, so the above loop ends one // iteration too soon. Add the last path here. child = child->addOrGetChild(fullPath.fullPath()); } parse(elem.Obj(), child, depth + 1); break; } default: { MONGO_UNREACHABLE; } } } // If _id was not specified, then doing nothing will cause it to be included. If the default _id // policy is kExcludeId, we add a new entry for _id to the ExclusionNode tree here. if (!idSpecified && _policies.idPolicy == ProjectionPolicies::DefaultIdPolicy::kExcludeId) { _root->addProjectionForPath({FieldPath("_id")}); } }