/** add index keys for a newly inserted record done in two steps/phases to allow potential deferal of write lock portion in the future */ void indexRecordUsingTwoSteps(const char *ns, NamespaceDetails *d, BSONObj obj, DiskLoc loc, bool shouldBeUnlocked) { vector<int> multi; vector<BSONObjSet> multiKeys; IndexInterface::IndexInserter inserter; // Step 1, read phase. int n = d->nIndexesBeingBuilt(); { BSONObjSet keys; for ( int i = 0; i < n; i++ ) { // this call throws on unique constraint violation. we haven't done any writes yet so that is fine. fetchIndexInserters(/*out*/keys, inserter, d, i, obj, loc); if( keys.size() > 1 ) { multi.push_back(i); multiKeys.push_back(BSONObjSet()); multiKeys[multiKeys.size()-1].swap(keys); } keys.clear(); } } inserter.finishAllInsertions(); // Step 2, write phase. // now finish adding multikeys for( unsigned j = 0; j < multi.size(); j++ ) { unsigned i = multi[j]; BSONObjSet& keys = multiKeys[j]; IndexDetails& idx = d->idx(i); IndexInterface& ii = idx.idxInterface(); Ordering ordering = Ordering::make(idx.keyPattern()); d->setIndexIsMultikey(ns, i); for( BSONObjSet::iterator k = ++keys.begin()/*skip 1*/; k != keys.end(); k++ ) { try { ii.bt_insert(idx.head, loc, *k, ordering, !idx.unique(), idx); } catch (AssertionException& e) { if( e.getCode() == 10287 && (int) i == d->nIndexes ) { DEV log() << "info: caught key already in index on bg indexing (ok)" << endl; } else { /* roll back previously added index entries note must do self index as it is multikey and could require some cleanup itself */ for( int j = 0; j < n; j++ ) { try { _unindexRecord(d->idx(j), obj, loc, false); } catch(...) { log(3) << "unindex fails on rollback after unique key constraint prevented insert\n"; } } throw; } } } } }
BSONObjSet PlanExecutor::getOutputSorts() const { if (_qs && _qs->root) { _qs->root->computeProperties(); return _qs->root->getSort(); } if (_root->stageType() == STAGE_MULTI_PLAN) { // If we needed a MultiPlanStage, the PlanExecutor does not own the QuerySolution. We // must go through the MultiPlanStage to access the output sort. auto multiPlanStage = static_cast<MultiPlanStage*>(_root.get()); if (multiPlanStage->bestSolution()) { multiPlanStage->bestSolution()->root->computeProperties(); return multiPlanStage->bestSolution()->root->getSort(); } } else if (_root->stageType() == STAGE_SUBPLAN) { auto subplanStage = static_cast<SubplanStage*>(_root.get()); if (subplanStage->compositeSolution()) { subplanStage->compositeSolution()->root->computeProperties(); return subplanStage->compositeSolution()->root->getSort(); } } return BSONObjSet(); }
BSONObjSet DocumentSourceGroup::getOutputSorts() { if (!_initialized) { initialize(); } if (!(_streaming || _spilled)) { return BSONObjSet(); } BSONObjBuilder sortOrder; if (_idFieldNames.empty()) { if (_spilled) { sortOrder.append("_id", 1); } else { // We have an expression like {_id: "$a"}. Check if this is a FieldPath, and if it is, // get the sort order out of it. if (auto obj = dynamic_cast<ExpressionFieldPath*>(_idExpressions[0].get())) { FieldPath _idSort = obj->getFieldPath(); sortOrder.append( "_id", _inputSort.getIntField(_idSort.getFieldName(_idSort.getPathLength() - 1))); } } } else if (_streaming) { // At this point, we know that _streaming is true, so _id must have only contained // ExpressionObjects, ExpressionConstants or ExpressionFieldPaths. We now process each // '_idExpression'. // We populate 'fieldMap' such that each key is a field the input is sorted by, and the // value is where that input field is located within the _id document. For example, if our // _id object is {_id: {x: {y: "$a.b"}}}, 'fieldMap' would be: {'a.b': '_id.x.y'}. StringMap<std::string> fieldMap; for (size_t i = 0; i < _idFieldNames.size(); i++) { intrusive_ptr<Expression> exp = _idExpressions[i]; if (auto obj = dynamic_cast<ExpressionObject*>(exp.get())) { // _id is an object containing a nested document, such as: {_id: {x: {y: "$b"}}}. getFieldPathMap(obj, "_id." + _idFieldNames[i], &fieldMap); } else if (auto fieldPath = dynamic_cast<ExpressionFieldPath*>(exp.get())) { FieldPath _idSort = fieldPath->getFieldPath(); fieldMap[_idSort.getFieldName(_idSort.getPathLength() - 1)] = "_id." + _idFieldNames[i]; } } // Because the order of '_inputSort' is important, we go through each field we are sorted on // and append it to the BSONObjBuilder in order. for (BSONElement sortField : _inputSort) { std::string sortString = sortField.fieldNameStringData().toString(); auto itr = fieldMap.find(sortString); // If our sort order is (a, b, c), we could not have converted to a streaming $group if // our _id was predicated on (a, c) but not 'b'. Verify that this is true. invariant(itr != fieldMap.end()); sortOrder.append(itr->second, _inputSort.getIntField(sortString)); } } else { // We are blocking and have spilled to disk. std::vector<std::string> outputSort; for (size_t i = 0; i < _idFieldNames.size(); i++) { intrusive_ptr<Expression> exp = _idExpressions[i]; if (auto obj = dynamic_cast<ExpressionObject*>(exp.get())) { // _id is an object containing a nested document, such as: {_id: {x: {y: "$b"}}}. getFieldPathListForSpilled(obj, "_id." + _idFieldNames[i], &outputSort); } else { outputSort.push_back("_id." + _idFieldNames[i]); } } for (auto&& field : outputSort) { sortOrder.append(field, 1); } } return allPrefixes(sortOrder.obj()); }