Skip to content

Commit

Permalink
refactor(NODE-3157)!: update find and modify interfaces for 4.0 (#2799)
Browse files Browse the repository at this point in the history
  • Loading branch information
dariakp committed May 7, 2021
1 parent c3a1839 commit 29512da
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 113 deletions.
22 changes: 12 additions & 10 deletions src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ import {
FindOneAndDeleteOperation,
FindOneAndReplaceOperation,
FindOneAndUpdateOperation,
FindAndModifyOptions
FindOneAndDeleteOptions,
FindOneAndReplaceOptions,
FindOneAndUpdateOptions
} from './operations/find_and_modify';
import {
InsertOneOperation,
Expand Down Expand Up @@ -1096,15 +1098,15 @@ export class Collection {
*/
findOneAndDelete(filter: Document): Promise<Document>;
findOneAndDelete(filter: Document, callback: Callback<Document>): void;
findOneAndDelete(filter: Document, options: FindAndModifyOptions): Promise<Document>;
findOneAndDelete(filter: Document, options: FindOneAndDeleteOptions): Promise<Document>;
findOneAndDelete(
filter: Document,
options: FindAndModifyOptions,
options: FindOneAndDeleteOptions,
callback: Callback<Document>
): void;
findOneAndDelete(
filter: Document,
options?: FindAndModifyOptions | Callback<Document>,
options?: FindOneAndDeleteOptions | Callback<Document>,
callback?: Callback<Document>
): Promise<Document> | void {
if (typeof options === 'function') (callback = options), (options = {});
Expand All @@ -1129,18 +1131,18 @@ export class Collection {
findOneAndReplace(
filter: Document,
replacement: Document,
options: FindAndModifyOptions
options: FindOneAndReplaceOptions
): Promise<Document>;
findOneAndReplace(
filter: Document,
replacement: Document,
options: FindAndModifyOptions,
options: FindOneAndReplaceOptions,
callback: Callback<Document>
): void;
findOneAndReplace(
filter: Document,
replacement: Document,
options?: FindAndModifyOptions | Callback<Document>,
options?: FindOneAndReplaceOptions | Callback<Document>,
callback?: Callback<Document>
): Promise<Document> | void {
if (typeof options === 'function') (callback = options), (options = {});
Expand All @@ -1165,18 +1167,18 @@ export class Collection {
findOneAndUpdate(
filter: Document,
update: Document,
options: FindAndModifyOptions
options: FindOneAndUpdateOptions
): Promise<Document>;
findOneAndUpdate(
filter: Document,
update: Document,
options: FindAndModifyOptions,
options: FindOneAndUpdateOptions,
callback: Callback<Document>
): void;
findOneAndUpdate(
filter: Document,
update: Document,
options?: FindAndModifyOptions | Callback<Document>,
options?: FindOneAndUpdateOptions | Callback<Document>,
callback?: Callback<Document>
): Promise<Document> | void {
if (typeof options === 'function') (callback = options), (options = {});
Expand Down
8 changes: 6 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,12 @@ export type { DropCollectionOptions, DropDatabaseOptions } from './operations/dr
export type { EstimatedDocumentCountOptions } from './operations/estimated_document_count';
export type { EvalOptions } from './operations/eval';
export type { FindOptions } from './operations/find';
export type { Sort, SortDirection } from './sort';
export type { FindAndModifyOptions } from './operations/find_and_modify';
export type { Sort, SortDirection, SortDirectionForCmd, SortForCmd } from './sort';
export type {
FindOneAndDeleteOptions,
FindOneAndReplaceOptions,
FindOneAndUpdateOptions
} from './operations/find_and_modify';
export type {
IndexSpecification,
CreateIndexesOptions,
Expand Down
206 changes: 107 additions & 99 deletions src/operations/find_and_modify.ts
Original file line number Diff line number Diff line change
@@ -1,127 +1,146 @@
import { ReadPreference } from '../read_preference';
import {
maxWireVersion,
applyRetryableWrites,
decorateWithCollation,
hasAtomicOperators,
Callback
} from '../utils';
import { maxWireVersion, decorateWithCollation, hasAtomicOperators, Callback } from '../utils';
import { MongoError } from '../error';
import { CommandOperation, CommandOperationOptions } from './command';
import { defineAspects, Aspect } from './operation';
import type { Document } from '../bson';
import type { Server } from '../sdam/server';
import type { Collection } from '../collection';
import { Sort, formatSort } from '../sort';
import { Sort, SortForCmd, formatSort } from '../sort';
import type { ClientSession } from '../sessions';
import type { WriteConcern, WriteConcernSettings } from '../write_concern';

/** @public */
export interface FindAndModifyOptions extends CommandOperationOptions {
/** When false, returns the updated document rather than the original. The default is true. */
returnOriginal?: boolean;
/** Upsert the document if it does not exist. */
upsert?: boolean;
export interface FindOneAndDeleteOptions extends CommandOperationOptions {
/** An optional hint for query optimization. See the {@link https://docs.mongodb.com/manual/reference/command/update/#update-command-hint|update command} reference for more information.*/
hint?: Document;
/** Limits the fields to return for all matching documents. */
projection?: Document;
/** @deprecated use `projection` instead */
fields?: Document;
/** Determines which document the operation modifies if the query selects multiple documents. */
sort?: Sort;
}

/** @public */
export interface FindOneAndReplaceOptions extends CommandOperationOptions {
/** Allow driver to bypass schema validation in MongoDB 3.2 or higher. */
bypassDocumentValidation?: boolean;
/** An optional hint for query optimization. See the {@link https://docs.mongodb.com/manual/reference/command/update/#update-command-hint|update command} reference for more information.*/
hint?: Document;
/** Limits the fields to return for all matching documents. */
projection?: Document;
/** When false, returns the updated document rather than the original. The default is true. */
returnOriginal?: boolean;
/** Determines which document the operation modifies if the query selects multiple documents. */
sort?: Sort;
/** Upsert the document if it does not exist. */
upsert?: boolean;
}

/** @public */
export interface FindOneAndUpdateOptions extends CommandOperationOptions {
/** Optional list of array filters referenced in filtered positional operators */
arrayFilters?: Document[];
/** Allow driver to bypass schema validation in MongoDB 3.2 or higher. */
bypassDocumentValidation?: boolean;
/** An optional hint for query optimization. See the {@link https://docs.mongodb.com/manual/reference/command/update/#update-command-hint|update command} reference for more information.*/
hint?: Document;
/** Limits the fields to return for all matching documents. */
projection?: Document;
/** When false, returns the updated document rather than the original. The default is true. */
returnOriginal?: boolean;
/** Determines which document the operation modifies if the query selects multiple documents. */
sort?: Sort;
/** Upsert the document if it does not exist. */
upsert?: boolean;
}

// TODO: NODE-1812 to deprecate returnOriginal for returnDocument

/** @internal */
interface FindAndModifyCmdBase {
remove: boolean;
new: boolean;
upsert: boolean;
update?: Document;
sort?: SortForCmd;
fields?: Document;
bypassDocumentValidation?: boolean;
arrayFilters?: Document[];
maxTimeMS?: number;
writeConcern?: WriteConcern | WriteConcernSettings;
}

// NOTE: These types are a misuse of options, can we think of a way to remove them?
update?: boolean;
remove?: boolean;
new?: boolean;
function configureFindAndModifyCmdBaseUpdateOpts(
cmdBase: FindAndModifyCmdBase,
options: FindOneAndReplaceOptions | FindOneAndUpdateOptions
): FindAndModifyCmdBase {
cmdBase.new = options.returnOriginal === false;
cmdBase.upsert = options.upsert === true;

if (options.bypassDocumentValidation === true) {
cmdBase.bypassDocumentValidation = options.bypassDocumentValidation;
}
return cmdBase;
}

/** @internal */
export class FindAndModifyOperation extends CommandOperation<Document> {
options: FindAndModifyOptions;
class FindAndModifyOperation extends CommandOperation<Document> {
options: FindOneAndReplaceOptions | FindOneAndUpdateOptions | FindOneAndDeleteOptions;
cmdBase: FindAndModifyCmdBase;
collection: Collection;
query: Document;
sort?: Sort;
doc?: Document;

constructor(
collection: Collection,
query: Document,
sort: Sort | undefined,
doc: Document | undefined,
options?: FindAndModifyOptions
options: FindOneAndReplaceOptions | FindOneAndUpdateOptions | FindOneAndDeleteOptions
) {
super(collection, options);
this.options = options ?? {};
this.cmdBase = {
remove: false,
new: false,
upsert: false
};

const sort = formatSort(options.sort);
if (sort) {
this.cmdBase.sort = sort;
}

if (options.projection) {
this.cmdBase.fields = options.projection;
}

if (options.maxTimeMS) {
this.cmdBase.maxTimeMS = options.maxTimeMS;
}

// Decorate the findAndModify command with the write Concern
if (options.writeConcern) {
this.cmdBase.writeConcern = options.writeConcern;
}

// force primary read preference
this.readPreference = ReadPreference.primary;

this.collection = collection;
this.query = query;
this.sort = sort;
this.doc = doc;
}

execute(server: Server, session: ClientSession, callback: Callback<Document>): void {
const coll = this.collection;
const query = this.query;
const sort = formatSort(this.sort);
const doc = this.doc;
let options = { ...this.options, ...this.bsonOptions };
const options = { ...this.options, ...this.bsonOptions };

// Create findAndModify command object
const cmd: Document = {
findAndModify: coll.collectionName,
query: query
query: query,
...this.cmdBase
};

if (sort) {
cmd.sort = sort;
}

cmd.new = options.new ? true : false;
cmd.remove = options.remove ? true : false;
cmd.upsert = options.upsert ? true : false;

const projection = options.projection || options.fields;

if (projection) {
cmd.fields = projection;
}

if (options.arrayFilters) {
cmd.arrayFilters = options.arrayFilters;
}

if (doc && !options.remove) {
cmd.update = doc;
}

if (options.maxTimeMS) {
cmd.maxTimeMS = options.maxTimeMS;
}

// No check on the documents
options.checkKeys = false;

// Final options for retryable writes
options = applyRetryableWrites(options, coll.s.db);

// Decorate the findAndModify command with the write Concern
if (options.writeConcern) {
cmd.writeConcern = options.writeConcern;
}

// Have we specified bypassDocumentValidation
if (options.bypassDocumentValidation === true) {
cmd.bypassDocumentValidation = options.bypassDocumentValidation;
}

// Have we specified collation
try {
decorateWithCollation(cmd, coll, options);
Expand Down Expand Up @@ -159,18 +178,14 @@ export class FindAndModifyOperation extends CommandOperation<Document> {

/** @internal */
export class FindOneAndDeleteOperation extends FindAndModifyOperation {
constructor(collection: Collection, filter: Document, options: FindAndModifyOptions) {
// Final options
const finalOptions = Object.assign({}, options);
finalOptions.fields = options.projection;
finalOptions.remove = true;

constructor(collection: Collection, filter: Document, options: FindOneAndDeleteOptions) {
// Basic validation
if (filter == null || typeof filter !== 'object') {
throw new TypeError('Filter parameter must be an object');
}

super(collection, filter, finalOptions.sort, undefined, finalOptions);
super(collection, filter, options);
this.cmdBase.remove = true;
}
}

Expand All @@ -180,15 +195,8 @@ export class FindOneAndReplaceOperation extends FindAndModifyOperation {
collection: Collection,
filter: Document,
replacement: Document,
options: FindAndModifyOptions
options: FindOneAndReplaceOptions
) {
// Final options
const finalOptions = Object.assign({}, options);
finalOptions.fields = options.projection;
finalOptions.update = true;
finalOptions.new = options.returnOriginal !== void 0 ? !options.returnOriginal : false;
finalOptions.upsert = options.upsert !== void 0 ? !!options.upsert : false;

if (filter == null || typeof filter !== 'object') {
throw new TypeError('Filter parameter must be an object');
}
Expand All @@ -201,7 +209,9 @@ export class FindOneAndReplaceOperation extends FindAndModifyOperation {
throw new TypeError('Replacement document must not contain atomic operators');
}

super(collection, filter, finalOptions.sort, replacement, finalOptions);
super(collection, filter, options);
this.cmdBase.update = replacement;
configureFindAndModifyCmdBaseUpdateOpts(this.cmdBase, options);
}
}

Expand All @@ -211,16 +221,8 @@ export class FindOneAndUpdateOperation extends FindAndModifyOperation {
collection: Collection,
filter: Document,
update: Document,
options: FindAndModifyOptions
options: FindOneAndUpdateOptions
) {
// Final options
const finalOptions = Object.assign({}, options);
finalOptions.fields = options.projection;
finalOptions.update = true;
finalOptions.new =
typeof options.returnOriginal === 'boolean' ? !options.returnOriginal : false;
finalOptions.upsert = typeof options.upsert === 'boolean' ? options.upsert : false;

if (filter == null || typeof filter !== 'object') {
throw new TypeError('Filter parameter must be an object');
}
Expand All @@ -233,7 +235,13 @@ export class FindOneAndUpdateOperation extends FindAndModifyOperation {
throw new TypeError('Update document requires atomic operators');
}

super(collection, filter, finalOptions.sort, update, finalOptions);
super(collection, filter, options);
this.cmdBase.update = update;
configureFindAndModifyCmdBaseUpdateOpts(this.cmdBase, options);

if (options.arrayFilters) {
this.cmdBase.arrayFilters = options.arrayFilters;
}
}
}

Expand Down
Loading

0 comments on commit 29512da

Please sign in to comment.