Document::Document(BSONObj *pBsonObj): vFieldName(), vpValue() { BSONObjIterator bsonIterator(pBsonObj->begin()); while(bsonIterator.more()) { BSONElement bsonElement(bsonIterator.next()); string fieldName(bsonElement.fieldName()); intrusive_ptr<const Value> pValue( Value::createFromBsonElement(&bsonElement)); vFieldName.push_back(fieldName); vpValue.push_back(pValue); } }
intrusive_ptr<Pipeline> Pipeline::parseCommand( string &errmsg, BSONObj &cmdObj, const intrusive_ptr<ExpressionContext> &pCtx) { intrusive_ptr<Pipeline> pPipeline(new Pipeline(pCtx)); vector<BSONElement> pipeline; /* gather the specification for the aggregation */ for(BSONObj::iterator cmdIterator = cmdObj.begin(); cmdIterator.more(); ) { BSONElement cmdElement(cmdIterator.next()); const char *pFieldName = cmdElement.fieldName(); /* look for the aggregation command */ if (!strcmp(pFieldName, commandName)) { pPipeline->collectionName = cmdElement.String(); continue; } /* check for the collection name */ if (!strcmp(pFieldName, pipelineName)) { pipeline = cmdElement.Array(); continue; } /* check for explain option */ if (!strcmp(pFieldName, explainName)) { pPipeline->explain = cmdElement.Bool(); continue; } /* if the request came from the router, we're in a shard */ if (!strcmp(pFieldName, fromRouterName)) { pCtx->setInShard(cmdElement.Bool()); continue; } /* check for debug options */ if (!strcmp(pFieldName, splitMongodPipelineName)) { pPipeline->splitMongodPipeline = true; continue; } /* we didn't recognize a field in the command */ ostringstream sb; sb << "Pipeline::parseCommand(): unrecognized field \"" << cmdElement.fieldName(); errmsg = sb.str(); return intrusive_ptr<Pipeline>(); } /* If we get here, we've harvested the fields we expect for a pipeline. Set up the specified document source pipeline. */ SourceVector *pSourceVector = &pPipeline->sourceVector; // shorthand /* iterate over the steps in the pipeline */ const size_t nSteps = pipeline.size(); for(size_t iStep = 0; iStep < nSteps; ++iStep) { /* pull out the pipeline element as an object */ BSONElement pipeElement(pipeline[iStep]); uassert(15942, str::stream() << "pipeline element " << iStep << " is not an object", pipeElement.type() == Object); BSONObj bsonObj(pipeElement.Obj()); intrusive_ptr<DocumentSource> pSource; /* use the object to add a DocumentSource to the processing chain */ BSONObjIterator bsonIterator(bsonObj); while(bsonIterator.more()) { BSONElement bsonElement(bsonIterator.next()); const char *pFieldName = bsonElement.fieldName(); /* select the appropriate operation and instantiate */ StageDesc key; key.pName = pFieldName; const StageDesc *pDesc = (const StageDesc *) bsearch(&key, stageDesc, nStageDesc, sizeof(StageDesc), stageDescCmp); if (pDesc) { pSource = (*pDesc->pFactory)(&bsonElement, pCtx); pSource->setPipelineStep(iStep); } else { ostringstream sb; sb << "Pipeline::run(): unrecognized pipeline op \"" << pFieldName; errmsg = sb.str(); return intrusive_ptr<Pipeline>(); } } pSourceVector->push_back(pSource); } /* if there aren't any pipeline stages, there's nothing more to do */ if (!pSourceVector->size()) return pPipeline; /* Move filters up where possible. CW TODO -- move filter past projections where possible, and noting corresponding field renaming. */ /* Wherever there is a match immediately following a sort, swap them. This means we sort fewer items. Neither changes the documents in the stream, so this transformation shouldn't affect the result. We do this first, because then when we coalesce operators below, any adjacent matches will be combined. */ for(size_t srcn = pSourceVector->size(), srci = 1; srci < srcn; ++srci) { intrusive_ptr<DocumentSource> &pSource = pSourceVector->at(srci); if (dynamic_cast<DocumentSourceMatch *>(pSource.get())) { intrusive_ptr<DocumentSource> &pPrevious = pSourceVector->at(srci - 1); if (dynamic_cast<DocumentSourceSort *>(pPrevious.get())) { /* swap this item with the previous */ intrusive_ptr<DocumentSource> pTemp(pPrevious); pPrevious = pSource; pSource = pTemp; } } } /* Coalesce adjacent filters where possible. Two adjacent filters are equivalent to one filter whose predicate is the conjunction of the two original filters' predicates. For now, capture this by giving any DocumentSource the option to absorb it's successor; this will also allow adjacent projections to coalesce when possible. Run through the DocumentSources, and give each one the opportunity to coalesce with its successor. If successful, remove the successor. Move all document sources to a temporary list. */ SourceVector tempVector(*pSourceVector); pSourceVector->clear(); /* move the first one to the final list */ pSourceVector->push_back(tempVector[0]); /* run through the sources, coalescing them or keeping them */ for(size_t tempn = tempVector.size(), tempi = 1; tempi < tempn; ++tempi) { /* If we can't coalesce the source with the last, then move it to the final list, and make it the new last. (If we succeeded, then we're still on the same last, and there's no need to move or do anything with the source -- the destruction of tempVector will take care of the rest.) */ intrusive_ptr<DocumentSource> &pLastSource = pSourceVector->back(); intrusive_ptr<DocumentSource> &pTemp = tempVector.at(tempi); if (!pLastSource->coalesce(pTemp)) pSourceVector->push_back(pTemp); } /* optimize the elements in the pipeline */ for(SourceVector::iterator iter(pSourceVector->begin()), listEnd(pSourceVector->end()); iter != listEnd; ++iter) (*iter)->optimize(); return pPipeline; }