From ba3cf52a7f1c318e9b56a625e89a305ee0781dbb Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Fri, 27 Aug 2021 20:41:43 +0200 Subject: [PATCH 1/8] add issue bot for prs --- .github/workflows/issue-bot.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/issue-bot.yml b/.github/workflows/issue-bot.yml index 18eb37ada1..4750fa403d 100644 --- a/.github/workflows/issue-bot.yml +++ b/.github/workflows/issue-bot.yml @@ -1,5 +1,8 @@ name: Issue Bot -on: [issues] +on: + issues: + pull_request: + types: [opened, reopened, edited] jobs: issue-bot: runs-on: ubuntu-latest From 2af63500e801739ce20bcd7767d93164591764e4 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Fri, 27 Aug 2021 20:45:32 +0200 Subject: [PATCH 2/8] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b04fa45be0..ed242afc50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -146,6 +146,7 @@ ___ - Add ability to pass context of an object via a header, X-Parse-Cloud-Context, for Cloud Code triggers. The header addition allows client SDK's to add context without injecting _context in the body of JSON objects (Corey Baker) [#7437](https://github.com/parse-community/parse-server/pull/7437) - Add CI check to add changelog entry (Manuel Trezza) [#7512](https://github.com/parse-community/parse-server/pull/7512) - Refactor: uniform issue templates across repos (Manuel Trezza) [#7528](https://github.com/parse-community/parse-server/pull/7528) +- ci: add Issue Bot for PRs (Manuel Trezza) [#7530](https://github.com/parse-community/parse-server/pull/7530) ## 4.10.2 [Full Changelog](https://github.com/parse-community/parse-server/compare/4.10.1...4.10.2) From d7a34ceeb9d99c995643c5c2dd6d8d7cb4f791b3 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Fri, 27 Aug 2021 20:50:41 +0200 Subject: [PATCH 3/8] Update issue-bot.yml --- .github/workflows/issue-bot.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/issue-bot.yml b/.github/workflows/issue-bot.yml index 4750fa403d..dd59371116 100644 --- a/.github/workflows/issue-bot.yml +++ b/.github/workflows/issue-bot.yml @@ -1,6 +1,7 @@ name: Issue Bot on: issues: + types: [opened, reopened, edited] pull_request: types: [opened, reopened, edited] jobs: From f9c09a8001ca432c55dd19e70e17567ab9954da9 Mon Sep 17 00:00:00 2001 From: mtrezza Date: Fri, 15 Oct 2021 00:19:18 +0000 Subject: [PATCH 4/8] ci: bump environment From c83aa9e3a774dc09653f2606e364cea59ebed805 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Mon, 8 Nov 2021 01:45:19 +0100 Subject: [PATCH 5/8] add mongodb 5.1.0 ci check --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 628903944d..03e3aada2f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,6 +101,11 @@ jobs: strategy: matrix: include: + - name: MongoDB 5.1, ReplicaSet, WiredTiger + MONGODB_VERSION: 5.1.0 + MONGODB_TOPOLOGY: replicaset + MONGODB_STORAGE_ENGINE: wiredTiger + NODE_VERSION: 14.18.1 - name: MongoDB 5.0, ReplicaSet, WiredTiger MONGODB_VERSION: 5.0.3 MONGODB_TOPOLOGY: replicaset From ba23a6d4b0733724bfe884d1dacc0a0436ad17c8 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Mon, 8 Nov 2021 01:45:23 +0100 Subject: [PATCH 6/8] Update README.md --- README.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5a8a5d7dcf..b11ec6176b 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,8 @@ Before you start make sure you have installed: #### Node.js 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 | Latest Version | End-of-Life | Compatible | +|------------|----------------|-------------|--------------| | Node.js 12 | 12.22.7 | April 2022 | ✅ Yes | | Node.js 14 | 14.18.1 | April 2023 | ✅ Yes | | Node.js 15 | 15.14.0 | June 2021 | ✅ Yes | @@ -122,22 +122,25 @@ Parse Server is continuously tested with the most recent releases of Node.js to #### MongoDB 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 only test against versions that are officially supported and have not reached their end-of-life date. -| Version | Latest Version | End-of-Life | Compatible | -|-------------|----------------|--------------|------------| -| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | -| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | -| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | -| MongoDB 5.0 | 5.0.3 | January 2024 | ✅ Yes | - +| Version | Latest Version | End-of-Life | Compatible | +|-------------|----------------|--------------|---------------| +| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | +| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | +| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | +| MongoDB 5.0 | 5.0.3 | January 2024 | ⚠️ Partially¹ | +| MongoDB 5.1 | 5.1.0 | January 2024 | ⚠️ Partially¹ | + +¹ Parse Server is still using the MongoDB NodeJS driver 3.x, so not all MongoDB 5.0 features may be fully supported yet, see issue [#7670](https://github.com/parse-community/parse-server/issues/7670). + #### PostgreSQL Parse Server is continuously tested with the most recent releases of PostgreSQL and PostGIS to ensure compatibility, using [PostGIS docker images](https://registry.hub.docker.com/r/postgis/postgis/tags?page=1&ordering=last_updated). We follow the [PostgreSQL support schedule](https://www.postgresql.org/support/versioning) and [PostGIS support schedule](https://www.postgis.net/eol_policy/) and only test against versions that are officially supported and have not reached their end-of-life date. Due to the extensive PostgreSQL support duration of 5 years, Parse Server drops support if a version is older than 3.5 years and a newer version has been available for at least 2.5 years. | Version | PostGIS Version | End-of-Life | Parse Server Support End | Compatible | |-------------|-----------------|---------------|--------------------------|------------| -| Postgres 11 | 3.0, 3.1 | November 2023 | April 2022 | ✅ Yes | -| Postgres 12 | 3.1 | November 2024 | April 2023 | ✅ Yes | -| Postgres 13 | 3.1 | November 2025 | April 2024 | ✅ Yes | -| Postgres 14 | 3.1 | November 2026 | April 2025 | ✅ Yes | +| Postgres 11 | 3.0, 3.1 | November 2023 | April 2022 | ✅ Yes | +| Postgres 12 | 3.1 | November 2024 | April 2023 | ✅ Yes | +| Postgres 13 | 3.1 | November 2025 | April 2024 | ✅ Yes | +| Postgres 14 | 3.1 | November 2026 | April 2025 | ✅ Yes | ### Locally ```bash From d59a901632c4484e7b3613f76cd2383493f63c62 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 27 Mar 2022 03:07:30 +0200 Subject: [PATCH 7/8] Update README.md --- README.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8ccef36e20..5a5d88c3d6 100644 --- a/README.md +++ b/README.md @@ -122,25 +122,23 @@ Parse Server is continuously tested with the most recent releases of Node.js to #### MongoDB 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 only test against versions that are officially supported and have not reached their end-of-life date. -| Version | Latest Version | End-of-Life | Compatible | -|-------------|----------------|--------------|---------------| -| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | -| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | -| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | -| MongoDB 5.0 | 5.0.3 | January 2024 | ⚠️ Partially¹ | -| MongoDB 5.1 | 5.1.0 | January 2024 | ⚠️ Partially¹ | - -¹ Parse Server is still using the MongoDB NodeJS driver 3.x, so not all MongoDB 5.0 features may be fully supported yet, see issue [#7670](https://github.com/parse-community/parse-server/issues/7670). +| Version | Latest Version | End-of-Life | Compatible | +|-------------|----------------|--------------|------------| +| MongoDB 4.0 | 4.0.27 | April 2022 | ✅ Yes | +| MongoDB 4.2 | 4.2.17 | TBD | ✅ Yes | +| MongoDB 4.4 | 4.4.10 | TBD | ✅ Yes | +| MongoDB 5.0 | 5.0.3 | January 2024 | ✅ Yes | +| MongoDB 5.1 | 5.1.0 | January 2024 | ✅ Yes | #### PostgreSQL Parse Server is continuously tested with the most recent releases of PostgreSQL and PostGIS to ensure compatibility, using [PostGIS docker images](https://registry.hub.docker.com/r/postgis/postgis/tags?page=1&ordering=last_updated). We follow the [PostgreSQL support schedule](https://www.postgresql.org/support/versioning) and [PostGIS support schedule](https://www.postgis.net/eol_policy/) and only test against versions that are officially supported and have not reached their end-of-life date. Due to the extensive PostgreSQL support duration of 5 years, Parse Server drops support if a version is older than 3.5 years and a newer version has been available for at least 2.5 years. | Version | PostGIS Version | End-of-Life | Parse Server Support End | Compatible | |-------------|-----------------|---------------|--------------------------|------------| -| Postgres 11 | 3.0, 3.1, 3.2 | November 2023 | April 2022 | ✅ Yes | -| Postgres 12 | 3.2 | November 2024 | April 2023 | ✅ Yes | -| Postgres 13 | 3.2 | November 2025 | April 2024 | ✅ Yes | -| Postgres 14 | 3.2 | November 2026 | April 2025 | ✅ Yes | +| Postgres 11 | 3.0, 3.1, 3.2 | November 2023 | April 2022 | ✅ Yes | +| Postgres 12 | 3.2 | November 2024 | April 2023 | ✅ Yes | +| Postgres 13 | 3.2 | November 2025 | April 2024 | ✅ Yes | +| Postgres 14 | 3.2 | November 2026 | April 2025 | ✅ Yes | ### Locally ```bash From 006621dd22ec229dc03ed137f0be07b42ad5ce6c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 27 Mar 2022 03:44:33 +0200 Subject: [PATCH 8/8] fix tests --- package.json | 9 +- spec/MongoStorageAdapter.spec.js | 36 +++++++- spec/ParseQuery.hint.spec.js | 144 +++++++++++++++++++++++++++++-- 3 files changed, 178 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index a2395fafe0..d8d229ab38 100644 --- a/package.json +++ b/package.json @@ -120,12 +120,13 @@ "test:mongodb:4.2.17": "npm run test:mongodb --dbversion=4.2.17", "test:mongodb:4.4.10": "npm run test:mongodb --dbversion=4.4.10", "test:mongodb:5.0.5": "npm run test:mongodb --dbversion=5.0.5", + "test:mongodb:5.1.0": "npm run test:mongodb --dbversion=5.1.0", "posttest:mongodb": "mongodb-runner stop", - "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", - "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", + "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner start", + "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 jasmine", "test": "npm run testonly", - "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", - "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.0.5} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", + "posttest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} mongodb-runner stop", + "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.1.0} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=wiredTiger} TESTING=1 nyc jasmine", "start": "node ./bin/parse-server", "prettier": "prettier --write {src,spec}/{**/*,*}.js", "prepare": "npm run build", diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index a31a6134f7..130971a535 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -308,7 +308,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); }); - it('should use index for caseInsensitive query', async () => { + it_only_mongodb_version('<5.1')('should use index for caseInsensitive query', async () => { const user = new Parse.User(); user.set('username', 'Bugs'); user.set('password', 'Bunny'); @@ -342,6 +342,40 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); }); + it_only_mongodb_version('>=5.1')('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 () => { const database = Config.get(Parse.applicationId).database; const obj = new Parse.Object('MyObject'); diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 2685137801..8ceb441d5b 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -27,7 +27,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { await TestUtils.destroyAllDataPermanently(false); }); - it('query find with hint string', async () => { + it_only_mongodb_version('<5.1')('query find with hint string', async () => { const object = new TestObject(); await object.save(); @@ -39,7 +39,18 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it('query find with hint object', async () => { + it_only_mongodb_version('>=5.1')('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('<5.1')('query find with hint object', async () => { const object = new TestObject(); await object.save(); @@ -53,6 +64,20 @@ describe_only_db('mongo')('Parse.Query hint', () => { }); }); + it_only_mongodb_version('>=5.1')('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('<4.4')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -73,7 +98,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=4.4')('query aggregate with hint string', async () => { + it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -97,6 +122,30 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); + it_only_mongodb_version('>=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.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('<4.4')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -117,7 +166,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=4.4')('query aggregate with hint object', async () => { + it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -142,7 +191,32 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it('query find with hint (rest)', async () => { + it_only_mongodb_version('>=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.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.1')('query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -167,6 +241,31 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); + it_only_mongodb_version('>=5.1')('query find 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' }); await object.save(); @@ -194,7 +293,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=4.4')('query aggregate with hint (rest)', async () => { + 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, { @@ -226,4 +325,37 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); + + it_only_mongodb_version('>=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({ objectId: '$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({ objectId: '$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 }); + }); });