Skip to content

Commit 95fedf4

Browse files
authored
feat: allow hinting the delete command
This change adds a new hint parameter to each statement of the delete command, which works just like hinting in update. Errors will now also be thrown if a hint is passed on older server versions which don't support them. NODE-2477
1 parent e5aced7 commit 95fedf4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+4245
-113
lines changed

lib/bulk/common.js

+3
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,9 @@ class BulkOperationBase {
984984
if (op.deleteOne || op.deleteMany) {
985985
const limit = op.deleteOne ? 1 : 0;
986986
const operation = { q: op[key].filter, limit: limit };
987+
if (op[key].hint) {
988+
operation.hint = op[key].hint;
989+
}
987990
if (this.isOrdered) {
988991
if (op.collation) operation.collation = op.collation;
989992
}

lib/collection.js

+2
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,7 @@ Collection.prototype.update = deprecate(function(selector, update, options, call
932932
* @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
933933
* @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
934934
* @param {ClientSession} [options.session] optional session to use for this operation
935+
* @param {string|object} [options.hint] optional index hint for optimizing the filter query
935936
* @param {Collection~deleteWriteOpCallback} [callback] The command result callback
936937
* @returns {Promise} returns Promise if no callback passed
937938
*/
@@ -966,6 +967,7 @@ Collection.prototype.removeOne = Collection.prototype.deleteOne;
966967
* @param {boolean} [options.serializeFunctions=false] Serialize functions on any object.
967968
* @param {boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
968969
* @param {ClientSession} [options.session] optional session to use for this operation
970+
* @param {string|object} [options.hint] optional index hint for optimizing the filter query
969971
* @param {Collection~deleteWriteOpCallback} [callback] The command result callback
970972
* @returns {Promise} returns Promise if no callback passed
971973
*/

lib/operations/common_functions.js

+3
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ function removeDocuments(coll, selector, options, callback) {
299299
} else if (finalOptions.retryWrites) {
300300
finalOptions.retryWrites = false;
301301
}
302+
if (options.hint) {
303+
op.hint = options.hint;
304+
}
302305

303306
// Have we specified collation
304307
try {

lib/sdam/server.js

+6
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,12 @@ function executeWriteOperation(args, options, callback) {
448448
callback(new MongoError(`server ${server.name} does not support collation`));
449449
return;
450450
}
451+
if (maxWireVersion(server) < 5) {
452+
if ((op === 'update' || op === 'remove') && ops.find(o => o.hint)) {
453+
callback(new MongoError(`servers < 3.4 do not support hint on ${op}`));
454+
return;
455+
}
456+
}
451457

452458
server.s.pool.withConnection((err, conn, cb) => {
453459
if (err) {

test/functional/crud_spec.test.js

+15-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ const TestRunnerContext = require('./spec-runner').TestRunnerContext;
1212
const gatherTestSuites = require('./spec-runner').gatherTestSuites;
1313
const generateTopologyTests = require('./spec-runner').generateTopologyTests;
1414

15+
function enforceServerVersionLimits(requires, scenario) {
16+
const versionLimits = [];
17+
if (scenario.minServerVersion) {
18+
versionLimits.push(`>=${scenario.minServerVersion}`);
19+
}
20+
if (scenario.maxServerVersion) {
21+
versionLimits.push(`<=${scenario.maxServerVersion}`);
22+
}
23+
if (versionLimits.length) {
24+
requires.mongodb = versionLimits.join(' ');
25+
}
26+
}
27+
1528
function findScenarios() {
1629
const route = [__dirname, '..', 'spec', 'crud'].concat(Array.from(arguments));
1730
return fs
@@ -53,9 +66,7 @@ describe('CRUD spec', function() {
5366
}
5467
};
5568

56-
if (scenario.minServerVersion) {
57-
metadata.requires.mongodb = `>=${scenario.minServerVersion}`;
58-
}
69+
enforceServerVersionLimits(metadata.requires, scenario);
5970

6071
describe(scenarioName, function() {
6172
scenario.tests.forEach(scenarioTest => {
@@ -83,9 +94,7 @@ describe('CRUD spec', function() {
8394
}
8495
};
8596

86-
if (scenario.minServerVersion) {
87-
metadata.requires.mongodb = `>=${scenario.minServerVersion}`;
88-
}
97+
enforceServerVersionLimits(metadata.requires, scenario);
8998

9099
describe(scenarioName, function() {
91100
beforeEach(() => testContext.db.dropDatabase());

test/functional/spec-runner/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ function generateTopologyTests(testSuites, testContext, filter) {
9999
let runOn = testSuite.runOn;
100100
if (!testSuite.runOn) {
101101
runOn = [{ minServerVersion: testSuite.minServerVersion }];
102+
if (testSuite.maxServerVersion) {
103+
runOn.push({ maxServerVersion: testSuite.maxServerVersion });
104+
}
102105
}
103-
104106
const environmentRequirementList = parseRunOn(runOn);
105107

106108
environmentRequirementList.forEach(requires => {

test/spec/crud/v2/bulkWrite-arrayFilters.json

+84-18
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"database_name": "crud-tests",
3333
"tests": [
3434
{
35-
"description": "BulkWrite with arrayFilters",
35+
"description": "BulkWrite updateOne with arrayFilters",
3636
"operations": [
3737
{
3838
"name": "bulkWrite",
@@ -53,7 +53,86 @@
5353
}
5454
]
5555
}
56-
},
56+
}
57+
],
58+
"options": {
59+
"ordered": true
60+
}
61+
},
62+
"result": {
63+
"deletedCount": 0,
64+
"insertedCount": 0,
65+
"insertedIds": {},
66+
"matchedCount": 1,
67+
"modifiedCount": 1,
68+
"upsertedCount": 0,
69+
"upsertedIds": {}
70+
}
71+
}
72+
],
73+
"expectations": [
74+
{
75+
"command_started_event": {
76+
"command": {
77+
"update": "test",
78+
"updates": [
79+
{
80+
"q": {},
81+
"u": {
82+
"$set": {
83+
"y.$[i].b": 2
84+
}
85+
},
86+
"arrayFilters": [
87+
{
88+
"i.b": 3
89+
}
90+
]
91+
}
92+
],
93+
"ordered": true
94+
},
95+
"command_name": "update",
96+
"database_name": "crud-tests"
97+
}
98+
}
99+
],
100+
"outcome": {
101+
"collection": {
102+
"data": [
103+
{
104+
"_id": 1,
105+
"y": [
106+
{
107+
"b": 2
108+
},
109+
{
110+
"b": 1
111+
}
112+
]
113+
},
114+
{
115+
"_id": 2,
116+
"y": [
117+
{
118+
"b": 0
119+
},
120+
{
121+
"b": 1
122+
}
123+
]
124+
}
125+
]
126+
}
127+
}
128+
},
129+
{
130+
"description": "BulkWrite updateMany with arrayFilters",
131+
"operations": [
132+
{
133+
"name": "bulkWrite",
134+
"arguments": {
135+
"requests": [
57136
{
58137
"name": "updateMany",
59138
"arguments": {
@@ -79,8 +158,8 @@
79158
"deletedCount": 0,
80159
"insertedCount": 0,
81160
"insertedIds": {},
82-
"matchedCount": 3,
83-
"modifiedCount": 3,
161+
"matchedCount": 2,
162+
"modifiedCount": 2,
84163
"upsertedCount": 0,
85164
"upsertedIds": {}
86165
}
@@ -92,19 +171,6 @@
92171
"command": {
93172
"update": "test",
94173
"updates": [
95-
{
96-
"q": {},
97-
"u": {
98-
"$set": {
99-
"y.$[i].b": 2
100-
}
101-
},
102-
"arrayFilters": [
103-
{
104-
"i.b": 3
105-
}
106-
]
107-
},
108174
{
109175
"q": {},
110176
"u": {
@@ -134,7 +200,7 @@
134200
"_id": 1,
135201
"y": [
136202
{
137-
"b": 2
203+
"b": 3
138204
},
139205
{
140206
"b": 2

test/spec/crud/v2/bulkWrite-arrayFilters.yml

+42-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ database_name: &database_name "crud-tests"
1111

1212
tests:
1313
-
14-
description: "BulkWrite with arrayFilters"
14+
description: "BulkWrite updateOne with arrayFilters"
1515
operations:
1616
-
1717
name: "bulkWrite"
@@ -24,6 +24,44 @@ tests:
2424
filter: {}
2525
update: { $set: { "y.$[i].b": 2 } }
2626
arrayFilters: [ { "i.b": 3 } ]
27+
options: { ordered: true }
28+
result:
29+
deletedCount: 0
30+
insertedCount: 0
31+
insertedIds: {}
32+
matchedCount: 1
33+
modifiedCount: 1
34+
upsertedCount: 0
35+
upsertedIds: {}
36+
expectations:
37+
-
38+
command_started_event:
39+
command:
40+
update: *collection_name
41+
updates:
42+
-
43+
q: {}
44+
u: { $set: { "y.$[i].b": 2 } }
45+
arrayFilters: [ { "i.b": 3 } ]
46+
ordered: true
47+
# TODO: check that these fields do not exist once
48+
# https://jira.mongodb.org/browse/SPEC-1215 has been resolved.
49+
# writeConcern: null
50+
# bypassDocumentValidation: null
51+
command_name: update
52+
database_name: *database_name
53+
outcome:
54+
collection:
55+
data:
56+
- {_id: 1, y: [{b: 2}, {b: 1}]}
57+
- {_id: 2, y: [{b: 0}, {b: 1}]}
58+
-
59+
description: "BulkWrite updateMany with arrayFilters"
60+
operations:
61+
-
62+
name: "bulkWrite"
63+
arguments:
64+
requests:
2765
-
2866
# UpdateMany when multiple documents match arrayFilters
2967
name: "updateMany"
@@ -36,8 +74,8 @@ tests:
3674
deletedCount: 0
3775
insertedCount: 0
3876
insertedIds: {}
39-
matchedCount: 3
40-
modifiedCount: 3
77+
matchedCount: 2
78+
modifiedCount: 2
4179
upsertedCount: 0
4280
upsertedIds: {}
4381
expectations:
@@ -46,10 +84,6 @@ tests:
4684
command:
4785
update: *collection_name
4886
updates:
49-
-
50-
q: {}
51-
u: { $set: { "y.$[i].b": 2 } }
52-
arrayFilters: [ { "i.b": 3 } ]
5387
-
5488
q: {}
5589
u: { $set: { "y.$[i].b": 2 } }
@@ -65,5 +99,5 @@ tests:
6599
outcome:
66100
collection:
67101
data:
68-
- {_id: 1, y: [{b: 2}, {b: 2}]}
102+
- {_id: 1, y: [{b: 3}, {b: 2}]}
69103
- {_id: 2, y: [{b: 0}, {b: 2}]}

0 commit comments

Comments
 (0)