Status ModifierInc::prepare(mutablebson::Element root, StringData matchedField, ExecInfo* execInfo) { _preparedState.reset(new PreparedState(root.getDocument())); // If we have a $-positional field, it is time to bind it to an actual field part. if (_posDollar) { if (matchedField.empty()) { return Status(ErrorCodes::BadValue, str::stream() << "The positional operator did not find the match " "needed from the query. Unexpanded update: " << _fieldRef.dottedField()); } _fieldRef.setPart(_posDollar, matchedField); } // Locate the field name in 'root'. Note that we may not have all the parts in the path // in the doc -- which is fine. Our goal now is merely to reason about whether this mod // apply is a noOp or whether is can be in place. The remaining path, if missing, will // be created during the apply. Status status = pathsupport::findLongestPrefix(_fieldRef, root, &_preparedState->idxFound, &_preparedState->elemFound); // FindLongestPrefix may say the path does not exist at all, which is fine here, or // that the path was not viable or otherwise wrong, in which case, the mod cannot // proceed. if (status.code() == ErrorCodes::NonExistentPath) { _preparedState->elemFound = root.getDocument().end(); } else if (!status.isOK()) { return status; } // We register interest in the field name. The driver needs this info to sort out if // there is any conflict among mods. execInfo->fieldRef[0] = &_fieldRef; // Capture the value we are going to write. At this point, there may not be a value // against which to operate, so the result will be simply _val. _preparedState->newValue = _val; // // in-place and no-op logic // // If the field path is not fully present, then this mod cannot be in place, nor is a // noOp. if (!_preparedState->elemFound.ok() || _preparedState->idxFound < (_fieldRef.numParts() - 1)) { // For multiplication, we treat ops against missing as yielding zero. We take // advantage here of the promotion rules for SafeNum; the expression below will // always yield a zero of the same type of operand that the user provided // (e.g. double). if (_mode == MODE_MUL) _preparedState->newValue *= SafeNum(static_cast<int>(0)); return Status::OK(); } // If the value being $inc'ed is the same as the one already in the doc, than this is a // noOp. if (!_preparedState->elemFound.isNumeric()) { mb::Element idElem = mb::findFirstChildNamed(root, "_id"); return Status( ErrorCodes::TypeMismatch, str::stream() << "Cannot apply " << (_mode == MODE_INC ? "$inc" : "$mul") << " to a value of non-numeric type. {" << idElem.toString() << "} has the field '" << _preparedState->elemFound.getFieldName() << "' of non-numeric type " << typeName(_preparedState->elemFound.getType())); } const SafeNum currentValue = _preparedState->elemFound.getValueSafeNum(); // Update newValue w.r.t to the current value of the found element. if (_mode == MODE_INC) _preparedState->newValue += currentValue; else _preparedState->newValue *= currentValue; // If the result of the addition is invalid, we must return an error. if (!_preparedState->newValue.isValid()) { mb::Element idElem = mb::findFirstChildNamed(root, "_id"); return Status(ErrorCodes::BadValue, str::stream() << "Failed to apply $inc operations to current value (" << currentValue.debugString() << ") for document {" << idElem.toString() << "}"); } // If the values are identical (same type, same value), then this is a no-op. if (_preparedState->newValue.isIdentical(currentValue)) { _preparedState->noOp = execInfo->noOp = true; return Status::OK(); } return Status::OK(); }