/** * Copyright 2011 (c) 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "mongo/pch.h" #include "mongo/db/jsobj.h" #include "mongo/db/pipeline/document.h" #include "mongo/db/pipeline/document_source.h" #include "mongo/db/pipeline/expression.h" #include "mongo/db/pipeline/value.h" namespace mongo { const char DocumentSourceProject::projectName[] = "$project"; DocumentSourceProject::~DocumentSourceProject() { } DocumentSourceProject::DocumentSourceProject(const intrusive_ptr& pExpCtx, const intrusive_ptr& exprObj) : DocumentSource(pExpCtx) , pEO(exprObj) { } const char *DocumentSourceProject::getSourceName() const { return projectName; } bool DocumentSourceProject::eof() { return pSource->eof(); } bool DocumentSourceProject::advance() { DocumentSource::advance(); // check for interrupts return pSource->advance(); } Document DocumentSourceProject::getCurrent() { Document pInDocument(pSource->getCurrent()); /* create the result document */ const size_t sizeHint = pEO->getSizeHint(); MutableDocument out (sizeHint); /* Use the ExpressionObject to create the base result. If we're excluding fields at the top level, leave out the _id if it is found, because we took care of it above. */ pEO->addToDocument(out, pInDocument, Variables(pInDocument)); #if defined(_DEBUG) if (!_simpleProjection.getSpec().isEmpty()) { // Make sure we return the same results as Projection class BSONObjBuilder inputBuilder; pSource->getCurrent()->toBson(&inputBuilder); BSONObj input = inputBuilder.done(); BSONObjBuilder outputBuilder; out.peek().toBson(&outputBuilder); BSONObj output = outputBuilder.done(); BSONObj projected = _simpleProjection.transform(input); if (projected != output) { log() << "$project applied incorrectly: " << getRaw() << endl; log() << "input: " << input << endl; log() << "out: " << output << endl; log() << "projected: " << projected << endl; verify(false); // exits in _DEBUG builds } } #endif return out.freeze(); } void DocumentSourceProject::optimize() { intrusive_ptr pE(pEO->optimize()); pEO = dynamic_pointer_cast(pE); } void DocumentSourceProject::sourceToBson(BSONObjBuilder* pBuilder, bool explain) const { *pBuilder << projectName << pEO->serialize(); } intrusive_ptr DocumentSourceProject::createFromBson( BSONElement *pBsonElement, const intrusive_ptr &pExpCtx) { /* validate */ uassert(15969, str::stream() << projectName << " specification must be an object", pBsonElement->type() == Object); Expression::ObjectCtx objectCtx( Expression::ObjectCtx::DOCUMENT_OK | Expression::ObjectCtx::TOP_LEVEL | Expression::ObjectCtx::INCLUSION_OK ); intrusive_ptr parsed = Expression::parseObject(pBsonElement, &objectCtx); ExpressionObject* exprObj = dynamic_cast(parsed.get()); massert(16402, "parseObject() returned wrong type of Expression", exprObj); uassert(16403, "$projection requires at least one output field", exprObj->getFieldCount()); intrusive_ptr pProject(new DocumentSourceProject(pExpCtx, exprObj)); BSONObj projectObj = pBsonElement->Obj(); pProject->_raw = projectObj.getOwned(); // probably not necessary, but better to be safe #if defined(_DEBUG) if (exprObj->isSimple()) { set deps; vector path; exprObj->addDependencies(deps, &path); pProject->_simpleProjection.init(depsToProjection(deps)); } #endif return pProject; } DocumentSource::GetDepsReturn DocumentSourceProject::getDependencies(set& deps) const { vector path; // empty == top-level pEO->addDependencies(deps, &path); return EXHAUSTIVE; } }