Skip to content

Commit 0135e9e

Browse files
authored
refactor!: conform CRUD result types to specification (#2651)
Our CRUD result types have been out-of-sync with the official drivers CRUD specification for some time. This patch brings them in line, and updates our tests accordingly NODE-2936
1 parent d811a01 commit 0135e9e

23 files changed

+406
-600
lines changed

src/bulk/common.ts

+2
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ export class Batch {
167167
export class BulkWriteResult {
168168
result: BulkResult;
169169

170+
/** Indicates whether this write result was acknowledged. If not, then all other members of this result will be undefined */
171+
// acknowledged: Boolean;
170172
/** Number of documents inserted. */
171173
insertedCount: number;
172174
/** Number of documents matched for update. */

src/gridfs-stream/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,11 @@ function _rename(
239239
if (error) {
240240
return callback(error);
241241
}
242-
if (!res?.result.n) {
242+
243+
if (!res?.matchedCount) {
243244
return callback(new MongoError(`File with id ${id} not found`));
244245
}
246+
245247
return callback();
246248
});
247249
}

src/operations/common_functions.ts

-84
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import type { ReadPreference } from '../read_preference';
1414
import type { Collection } from '../collection';
1515
import type { UpdateOptions } from './update';
1616
import type { WriteCommandOptions } from '../cmap/wire_protocol/write_command';
17-
import type { DeleteOptions } from './delete';
1817

1918
/** @public */
2019
export interface IndexInformationOptions {
@@ -104,89 +103,6 @@ export function prepareDocs(
104103
});
105104
}
106105

107-
export function removeDocuments(server: Server, coll: Collection, callback?: Callback): void;
108-
export function removeDocuments(
109-
server: Server,
110-
coll: Collection,
111-
selector?: Document,
112-
callback?: Callback
113-
): void;
114-
export function removeDocuments(
115-
server: Server,
116-
coll: Collection,
117-
selector?: Document,
118-
options?: DeleteOptions,
119-
callback?: Callback
120-
): void;
121-
export function removeDocuments(
122-
server: Server,
123-
coll: Collection,
124-
selector?: Document,
125-
options?: DeleteOptions | Document,
126-
callback?: Callback
127-
): void {
128-
if (typeof options === 'function') {
129-
(callback = options as Callback), (options = {});
130-
} else if (typeof selector === 'function') {
131-
callback = selector as Callback;
132-
options = {};
133-
selector = {};
134-
}
135-
136-
// Create an empty options object if the provided one is null
137-
options = options || {};
138-
139-
// Final options for retryable writes
140-
let finalOptions = Object.assign({}, options);
141-
finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
142-
143-
// If selector is null set empty
144-
if (selector == null) selector = {};
145-
146-
// Build the op
147-
const op = { q: selector, limit: 0 } as any;
148-
if (options.single) {
149-
op.limit = 1;
150-
} else if (finalOptions.retryWrites) {
151-
finalOptions.retryWrites = false;
152-
}
153-
if (options.hint) {
154-
op.hint = options.hint;
155-
}
156-
157-
// Have we specified collation
158-
try {
159-
decorateWithCollation(finalOptions, coll, options);
160-
} catch (err) {
161-
return callback ? callback(err, null) : undefined;
162-
}
163-
164-
if (options.explain !== undefined && maxWireVersion(server) < 3) {
165-
return callback
166-
? callback(new MongoError(`server ${server.name} does not support explain on remove`))
167-
: undefined;
168-
}
169-
170-
// Execute the remove
171-
server.remove(
172-
coll.s.namespace.toString(),
173-
[op],
174-
finalOptions as WriteCommandOptions,
175-
(err, result) => {
176-
if (callback == null) return;
177-
if (err) return callback(err);
178-
if (result == null) return callback();
179-
if (result.code) return callback(new MongoError(result));
180-
if (result.writeErrors) {
181-
return callback(new MongoError(result.writeErrors[0]));
182-
}
183-
184-
// Return the results
185-
callback(undefined, result);
186-
}
187-
);
188-
}
189-
190106
export function updateDocuments(
191107
server: Server,
192108
coll: Collection,

src/operations/delete.ts

+90-26
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { defineAspects, Aspect, OperationBase, Hint } from './operation';
2-
import { removeDocuments } from './common_functions';
32
import { CommandOperation, CommandOperationOptions } from './command';
43
import { isObject } from 'util';
5-
import type { Callback, MongoDBNamespace } from '../utils';
4+
import {
5+
applyRetryableWrites,
6+
Callback,
7+
decorateWithCollation,
8+
maxWireVersion,
9+
MongoDBNamespace
10+
} from '../utils';
611
import type { Document } from '../bson';
712
import type { Server } from '../sdam/server';
813
import type { Collection } from '../collection';
914
import type { WriteCommandOptions } from '../cmap/wire_protocol/write_command';
10-
import type { Connection } from '../cmap/connection';
15+
import { MongoError } from '../error';
1116

1217
/** @public */
1318
export interface DeleteOptions extends CommandOperationOptions {
@@ -17,14 +22,10 @@ export interface DeleteOptions extends CommandOperationOptions {
1722

1823
/** @public */
1924
export interface DeleteResult {
20-
/** Indicates whether this write result was acknowledged */
25+
/** Indicates whether this write result was acknowledged. If not, then all other members of this result will be undefined. */
2126
acknowledged: boolean;
2227
/** The number of documents that were deleted */
2328
deletedCount: number;
24-
/** The raw result returned from MongoDB. Will vary depending on server version */
25-
result: Document;
26-
/** The connection object used for the operation */
27-
connection?: Connection;
2829
}
2930

3031
/** @internal */
@@ -68,15 +69,13 @@ export class DeleteOneOperation extends CommandOperation<DeleteOptions, DeleteRe
6869
const options = { ...this.options, ...this.bsonOptions };
6970

7071
options.single = true;
71-
removeDocuments(server, coll, filter, options, (err, r) => {
72-
if (callback == null) return;
73-
if (err && callback) return callback(err);
74-
if (r == null) {
75-
return callback(undefined, { acknowledged: true, deletedCount: 0, result: { ok: 1 } });
76-
}
77-
78-
r.deletedCount = r.n;
79-
if (callback) callback(undefined, r);
72+
removeDocuments(server, coll, filter, options, (err, res) => {
73+
if (err || res == null) return callback(err);
74+
if (typeof options.explain !== 'undefined') return callback(undefined, res);
75+
callback(undefined, {
76+
acknowledged: this.writeConcern?.w !== 0 ?? true,
77+
deletedCount: res.n
78+
});
8079
});
8180
}
8281
}
@@ -106,19 +105,84 @@ export class DeleteManyOperation extends CommandOperation<DeleteOptions, DeleteR
106105
options.single = false;
107106
}
108107

109-
removeDocuments(server, coll, filter, options, (err, r) => {
110-
if (callback == null) return;
111-
if (err && callback) return callback(err);
112-
if (r == null) {
113-
return callback(undefined, { acknowledged: true, deletedCount: 0, result: { ok: 1 } });
114-
}
115-
116-
r.deletedCount = r.n;
117-
if (callback) callback(undefined, r);
108+
removeDocuments(server, coll, filter, options, (err, res) => {
109+
if (err || res == null) return callback(err);
110+
if (typeof options.explain !== 'undefined') return callback(undefined, res);
111+
callback(undefined, {
112+
acknowledged: this.writeConcern?.w !== 0 ?? true,
113+
deletedCount: res.n
114+
});
118115
});
119116
}
120117
}
121118

119+
function removeDocuments(
120+
server: Server,
121+
coll: Collection,
122+
selector: Document,
123+
options: DeleteOptions | Document,
124+
callback: Callback
125+
): void {
126+
if (typeof options === 'function') {
127+
(callback = options as Callback), (options = {});
128+
} else if (typeof selector === 'function') {
129+
callback = selector as Callback;
130+
options = {};
131+
selector = {};
132+
}
133+
134+
// Create an empty options object if the provided one is null
135+
options = options || {};
136+
137+
// Final options for retryable writes
138+
let finalOptions = Object.assign({}, options);
139+
finalOptions = applyRetryableWrites(finalOptions, coll.s.db);
140+
141+
// If selector is null set empty
142+
if (selector == null) selector = {};
143+
144+
// Build the op
145+
const op = { q: selector, limit: 0 } as any;
146+
if (options.single) {
147+
op.limit = 1;
148+
} else if (finalOptions.retryWrites) {
149+
finalOptions.retryWrites = false;
150+
}
151+
if (options.hint) {
152+
op.hint = options.hint;
153+
}
154+
155+
// Have we specified collation
156+
try {
157+
decorateWithCollation(finalOptions, coll, options);
158+
} catch (err) {
159+
return callback ? callback(err, null) : undefined;
160+
}
161+
162+
if (options.explain !== undefined && maxWireVersion(server) < 3) {
163+
return callback
164+
? callback(new MongoError(`server ${server.name} does not support explain on remove`))
165+
: undefined;
166+
}
167+
168+
// Execute the remove
169+
server.remove(
170+
coll.s.namespace.toString(),
171+
[op],
172+
finalOptions as WriteCommandOptions,
173+
(err, result) => {
174+
if (err || result == null) return callback(err);
175+
if (result.code) return callback(new MongoError(result));
176+
if (result.writeErrors) {
177+
return callback(new MongoError(result.writeErrors[0]));
178+
}
179+
180+
// Return the results
181+
callback(undefined, result);
182+
}
183+
);
184+
}
185+
122186
defineAspects(DeleteOperation, [Aspect.RETRYABLE, Aspect.WRITE_OPERATION]);
123187
defineAspects(DeleteOneOperation, [Aspect.RETRYABLE, Aspect.WRITE_OPERATION, Aspect.EXPLAINABLE]);
124188
defineAspects(DeleteManyOperation, [Aspect.WRITE_OPERATION, Aspect.EXPLAINABLE]);

0 commit comments

Comments
 (0)