intrusive_ptr<DocumentSource> DocumentSourceProject::createFromBson( BSONElement *pBsonElement, const intrusive_ptr<ExpressionContext> &pExpCtx) { /* validate */ uassert(15969, str::stream() << projectName << " specification must be an object", pBsonElement->type() == Object); intrusive_ptr<DocumentSourceProject> pProject(new DocumentSourceProject(pExpCtx)); BSONObj projectObj(pBsonElement->Obj()); pProject->_raw = projectObj.getOwned(); // probably not necessary, but better to be safe Expression::ObjectCtx objectCtx( Expression::ObjectCtx::DOCUMENT_OK | Expression::ObjectCtx::TOP_LEVEL | Expression::ObjectCtx::INCLUSION_OK ); intrusive_ptr<Expression> parsed = Expression::parseObject(pBsonElement, &objectCtx); ExpressionObject* exprObj = dynamic_cast<ExpressionObject*>(parsed.get()); massert(16402, "parseObject() returned wrong type of Expression", exprObj); uassert(16403, "$projection requires at least one output field", exprObj->getFieldCount()); pProject->pEO = exprObj; #if defined(_DEBUG) if (exprObj->isSimple()) { set<string> deps; vector<string> path; exprObj->addDependencies(deps, &path); pProject->_simpleProjection.init(depsToProjection(deps)); } #endif return pProject; }
intrusive_ptr<DocumentSource> DocumentSourceProject::createFromBson( BSONElement *pBsonElement, const intrusive_ptr<ExpressionContext> &pExpCtx) { /* validate */ uassert(15969, str::stream() << projectName << " specification must be an object", pBsonElement->type() == Object); /* chain the projection onto the original source */ intrusive_ptr<DocumentSourceProject> pProject( DocumentSourceProject::create(pExpCtx)); /* Pull out the $project object. This should just be a list of field inclusion or exclusion specifications. Note you can't do both, except for the case of _id. */ BSONObj projectObj(pBsonElement->Obj()); BSONObjIterator fieldIterator(projectObj); Expression::ObjectCtx objectCtx( Expression::ObjectCtx::DOCUMENT_OK); while(fieldIterator.more()) { BSONElement outFieldElement(fieldIterator.next()); string outFieldPath(outFieldElement.fieldName()); string inFieldName(outFieldPath); BSONType specType = outFieldElement.type(); int fieldInclusion = -1; switch(specType) { case NumberDouble: { double inclusion = outFieldElement.numberDouble(); fieldInclusion = static_cast<int>(inclusion); goto IncludeExclude; } case NumberLong: { long long inclusion = outFieldElement.numberLong(); fieldInclusion = static_cast<int>(inclusion); goto IncludeExclude; } case NumberInt: /* just a plain integer include/exclude specification */ fieldInclusion = outFieldElement.numberInt(); IncludeExclude: uassert(15970, str::stream() << "field inclusion or exclusion specification for \"" << outFieldPath << "\" must be true, 1, false, or zero", ((fieldInclusion == 0) || (fieldInclusion == 1))); if (fieldInclusion == 0) pProject->excludePath(outFieldPath); else pProject->includePath(outFieldPath); break; case Bool: /* just a plain boolean include/exclude specification */ fieldInclusion = (outFieldElement.Bool() ? 1 : 0); goto IncludeExclude; case String: /* include a field, with rename */ fieldInclusion = 1; inFieldName = outFieldElement.String(); pProject->addField( outFieldPath, ExpressionFieldPath::create( Expression::removeFieldPrefix(inFieldName))); break; case Object: { intrusive_ptr<Expression> pDocument( Expression::parseObject(&outFieldElement, &objectCtx)); /* add The document expression to the projection */ pProject->addField(outFieldPath, pDocument); break; } default: uassert(15971, str::stream() << "invalid BSON type (" << specType << ") for " << projectName << " field " << outFieldPath, false); } } return pProject; }
intrusive_ptr<DocumentSource> DocumentSourceProject::createFromBson( BSONElement *pBsonElement, const intrusive_ptr<ExpressionContext> &pCtx) { /* validate */ assert(pBsonElement->type() == Object); // CW TODO user error /* chain the projection onto the original source */ intrusive_ptr<DocumentSourceProject> pProject( DocumentSourceProject::create()); /* Pull out the $project object. This should just be a list of field inclusion or exclusion specifications. Note you can't do both, except for the case of _id. */ BSONObj projectObj(pBsonElement->Obj()); BSONObjIterator fieldIterator(projectObj); Expression::ObjectCtx objectCtx( Expression::ObjectCtx::DOCUMENT_OK); while(fieldIterator.more()) { BSONElement outFieldElement(fieldIterator.next()); string outFieldPath(outFieldElement.fieldName()); string inFieldName(outFieldPath); BSONType specType = outFieldElement.type(); int fieldInclusion = -1; switch(specType) { case NumberDouble: { double inclusion = outFieldElement.numberDouble(); if ((inclusion == 0) || (inclusion == 1)) fieldInclusion = (int)inclusion; else { assert(false); // CW TODO unimplemented constant expression } goto IncludeExclude; } case NumberInt: /* just a plain integer include/exclude specification */ fieldInclusion = outFieldElement.numberInt(); assert((fieldInclusion >= 0) && (fieldInclusion <= 1)); // CW TODO invalid field projection specification IncludeExclude: if (fieldInclusion == 0) pProject->excludePath(outFieldPath); else pProject->includePath(outFieldPath); break; case Bool: /* just a plain boolean include/exclude specification */ fieldInclusion = outFieldElement.Bool() ? 1 : 0; goto IncludeExclude; case String: /* include a field, with rename */ fieldInclusion = 1; inFieldName = outFieldElement.String(); pProject->addField( outFieldPath, ExpressionFieldPath::create( Expression::removeFieldPrefix(inFieldName))); break; case Object: { intrusive_ptr<Expression> pDocument( Expression::parseObject(&outFieldElement, &objectCtx)); /* add The document expression to the projection */ pProject->addField(outFieldPath, pDocument); break; } default: assert(false); // CW TODO invalid field projection specification } } return pProject; }