feat: Increase required minimum MongoDB versions to 6.0.19, 7.0.16, 8.0.4 (#9531)

BREAKING CHANGE: This releases increases the required minimum MongoDB versions to `6.0.19`, `7.0.16`, `8.0.4` and removes support for MongoDB `4`, `5`.
This commit is contained in:
Manuel
2025-01-12 01:44:10 +01:00
committed by GitHub
parent fbf78f0802
commit 871e5082a9
5 changed files with 31 additions and 284 deletions

View File

@@ -142,41 +142,29 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- name: MongoDB 4.2, ReplicaSet
MONGODB_VERSION: 4.2.25
MONGODB_TOPOLOGY: replset
NODE_VERSION: 22.12.0
- name: MongoDB 4.4, ReplicaSet
MONGODB_VERSION: 4.4.29
MONGODB_TOPOLOGY: replset
NODE_VERSION: 22.12.0
- name: MongoDB 5, ReplicaSet
MONGODB_VERSION: 5.0.26
MONGODB_TOPOLOGY: replset
NODE_VERSION: 22.12.0
- name: MongoDB 6, ReplicaSet - name: MongoDB 6, ReplicaSet
MONGODB_VERSION: 6.0.14 MONGODB_VERSION: 6.0.19
MONGODB_TOPOLOGY: replset MONGODB_TOPOLOGY: replset
NODE_VERSION: 22.12.0 NODE_VERSION: 22.12.0
- name: MongoDB 7, ReplicaSet - name: MongoDB 7, ReplicaSet
MONGODB_VERSION: 7.0.8 MONGODB_VERSION: 7.0.16
MONGODB_TOPOLOGY: replset MONGODB_TOPOLOGY: replset
NODE_VERSION: 22.12.0 NODE_VERSION: 22.12.0
- name: MongoDB 8, ReplicaSet - name: MongoDB 8, ReplicaSet
MONGODB_VERSION: 8.0.0 MONGODB_VERSION: 8.0.4
MONGODB_TOPOLOGY: replset MONGODB_TOPOLOGY: replset
NODE_VERSION: 22.12.0 NODE_VERSION: 22.12.0
- name: Redis Cache - name: Redis Cache
PARSE_SERVER_TEST_CACHE: redis PARSE_SERVER_TEST_CACHE: redis
MONGODB_VERSION: 8.0.0 MONGODB_VERSION: 8.0.4
MONGODB_TOPOLOGY: standalone MONGODB_TOPOLOGY: standalone
NODE_VERSION: 22.12.0 NODE_VERSION: 22.12.0
- name: Node 20 - name: Node 20
MONGODB_VERSION: 8.0.0 MONGODB_VERSION: 8.0.4
MONGODB_TOPOLOGY: standalone MONGODB_TOPOLOGY: standalone
NODE_VERSION: 20.18.0 NODE_VERSION: 20.18.0
- name: Node 18 - name: Node 18
MONGODB_VERSION: 8.0.0 MONGODB_VERSION: 8.0.4
MONGODB_TOPOLOGY: standalone MONGODB_TOPOLOGY: standalone
NODE_VERSION: 18.20.4 NODE_VERSION: 18.20.4
fail-fast: false fail-fast: false

View File

@@ -127,8 +127,8 @@ Before you start make sure you have installed:
Parse Server is continuously tested with the most recent releases of Node.js to ensure compatibility. We follow the [Node.js Long Term Support plan](https://github.com/nodejs/Release) and only test against versions that are officially supported and have not reached their end-of-life date. Parse Server is continuously tested with the most recent releases of Node.js to ensure compatibility. We follow the [Node.js Long Term Support plan](https://github.com/nodejs/Release) and only test against versions that are officially supported and have not reached their end-of-life date.
| Version | Latest Version | End-of-Life | Compatible | | Version | Minimum Version | End-of-Life | Compatible |
|------------|----------------|-------------|------------| |------------|-----------------|-------------|------------|
| Node.js 18 | 18.20.4 | April 2025 | ✅ Yes | | Node.js 18 | 18.20.4 | April 2025 | ✅ Yes |
| Node.js 20 | 20.18.0 | April 2026 | ✅ Yes | | Node.js 20 | 20.18.0 | April 2026 | ✅ Yes |
| Node.js 22 | 22.12.0 | April 2027 | ✅ Yes | | Node.js 22 | 22.12.0 | April 2027 | ✅ Yes |
@@ -137,14 +137,11 @@ Parse Server is continuously tested with the most recent releases of Node.js to
Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and [MongoDB lifecycle schedule](https://www.mongodb.com/support-policy/lifecycles) and only test against versions that are officially supported and have not reached their end-of-life date. MongoDB "rapid releases" are ignored as these are considered pre-releases of the next major version. Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and [MongoDB lifecycle schedule](https://www.mongodb.com/support-policy/lifecycles) and only test against versions that are officially supported and have not reached their end-of-life date. MongoDB "rapid releases" are ignored as these are considered pre-releases of the next major version.
| Version | Latest Version | End-of-Life | Compatible | | Version | Minimum Version | End-of-Life | Compatible |
|-------------|----------------|---------------|------------| |-----------|-----------------|-------------|------------|
| MongoDB 4.2 | 4.2.25 | April 2023 | ✅ Yes | | MongoDB 6 | 6.0.19 | July 2025 | ✅ Yes |
| MongoDB 4.4 | 4.4.29 | February 2024 | ✅ Yes | | MongoDB 7 | 7.0.16 | August 2026 | ✅ Yes |
| MongoDB 5 | 5.0.26 | October 2024 | ✅ Yes | | MongoDB 8 | 8.0.4 | TDB | ✅ Yes |
| MongoDB 6 | 6.0.14 | July 2025 | ✅ Yes |
| MongoDB 7 | 7.0.8 | TDB | ✅ Yes |
| MongoDB 8 | 8.0.0 | TDB | ✅ Yes |
#### PostgreSQL #### PostgreSQL

View File

@@ -121,18 +121,15 @@
"test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017", "test:mongodb:runnerstart": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017",
"test:mongodb:testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", "test:mongodb:testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=$npm_config_dbversion} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
"test:mongodb": "npm run test:mongodb:runnerstart --dbversion=$npm_config_dbversion && npm run test:mongodb:testonly --dbversion=$npm_config_dbversion", "test:mongodb": "npm run test:mongodb:runnerstart --dbversion=$npm_config_dbversion && npm run test:mongodb:testonly --dbversion=$npm_config_dbversion",
"test:mongodb:4.2.19": "npm run test:mongodb --dbversion=4.2.19", "test:mongodb:6.0.19": "npm run test:mongodb --dbversion=6.0.19",
"test:mongodb:4.4.13": "npm run test:mongodb --dbversion=4.4.13", "test:mongodb:7.0.16": "npm run test:mongodb --dbversion=7.0.16",
"test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2", "test:mongodb:8.0.4": "npm run test:mongodb --dbversion=8.0.4",
"test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.2",
"test:mongodb:7.0.1": "npm run test:mongodb --dbversion=7.0.1",
"test:mongodb:8.0.3": "npm run test:mongodb --dbversion=8.0.3",
"test:postgres:testonly": "cross-env PARSE_SERVER_TEST_DB=postgres PARSE_SERVER_TEST_DATABASE_URI=postgres://postgres:password@localhost:5432/parse_server_postgres_adapter_test_database npm run testonly", "test:postgres:testonly": "cross-env PARSE_SERVER_TEST_DB=postgres PARSE_SERVER_TEST_DATABASE_URI=postgres://postgres:password@localhost:5432/parse_server_postgres_adapter_test_database npm run testonly",
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017", "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017",
"testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine",
"test": "npm run testonly", "test": "npm run testonly",
"posttest": "cross-env mongodb-runner stop --all", "posttest": "cross-env mongodb-runner stop --all",
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 nyc jasmine", "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 nyc jasmine",
"start": "node ./bin/parse-server", "start": "node ./bin/parse-server",
"prettier": "prettier --write {src,spec}/{**/*,*}.js", "prettier": "prettier --write {src,spec}/{**/*,*}.js",
"prepare": "npm run build", "prepare": "npm run build",

View File

@@ -424,40 +424,6 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH');
}); });
it_only_mongodb_version('>=5.1 <6')('should use index for caseInsensitive query', async () => {
const user = new Parse.User();
user.set('username', 'Bugs');
user.set('password', 'Bunny');
await user.signUp();
const database = Config.get(Parse.applicationId).database;
await database.adapter.dropAllIndexes('_User');
const preIndexPlan = await database.find(
'_User',
{ username: 'bugs' },
{ caseInsensitive: true, explain: true }
);
const schema = await new Parse.Schema('_User').get();
await database.adapter.ensureIndex(
'_User',
schema,
['username'],
'case_insensitive_username',
true
);
const postIndexPlan = await database.find(
'_User',
{ username: 'bugs' },
{ caseInsensitive: true, explain: true }
);
expect(preIndexPlan.queryPlanner.winningPlan.queryPlan.stage).toBe('COLLSCAN');
expect(postIndexPlan.queryPlanner.winningPlan.queryPlan.stage).toBe('FETCH');
});
it('should delete field without index', async () => { it('should delete field without index', async () => {
const database = Config.get(Parse.applicationId).database; const database = Config.get(Parse.applicationId).database;
const obj = new Parse.Object('MyObject'); const obj = new Parse.Object('MyObject');

View File

@@ -39,17 +39,6 @@ describe_only_db('mongo')('Parse.Query hint', () => {
expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_');
}); });
it_only_mongodb_version('>=5.1 <6')('query find with hint string', async () => {
const object = new TestObject();
await object.save();
const collection = await config.database.adapter._adaptiveCollection('TestObject');
const explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true });
expect(explain.queryPlanner.winningPlan.queryPlan.stage).toBe('FETCH');
expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('IXSCAN');
expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.indexName).toBe('_id_');
});
it_only_mongodb_version('>=8')('query find with hint string', async () => { it_only_mongodb_version('>=8')('query find with hint string', async () => {
const object = new TestObject(); const object = new TestObject();
await object.save(); await object.save();
@@ -76,20 +65,6 @@ describe_only_db('mongo')('Parse.Query hint', () => {
}); });
}); });
it_only_mongodb_version('>=5.1 <6')('query find with hint object', async () => {
const object = new TestObject();
await object.save();
const collection = await config.database.adapter._adaptiveCollection('TestObject');
const explain = await collection._rawFind(
{ _id: object.id },
{ hint: { _id: 1 }, explain: true }
);
expect(explain.queryPlanner.winningPlan.queryPlan.stage).toBe('FETCH');
expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('IXSCAN');
expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.keyPattern).toEqual({ _id: 1 });
});
it_only_mongodb_version('>=8')('query find with hint object', async () => { it_only_mongodb_version('>=8')('query find with hint object', async () => {
const object = new TestObject(); const object = new TestObject();
await object.save(); await object.save();
@@ -104,7 +79,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
}); });
}); });
it_only_mongodb_version('<4.4')('query aggregate with hint string', async () => { it_only_mongodb_version('<7')('query aggregate with hint string', async () => {
const object = new TestObject({ foo: 'bar' }); const object = new TestObject({ foo: 'bar' });
await object.save(); await object.save();
@@ -112,29 +87,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { let result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
explain: true, explain: true,
}); });
let queryPlanner = result[0].stages[0].$cursor.queryPlanner;
let { queryPlanner } = result[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.stage).toBe('COLLSCAN');
result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
hint: '_id_',
explain: true,
});
queryPlanner = result[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.stage).toBe('FETCH');
expect(queryPlanner.winningPlan.inputStage.indexName).toBe('_id_');
});
it_only_mongodb_version('>=4.4 <5.1')('query aggregate with hint string', async () => {
const object = new TestObject({ foo: 'bar' });
await object.save();
const collection = await config.database.adapter._adaptiveCollection('TestObject');
let result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
explain: true,
});
let { queryPlanner } = result[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN');
expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined();
@@ -150,31 +103,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_');
}); });
it_only_mongodb_version('>=5.1 <5.2')('query aggregate with hint string', async () => { it_only_mongodb_version('>=7')('query aggregate with hint string', async () => {
const object = new TestObject({ foo: 'bar' });
await object.save();
const collection = await config.database.adapter._adaptiveCollection('TestObject');
let result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
explain: true,
});
let { queryPlanner } = result[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined();
result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
hint: '_id_',
explain: true,
});
queryPlanner = result[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_');
});
it_only_mongodb_version('>=5.2')('query aggregate with hint string', async () => {
const object = new TestObject({ foo: 'bar' }); const object = new TestObject({ foo: 'bar' });
await object.save(); await object.save();
@@ -198,7 +127,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_');
}); });
it_only_mongodb_version('<4.4')('query aggregate with hint object', async () => { it_only_mongodb_version('<7')('query aggregate with hint object', async () => {
const object = new TestObject({ foo: 'bar' }); const object = new TestObject({ foo: 'bar' });
await object.save(); await object.save();
@@ -206,27 +135,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { let result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
explain: true, explain: true,
}); });
let { queryPlanner } = result[0].stages[0].$cursor; let queryPlanner = result[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.stage).toBe('COLLSCAN');
result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
hint: { _id: 1 },
explain: true,
});
queryPlanner = result[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.stage).toBe('FETCH');
expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 });
});
it_only_mongodb_version('>=4.4 <5.1')('query aggregate with hint object', async () => {
const object = new TestObject({ foo: 'bar' });
await object.save();
const collection = await config.database.adapter._adaptiveCollection('TestObject');
let result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
explain: true,
});
let { queryPlanner } = result[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN');
expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined();
@@ -243,32 +152,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 });
}); });
it_only_mongodb_version('>=5.1 <5.2')('query aggregate with hint object', async () => { it_only_mongodb_version('>=7')('query aggregate with hint object', async () => {
const object = new TestObject({ foo: 'bar' });
await object.save();
const collection = await config.database.adapter._adaptiveCollection('TestObject');
let result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
explain: true,
});
let { queryPlanner } = result[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined();
result = await collection.aggregate([{ $group: { _id: '$foo' } }], {
hint: { _id: 1 },
explain: true,
});
queryPlanner = result[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 });
});
it_only_mongodb_version('>=5.2')('query aggregate with hint object', async () => {
const object = new TestObject({ foo: 'bar' }); const object = new TestObject({ foo: 'bar' });
await object.save(); await object.save();
@@ -318,32 +202,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_');
}); });
it_only_mongodb_version('>=5.1 <6')('query find with hint (rest)', async () => { it_only_mongodb_version('<7')('query aggregate with hint (rest)', async () => {
const object = new TestObject();
await object.save();
let options = Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/classes/TestObject',
qs: {
explain: true,
},
});
let response = await request(options);
let explain = response.data.results;
expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN');
options = Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/classes/TestObject',
qs: {
explain: true,
hint: '_id_',
},
});
response = await request(options);
explain = response.data.results;
expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_');
});
it_only_mongodb_version('<4.4')('query aggregate with hint (rest)', async () => {
const object = new TestObject({ foo: 'bar' }); const object = new TestObject({ foo: 'bar' });
await object.save(); await object.save();
let options = Object.assign({}, masterKeyOptions, { let options = Object.assign({}, masterKeyOptions, {
@@ -354,34 +213,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
}, },
}); });
let response = await request(options); let response = await request(options);
let { queryPlanner } = response.data.results[0].stages[0].$cursor; let queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.stage).toBe('COLLSCAN');
options = Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/aggregate/TestObject',
qs: {
explain: true,
hint: '_id_',
$group: JSON.stringify({ _id: '$foo' }),
},
});
response = await request(options);
queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 });
});
it_only_mongodb_version('>=4.4 <5.1')('query aggregate with hint (rest)', async () => {
const object = new TestObject({ foo: 'bar' });
await object.save();
let options = Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/aggregate/TestObject',
qs: {
explain: true,
$group: JSON.stringify({ _id: '$foo' }),
},
});
let response = await request(options);
let { queryPlanner } = response.data.results[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN');
expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined();
@@ -403,40 +235,7 @@ describe_only_db('mongo')('Parse.Query hint', () => {
expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 });
}); });
it_only_mongodb_version('>=5.1 <5.2')('query aggregate with hint (rest)', async () => { it_only_mongodb_version('>=7')('query aggregate with hint (rest)', async () => {
const object = new TestObject({ foo: 'bar' });
await object.save();
let options = Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/aggregate/TestObject',
qs: {
explain: true,
$group: JSON.stringify({ _id: '$foo' }),
},
});
let response = await request(options);
let { queryPlanner } = response.data.results[0].stages[0].$cursor;
expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined();
options = Object.assign({}, masterKeyOptions, {
url: Parse.serverURL + '/aggregate/TestObject',
qs: {
explain: true,
hint: '_id_',
$group: JSON.stringify({ _id: '$foo' }),
},
});
response = await request(options);
queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner;
expect(queryPlanner.winningPlan.queryPlan.stage).toBe('PROJECTION_SIMPLE');
expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_');
expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 });
});
it_only_mongodb_version('>=5.2')('query aggregate with hint (rest)', async () => {
const object = new TestObject({ foo: 'bar' }); const object = new TestObject({ foo: 'bar' });
await object.save(); await object.save();
let options = Object.assign({}, masterKeyOptions, { let options = Object.assign({}, masterKeyOptions, {