All files / src/operations insert.ts

98% Statements 49/50
90% Branches 18/20
100% Functions 9/9
100% Lines 49/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167      408x         408x 408x 408x 408x 408x     408x         1276979x 1276979x 1276979x 1276979x       1287441x               1286419x 1286419x 1286419x           1286419x 1053x         1286419x 2406x     1286419x                                       408x   870688x               876303x 856512x 856512x   3709x     852803x                                   408x           381719x   381719x 229x     381490x 381490x 381490x       381719x               381261x 381261x 381261x 381261x   27672507x           381261x 381261x 374541x           6720x 229x       6491x         408x 408x 408x  
import type { Document } from '../bson';
import type { BulkWriteOptions } from '../bulk/common';
import type { Collection } from '../collection';
import { MongoInvalidArgumentError, MongoServerError } from '../error';
import type { InferIdType } from '../mongo_types';
import type { Server } from '../sdam/server';
import type { ClientSession } from '../sessions';
import { type TimeoutContext } from '../timeout';
import { maybeAddIdToDocuments, type MongoDBNamespace } from '../utils';
import { WriteConcern } from '../write_concern';
import { BulkWriteOperation } from './bulk_write';
import { CommandOperation, type CommandOperationOptions } from './command';
import { AbstractOperation, Aspect, defineAspects } from './operation';
 
/** @internal */
export class InsertOperation extends CommandOperation<Document> {
  override options: BulkWriteOptions;
  documents: Document[];
 
  constructor(ns: MongoDBNamespace, documents: Document[], options: BulkWriteOptions) {
    super(undefined, options);
    this.options = { ...options, checkKeys: options.checkKeys ?? false };
    this.ns = ns;
    this.documents = documents;
  }
 
  override get commandName() {
    return 'insert' as const;
  }
 
  override async execute(
    server: Server,
    session: ClientSession | undefined,
    timeoutContext: TimeoutContext
  ): Promise<Document> {
    const options = this.options ?? {};
    const ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
    const command: Document = {
      insert: this.ns.collection,
      documents: this.documents,
      ordered
    };
 
    if (typeof options.bypassDocumentValidation === 'boolean') {
      command.bypassDocumentValidation = options.bypassDocumentValidation;
    }
 
    // we check for undefined specifically here to allow falsy values
    // eslint-disable-next-line no-restricted-syntax
    if (options.comment !== undefined) {
      command.comment = options.comment;
    }
 
    return await super.executeCommand(server, session, command, timeoutContext);
  }
}
 
/** @public */
export interface InsertOneOptions extends CommandOperationOptions {
  /** Allow driver to bypass schema validation. */
  bypassDocumentValidation?: boolean;
  /** Force server to assign _id values instead of driver. */
  forceServerObjectId?: boolean;
}
 
/** @public */
export interface InsertOneResult<TSchema = Document> {
  /** Indicates whether this write result was acknowledged. If not, then all other members of this result will be undefined */
  acknowledged: boolean;
  /** The identifier that was inserted. If the server generated the identifier, this value will be null as the driver does not have access to that data */
  insertedId: InferIdType<TSchema>;
}
 
export class InsertOneOperation extends InsertOperation {
  constructor(collection: Collection, doc: Document, options: InsertOneOptions) {
    super(collection.s.namespace, maybeAddIdToDocuments(collection, [doc], options), options);
  }
 
  override async execute(
    server: Server,
    session: ClientSession | undefined,
    timeoutContext: TimeoutContext
  ): Promise<InsertOneResult> {
    const res = await super.execute(server, session, timeoutContext);
    Iif (res.code) throw new MongoServerError(res);
    if (res.writeErrors) {
      // This should be a WriteError but we can't change it now because of error hierarchy
      throw new MongoServerError(res.writeErrors[0]);
    }
 
    return {
      acknowledged: this.writeConcern?.w !== 0,
      insertedId: this.documents[0]._id
    };
  }
}
 
/** @public */
export interface InsertManyResult<TSchema = Document> {
  /** Indicates whether this write result was acknowledged. If not, then all other members of this result will be undefined */
  acknowledged: boolean;
  /** The number of inserted documents for this operations */
  insertedCount: number;
  /** Map of the index of the inserted document to the id of the inserted document */
  insertedIds: { [key: number]: InferIdType<TSchema> };
}
 
/** @internal */
export class InsertManyOperation extends AbstractOperation<InsertManyResult> {
  override options: BulkWriteOptions;
  collection: Collection;
  docs: ReadonlyArray<Document>;
 
  constructor(collection: Collection, docs: ReadonlyArray<Document>, options: BulkWriteOptions) {
    super(options);
 
    if (!Array.isArray(docs)) {
      throw new MongoInvalidArgumentError('Argument "docs" must be an array of documents');
    }
 
    this.options = options;
    this.collection = collection;
    this.docs = docs;
  }
 
  override get commandName() {
    return 'insert' as const;
  }
 
  override async execute(
    server: Server,
    session: ClientSession | undefined,
    timeoutContext: TimeoutContext
  ): Promise<InsertManyResult> {
    const coll = this.collection;
    const options = { ...this.options, ...this.bsonOptions, readPreference: this.readPreference };
    const writeConcern = WriteConcern.fromOptions(options);
    const bulkWriteOperation = new BulkWriteOperation(
      coll,
      this.docs.map(document => ({
        insertOne: { document }
      })),
      options
    );
 
    try {
      const res = await bulkWriteOperation.execute(server, session, timeoutContext);
      return {
        acknowledged: writeConcern?.w !== 0,
        insertedCount: res.insertedCount,
        insertedIds: res.insertedIds
      };
    } catch (err) {
      if (err && err.message === 'Operation must be an object with an operation key') {
        throw new MongoInvalidArgumentError(
          'Collection.insertMany() cannot be called with an array that has null/undefined values'
        );
      }
      throw err;
    }
  }
}
 
defineAspects(InsertOperation, [Aspect.RETRYABLE, Aspect.WRITE_OPERATION]);
defineAspects(InsertOneOperation, [Aspect.RETRYABLE, Aspect.WRITE_OPERATION]);
defineAspects(InsertManyOperation, [Aspect.WRITE_OPERATION]);