Skip to content

Commit c3c6333

Browse files
feat(NODE-6290): Add sort option to UpdateOne
1 parent 833eaa4 commit c3c6333

File tree

4 files changed

+374
-14
lines changed

4 files changed

+374
-14
lines changed

src/operations/update.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
import type { Document } from '../bson';
22
import type { Collection } from '../collection';
3-
import { MongoCompatibilityError, MongoInvalidArgumentError, MongoServerError } from '../error';
3+
import { MongoAPIError, MongoCompatibilityError, MongoInvalidArgumentError, MongoServerError } from '../error';
44
import type { InferIdType, TODO_NODE_3286 } from '../mongo_types';
55
import type { Server } from '../sdam/server';
66
import type { ClientSession } from '../sessions';
77
import { hasAtomicOperators, type MongoDBNamespace } from '../utils';
88
import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
99
import { Aspect, defineAspects, type Hint } from './operation';
1010

11+
12+
/** @public */
13+
export interface UpdateOneOptions extends UpdateOptions {
14+
/**
15+
* Specify which document the operation updates if the query matches multiple
16+
* documents. The first document matched by the sort order will be updated.
17+
*
18+
* This option is only supported by servers >= 8.0. Older servers will report an error for using this option.
19+
*/
20+
sort?: Document;
21+
}
22+
1123
/** @public */
1224
export interface UpdateOptions extends CommandOperationOptions {
1325
/** A set of filters specifying to which array elements an update should apply */
@@ -41,6 +53,12 @@ export interface UpdateResult<TSchema extends Document = Document> {
4153
upsertedId: InferIdType<TSchema> | null;
4254
}
4355

56+
/** @public */
57+
export interface UpdateOneStatement extends UpdateStatement {
58+
/** If the query matches multiple documents, the first document matched by the sort order will be updated. */
59+
sort?: Document;
60+
}
61+
4462
/** @public */
4563
export interface UpdateStatement {
4664
/** The query that matches documents to update. */
@@ -129,7 +147,7 @@ export class UpdateOperation extends CommandOperation<Document> {
129147

130148
/** @internal */
131149
export class UpdateOneOperation extends UpdateOperation {
132-
constructor(collection: Collection, filter: Document, update: Document, options: UpdateOptions) {
150+
constructor(collection: Collection, filter: Document, update: Document, options: UpdateOneOptions) {
133151
super(
134152
collection.s.namespace,
135153
[makeUpdateStatement(filter, update, { ...options, multi: false })],
@@ -195,6 +213,17 @@ export class UpdateManyOperation extends UpdateOperation {
195213
}
196214
}
197215

216+
/** @public */
217+
export interface ReplaceOneOptions extends ReplaceOptions {
218+
/**
219+
* Specify which document the operation replaces if the query matches multiple
220+
* documents. The first document matched by the sort order will be replaced.
221+
*
222+
* This option is only supported by servers >= 8.0. Older servers will report an error for using this option.
223+
*/
224+
sort?: Document;
225+
}
226+
198227
/** @public */
199228
export interface ReplaceOptions extends CommandOperationOptions {
200229
/** If true, allows the write to opt-out of document level validation */
@@ -215,7 +244,7 @@ export class ReplaceOneOperation extends UpdateOperation {
215244
collection: Collection,
216245
filter: Document,
217246
replacement: Document,
218-
options: ReplaceOptions
247+
options: ReplaceOneOptions
219248
) {
220249
super(
221250
collection.s.namespace,
@@ -251,8 +280,8 @@ export class ReplaceOneOperation extends UpdateOperation {
251280
export function makeUpdateStatement(
252281
filter: Document,
253282
update: Document | Document[],
254-
options: UpdateOptions & { multi?: boolean }
255-
): UpdateStatement {
283+
options: UpdateOneOptions & { multi?: boolean }
284+
): UpdateOneStatement {
256285
if (filter == null || typeof filter !== 'object') {
257286
throw new MongoInvalidArgumentError('Selector must be a valid JavaScript object');
258287
}
@@ -261,13 +290,15 @@ export function makeUpdateStatement(
261290
throw new MongoInvalidArgumentError('Document must be a valid JavaScript object');
262291
}
263292

264-
const op: UpdateStatement = { q: filter, u: update };
293+
const op: UpdateOneStatement = { q: filter, u: update };
265294
if (typeof options.upsert === 'boolean') {
266295
op.upsert = options.upsert;
267296
}
268297

269298
if (options.multi) {
270299
op.multi = options.multi;
300+
} else {
301+
op.sort = options?.sort;
271302
}
272303

273304
if (options.hint) {

test/integration/crud/crud.spec.test.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,13 @@ const loadBalancedCollationTests = [
5555
'Distinct with a collation'
5656
];
5757

58-
describe('CRUD unified', function () {
58+
describe.only('CRUD unified', function () {
5959
runUnifiedSuite(
6060
loadSpecTests(path.join('crud', 'unified')),
6161
({ description }, { isLoadBalanced }) => {
62-
return description.match(clientBulkWriteTests)
63-
? 'TODO(NODE-6257): implement client level bulk write'
64-
: unacknowledgedHintTests.includes(description)
65-
? `TODO(NODE-3541)`
66-
: isLoadBalanced && loadBalancedCollationTests.includes(description)
67-
? `TODO(NODE-6280): fix collation for find and modify commands on load balanced mode`
68-
: false;
62+
return description === 'BulkWrite replaceOne with sort option unsupported (server-side error)'
63+
? false
64+
: 'skipping you';
6965
}
7066
);
7167
});
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
{
2+
"description": "BulkWrite replaceOne-sort",
3+
"schemaVersion": "1.0",
4+
"createEntities": [
5+
{
6+
"client": {
7+
"id": "client0",
8+
"observeEvents": [
9+
"commandStartedEvent",
10+
"commandSucceededEvent"
11+
]
12+
}
13+
},
14+
{
15+
"database": {
16+
"id": "database0",
17+
"client": "client0",
18+
"databaseName": "crud-tests"
19+
}
20+
},
21+
{
22+
"collection": {
23+
"id": "collection0",
24+
"database": "database0",
25+
"collectionName": "coll0"
26+
}
27+
}
28+
],
29+
"initialData": [
30+
{
31+
"collectionName": "coll0",
32+
"databaseName": "crud-tests",
33+
"documents": [
34+
{
35+
"_id": 1,
36+
"x": 11
37+
},
38+
{
39+
"_id": 2,
40+
"x": 22
41+
},
42+
{
43+
"_id": 3,
44+
"x": 33
45+
}
46+
]
47+
}
48+
],
49+
"tests": [
50+
{
51+
"description": "BulkWrite replaceOne with sort option",
52+
"runOnRequirements": [
53+
{
54+
"minServerVersion": "8.0"
55+
}
56+
],
57+
"operations": [
58+
{
59+
"object": "collection0",
60+
"name": "bulkWrite",
61+
"arguments": {
62+
"requests": [
63+
{
64+
"replaceOne": {
65+
"filter": {
66+
"_id": {
67+
"$gt": 1
68+
}
69+
},
70+
"sort": {
71+
"_id": -1
72+
},
73+
"replacement": {
74+
"x": 1
75+
}
76+
}
77+
}
78+
]
79+
}
80+
}
81+
],
82+
"expectEvents": [
83+
{
84+
"client": "client0",
85+
"events": [
86+
{
87+
"commandStartedEvent": {
88+
"command": {
89+
"update": "coll0",
90+
"updates": [
91+
{
92+
"q": {
93+
"_id": {
94+
"$gt": 1
95+
}
96+
},
97+
"u": {
98+
"x": 1
99+
},
100+
"sort": {
101+
"_id": -1
102+
},
103+
"multi": {
104+
"$$unsetOrMatches": false
105+
},
106+
"upsert": {
107+
"$$unsetOrMatches": false
108+
}
109+
}
110+
]
111+
}
112+
}
113+
},
114+
{
115+
"commandSucceededEvent": {
116+
"reply": {
117+
"ok": 1,
118+
"n": 1
119+
},
120+
"commandName": "update"
121+
}
122+
}
123+
]
124+
}
125+
],
126+
"outcome": [
127+
{
128+
"collectionName": "coll0",
129+
"databaseName": "crud-tests",
130+
"documents": [
131+
{
132+
"_id": 1,
133+
"x": 11
134+
},
135+
{
136+
"_id": 2,
137+
"x": 22
138+
},
139+
{
140+
"_id": 3,
141+
"x": 1
142+
}
143+
]
144+
}
145+
]
146+
},
147+
{
148+
"description": "BulkWrite replaceOne with sort option unsupported (server-side error)",
149+
"runOnRequirements": [
150+
{
151+
"maxServerVersion": "7.99"
152+
}
153+
],
154+
"operations": [
155+
{
156+
"object": "collection0",
157+
"name": "bulkWrite",
158+
"arguments": {
159+
"requests": [
160+
{
161+
"replaceOne": {
162+
"filter": {
163+
"_id": {
164+
"$gt": 1
165+
}
166+
},
167+
"sort": {
168+
"_id": -1
169+
},
170+
"replacement": {
171+
"x": 1
172+
}
173+
}
174+
}
175+
]
176+
},
177+
"expectError": {
178+
"isClientError": false
179+
}
180+
}
181+
],
182+
"expectEvents": [
183+
{
184+
"client": "client0",
185+
"events": [
186+
{
187+
"commandStartedEvent": {
188+
"command": {
189+
"update": "coll0",
190+
"updates": [
191+
{
192+
"q": {
193+
"_id": {
194+
"$gt": 1
195+
}
196+
},
197+
"u": {
198+
"x": 1
199+
},
200+
"sort": {
201+
"_id": -1
202+
},
203+
"multi": {
204+
"$$unsetOrMatches": false
205+
},
206+
"upsert": {
207+
"$$unsetOrMatches": false
208+
}
209+
}
210+
]
211+
}
212+
}
213+
}
214+
]
215+
}
216+
],
217+
"outcome": [
218+
{
219+
"collectionName": "coll0",
220+
"databaseName": "crud-tests",
221+
"documents": [
222+
{
223+
"_id": 1,
224+
"x": 11
225+
},
226+
{
227+
"_id": 2,
228+
"x": 22
229+
},
230+
{
231+
"_id": 3,
232+
"x": 33
233+
}
234+
]
235+
}
236+
]
237+
}
238+
]
239+
}

0 commit comments

Comments
 (0)