void WireProtocolWriter::write(
        const StringData& ns,
        const std::vector<WriteOperation*>& write_operations,
        bool ordered,
        const WriteConcern* wc,
        std::vector<BSONObj>* results
    ) {
        bool inRequest = false;
        int opsInRequest = 0;
        Operations requestType;

        BufBuilder builder;

        std::vector<WriteOperation*>::const_iterator iter = write_operations.begin();

        while (iter != write_operations.end()) {
            // We don't have a pending request yet
            if (!inRequest) {
                (*iter)->startRequest(ns.toString(), ordered, &builder);
                inRequest = true;
                requestType = (*iter)->operationType();
            }

            // now we have a pending request, can we add to it?
            if (requestType == (*iter)->operationType() &&
                opsInRequest < _client->getMaxWriteBatchSize()) {

                // We can add to the request, lets see if it will fit and we can batch
                if(_fits(&builder, *iter)) {
                    (*iter)->appendSelfToRequest(&builder);
                    ++opsInRequest;
                    ++iter;

                    if (_batchableRequest(requestType))
                        continue;
                }
            }

            // Send the current request to the server, record the response, start a new request
            results->push_back(_send(requestType, builder, wc, ns));
            inRequest = false;
            opsInRequest = 0;
            builder.reset();
        }

        // Last batch
        if (opsInRequest != 0)
            results->push_back(_send(requestType, builder, wc, ns));
    }
void WireProtocolWriter::write(const StringData& ns,
                               const std::vector<WriteOperation*>& write_operations,
                               bool ordered,
                               bool bypassDocumentValidation,
                               const WriteConcern* writeConcern,
                               WriteResult* writeResult) {
    if (_client->getMaxWireVersion() >= 4) {
        // Per DRIVERS-250:
        // If your driver sends unacknowledged writes using op codes (OP_INSERT, OP_UPDATE,
        // OP_DELETE), you MUST raise an error when bypassDocumentValidation is explicitly set by a
        // user on >= 3.2 servers.
        //
        uassert(0,
                "bypassDocumentValidation is not supported for unacknowledged writes with MongoDB "
                "3.2 and later.",
                !bypassDocumentValidation);
    }

    // Effectively a map of batch relative indexes to WriteOperations
    std::vector<WriteOperation*> batchOps;

    BufBuilder builder;

    std::vector<WriteOperation*>::const_iterator batch_begin = write_operations.begin();
    const std::vector<WriteOperation*>::const_iterator end = write_operations.end();

    while (batch_begin != end) {
        std::vector<WriteOperation*>::const_iterator batch_iter = batch_begin;

        // We must be able to fit the first item of the batch. Otherwise, the calling code
        // passed an over size write operation in violation of our contract.
        invariant(_fits(&builder, *batch_iter));

        // Set the current operation type for this batch
        const WriteOpType batchOpType = (*batch_iter)->operationType();

        // Begin the command for this batch.
        (*batch_iter)->startRequest(ns.toString(), ordered, &builder);

        while (true) {
            // Always safe to append here: either we just entered the loop, or all the
            // below checks passed.
            (*batch_iter)->appendSelfToRequest(&builder);

            // Associate batch index with WriteOperation
            batchOps.push_back(*batch_iter);

            // If the operation we just queued isn't batchable, issue what we have.
            if (!_batchableRequest(batchOpType, writeResult))
                break;

            // Peek at the next operation.
            const std::vector<WriteOperation*>::const_iterator next = boost::next(batch_iter);

            // If we are out of operations, issue what we have.
            if (next == end)
                break;

            // If the next operation is of a different type, issue what we have.
            if ((*next)->operationType() != batchOpType)
                break;

            // If adding the next op would put us over the limit of ops in a batch, issue
            // what we have.
            if (std::distance(batch_begin, next) >= _client->getMaxWriteBatchSize())
                break;

            // If we can't put the next item into the current batch, issue what we have.
            if (!_fits(&builder, *next))
                break;

            // OK to proceed to next op
            batch_iter = next;
        }

        // Issue the complete command.
        BSONObj batchResult = _send(batchOpType, builder, writeConcern, ns);

        // Merge this batch's result into the result for all batches written.
        writeResult->_mergeGleResult(batchOps, batchResult);
        batchOps.clear();

        // Check write result for errors if we are doing ordered processing or last op
        bool lastOp = *batch_iter == write_operations.back();
        if (ordered || lastOp)
            writeResult->_check(lastOp);

        // Reset the builder so we can build the next request.
        builder.reset();

        // The next batch begins with the op after the last one in the just issued batch.
        batch_begin = ++batch_iter;
    }
}
    void CommandWriter::write(
        const StringData& ns,
        const std::vector<WriteOperation*>& write_operations,
        bool ordered,
        const WriteConcern* writeConcern,
        WriteResult* writeResult
    ) {
        // Effectively a map of batch relative indexes to WriteOperations
        std::vector<WriteOperation*> batchOps;

        std::vector<WriteOperation*>::const_iterator batch_begin = write_operations.begin();
        const std::vector<WriteOperation*>::const_iterator end = write_operations.end();

        while (batch_begin != end) {

            boost::scoped_ptr<BSONObjBuilder> command(new BSONObjBuilder);
            boost::scoped_ptr<BSONArrayBuilder> batch(new BSONArrayBuilder);
            std::vector<WriteOperation*>::const_iterator batch_iter = batch_begin;

            // We must be able to fit the first item of the batch. Otherwise, the calling code
            // passed an over size write operation in violation of our contract.
            invariant(_fits(batch.get(), *batch_iter));

            // Set the current operation type
            const WriteOpType batchOpType = (*batch_iter)->operationType();

            // Begin the command for this batch.
            (*batch_iter)->startCommand(ns.toString(), command.get());

            while (true) {

                // Always safe to append here: either we just entered the loop, or all the
                // checks below passed.
                (*batch_iter)->appendSelfToCommand(batch.get());

                // Associate batch index with WriteOperation
                batchOps.push_back(*batch_iter);

                // Peek at the next operation.
                const std::vector<WriteOperation*>::const_iterator next = boost::next(batch_iter);

                // If we are out of operations, issue what we have.
                if (next == end)
                    break;

                // If the next operation is of a different type, issue what we have.
                if ((*next)->operationType() != batchOpType)
                    break;

                // If adding the next op would put us over the limit of ops in a batch, issue
                // what we have.
                if (std::distance(batch_begin, next) >= _client->getMaxWriteBatchSize())
                    break;

                // If we can't put the next item into the current batch, issue what we have.
                if (!_fits(batch.get(), *next))
                    break;

                // OK to proceed to next op.
                batch_iter = next;
            }

            // End the command for this batch.
            _endCommand(batch.get(), *batch_iter, ordered, command.get());

            // Issue the complete command.
            BSONObj batchResult = _send(command.get(), writeConcern, ns);

            // Merge this batch's result into the result for all batches written.
            writeResult->_mergeCommandResult(batchOps, batchResult);
            batchOps.clear();

            // Check write result for errors if we are doing ordered processing or last op
            bool lastOp = *batch_iter == write_operations.back();
            if (ordered || lastOp)
                writeResult->_check(lastOp);

            // The next batch begins with the op after the last one in the just issued batch.
            batch_begin = ++batch_iter;
        }

    }