From 15229ca488798aa36bec36cb3be23310028e9d99 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 11 Jan 2025 19:14:06 +0100 Subject: [PATCH 1/7] remove old mogodb versions --- .github/workflows/ci.yml | 24 ++++++------------------ README.md | 23 ++++++++++------------- package.json | 9 +++------ 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79d820cbff..de9f8aecd4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,41 +142,29 @@ jobs: strategy: matrix: 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 - MONGODB_VERSION: 6.0.14 + MONGODB_VERSION: 6.0.19 MONGODB_TOPOLOGY: replset NODE_VERSION: 22.12.0 - name: MongoDB 7, ReplicaSet - MONGODB_VERSION: 7.0.8 + MONGODB_VERSION: 7.0.16 MONGODB_TOPOLOGY: replset NODE_VERSION: 22.12.0 - name: MongoDB 8, ReplicaSet - MONGODB_VERSION: 8.0.0 + MONGODB_VERSION: 8.0.4 MONGODB_TOPOLOGY: replset NODE_VERSION: 22.12.0 - name: Redis Cache PARSE_SERVER_TEST_CACHE: redis - MONGODB_VERSION: 8.0.0 + MONGODB_VERSION: 8.0.4 MONGODB_TOPOLOGY: standalone NODE_VERSION: 22.12.0 - name: Node 20 - MONGODB_VERSION: 8.0.0 + MONGODB_VERSION: 8.0.4 MONGODB_TOPOLOGY: standalone NODE_VERSION: 20.18.0 - name: Node 18 - MONGODB_VERSION: 8.0.0 + MONGODB_VERSION: 8.0.4 MONGODB_TOPOLOGY: standalone NODE_VERSION: 18.20.4 fail-fast: false diff --git a/README.md b/README.md index eaea473a85..57924885ff 100644 --- a/README.md +++ b/README.md @@ -127,24 +127,21 @@ 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. -| Version | Latest Version | End-of-Life | Compatible | -|------------|----------------|-------------|------------| -| Node.js 18 | 18.20.4 | April 2025 | ✅ Yes | -| Node.js 20 | 20.18.0 | April 2026 | ✅ Yes | -| Node.js 22 | 22.12.0 | April 2027 | ✅ Yes | +| Version | Minimum Version | End-of-Life | Compatible | +|------------|-----------------|-------------|------------| +| Node.js 18 | 18.20.4 | April 2025 | ✅ Yes | +| Node.js 20 | 20.18.0 | April 2026 | ✅ Yes | +| Node.js 22 | 22.12.0 | April 2027 | ✅ Yes | #### 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 [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 | -|-------------|----------------|---------------|------------| -| MongoDB 4.2 | 4.2.25 | April 2023 | ✅ Yes | -| MongoDB 4.4 | 4.4.29 | February 2024 | ✅ Yes | -| MongoDB 5 | 5.0.26 | October 2024 | ✅ Yes | -| MongoDB 6 | 6.0.14 | July 2025 | ✅ Yes | -| MongoDB 7 | 7.0.8 | TDB | ✅ Yes | -| MongoDB 8 | 8.0.0 | TDB | ✅ Yes | +| Version | Minimum Version | End-of-Life | Compatible | +|-----------|-----------------|-------------|------------| +| MongoDB 6 | 6.0.19 | July 2025 | ✅ Yes | +| MongoDB 7 | 7.0.16 | August 2026 | ✅ Yes | +| MongoDB 8 | 8.0.4 | TDB | ✅ Yes | #### PostgreSQL diff --git a/package.json b/package.json index eda843a334..9f2d5b2ebf 100644 --- a/package.json +++ b/package.json @@ -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: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:4.2.19": "npm run test:mongodb --dbversion=4.2.19", - "test:mongodb:4.4.13": "npm run test:mongodb --dbversion=4.4.13", - "test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2", "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", - "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", - "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", + "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.3} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017", + "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.3} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", "test": "npm run testonly", "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.3} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 nyc jasmine", "start": "node ./bin/parse-server", "prettier": "prettier --write {src,spec}/{**/*,*}.js", "prepare": "npm run build", From 71b85a87cc58649522e71c5f4ca47f8f5cf12258 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:03:32 +0100 Subject: [PATCH 2/7] remove tests --- spec/MongoStorageAdapter.spec.js | 34 ---- spec/ParseQuery.hint.spec.js | 285 +------------------------------ 2 files changed, 1 insertion(+), 318 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index a913930c28..8c2e177724 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -424,40 +424,6 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { 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 () => { 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 cd8a01cbe9..1acf531eed 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -39,17 +39,6 @@ describe_only_db('mongo')('Parse.Query hint', () => { 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 () => { const object = new TestObject(); 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 () => { const object = new TestObject(); await object.save(); @@ -104,77 +79,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { }); }); - it_only_mongodb_version('<4.4')('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('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.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: '_id_', - explain: true, - }); - queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); - }); - - it_only_mongodb_version('>=5.1 <5.2')('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 () => { + fit_only_mongodb_version('>=5.2')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -198,76 +103,6 @@ describe_only_db('mongo')('Parse.Query hint', () => { 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(); - - 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('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.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.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.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); - expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); - }); - - it_only_mongodb_version('>=5.1 <5.2')('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' }); await object.save(); @@ -318,124 +153,6 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=5.1 <6')('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(); - 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('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.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.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.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); - expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); - }); - - it_only_mongodb_version('>=5.1 <5.2')('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' }); await object.save(); From ab5a5c86d0aadb24f1a8ea9f71f7122232c60afc Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:03:37 +0100 Subject: [PATCH 3/7] update scripts --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9f2d5b2ebf..0bed436c51 100644 --- a/package.json +++ b/package.json @@ -121,15 +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: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: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:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.19", + "test:mongodb:7.0.1": "npm run test:mongodb --dbversion=7.0.16", + "test:mongodb:8.0.4": "npm run test:mongodb --dbversion=8.0.4", "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:=8.0.3} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017", - "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.3} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", + "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:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", "test": "npm run testonly", "posttest": "cross-env mongodb-runner stop --all", - "coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=8.0.3} 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", "prettier": "prettier --write {src,spec}/{**/*,*}.js", "prepare": "npm run build", From ce2ce4269e4a2f54f04cb4dd59b955dc19e714b8 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:04:11 +0100 Subject: [PATCH 4/7] update scripts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0bed436c51..44436d4986 100644 --- a/package.json +++ b/package.json @@ -121,8 +121,8 @@ "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": "npm run test:mongodb:runnerstart --dbversion=$npm_config_dbversion && npm run test:mongodb:testonly --dbversion=$npm_config_dbversion", - "test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.19", - "test:mongodb:7.0.1": "npm run test:mongodb --dbversion=7.0.16", + "test:mongodb:6.0.19": "npm run test:mongodb --dbversion=6.0.19", + "test:mongodb:7.0.16": "npm run test:mongodb --dbversion=7.0.16", "test:mongodb:8.0.4": "npm run test:mongodb --dbversion=8.0.4", "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:=8.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017", From 49b8fbd6641b1b39667c2f57cdb8ee281ead8fc4 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:23:56 +0100 Subject: [PATCH 5/7] fix query planner tests --- spec/ParseQuery.hint.spec.js | 60 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 1acf531eed..bd0f5df736 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -79,7 +79,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { }); }); - fit_only_mongodb_version('>=5.2')('query aggregate with hint string', async () => { + it_only_mongodb_version('>=5.2')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -87,20 +87,20 @@ describe_only_db('mongo')('Parse.Query hint', () => { let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); - let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + let queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); result = await collection.aggregate([{ $group: { _id: '$foo' } }], { hint: '_id_', explain: true, }); - queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - 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_'); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); it_only_mongodb_version('>=5.2')('query aggregate with hint object', async () => { @@ -111,21 +111,21 @@ describe_only_db('mongo')('Parse.Query hint', () => { let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); - let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + let queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); result = await collection.aggregate([{ $group: { _id: '$foo' } }], { hint: { _id: 1 }, explain: true, }); - queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - 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 }); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); it_only_mongodb_version('<5.1 || >=6')('query find with hint (rest)', async () => { @@ -164,10 +164,10 @@ describe_only_db('mongo')('Parse.Query hint', () => { }, }); let response = await request(options); - let queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + let queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); options = Object.assign({}, masterKeyOptions, { url: Parse.serverURL + '/aggregate/TestObject', @@ -178,11 +178,11 @@ describe_only_db('mongo')('Parse.Query hint', () => { }, }); response = await request(options); - queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - 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 }); + queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); }); From 9b4d8f0ccf7bb1a82bb3c661a309b7656ebfe24e Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:37:33 +0100 Subject: [PATCH 6/7] fix query planner tests --- spec/ParseQuery.hint.spec.js | 88 ++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index bd0f5df736..07bd6ae666 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -79,7 +79,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { }); }); - it_only_mongodb_version('>=5.2')('query aggregate with hint string', async () => { + it_only_mongodb_version('<6')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -103,7 +103,31 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=5.2')('query aggregate with hint object', async () => { + it_only_mongodb_version('>=6')('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].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + 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].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + 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('<6')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -128,6 +152,31 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); + it_only_mongodb_version('>=6')('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].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + 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].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + 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 || >=6')('query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); @@ -153,7 +202,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=5.2')('query aggregate with hint (rest)', async () => { + it_only_mongodb_version('<6')('query aggregate with hint (rest)', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -185,4 +234,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('>=6')('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].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + 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].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + 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 }); + }); }); From 10688a52417d9c19e7043a300666e0bd3ad2bdd9 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 12 Jan 2025 01:13:54 +0100 Subject: [PATCH 7/7] fix incorrect mongodb ranges in tests --- spec/ParseQuery.hint.spec.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 07bd6ae666..0905eb7d32 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -79,7 +79,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { }); }); - it_only_mongodb_version('<6')('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(); @@ -103,7 +103,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=6')('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(); @@ -127,7 +127,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('<6')('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(); @@ -152,7 +152,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=6')('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(); @@ -202,7 +202,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('<6')('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, { @@ -235,7 +235,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=6')('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, {