From 5c7918980cf9b3bccd5b521e1831fa6b65d32705 Mon Sep 17 00:00:00 2001 From: Arthur Cinader <700572+acinader@users.noreply.github.com> Date: Thu, 27 Feb 2020 10:56:14 -0800 Subject: [PATCH] Set min mongodb to 3.6 in prep for parse-server 4.0 (#6445) * Set min mongodb to 3.6 in prep for parse-server 4.0 fixes: 6444 * don't use anonymous functions when we can just pass the function. Also remove the boolean argument in tests that no longer exists. * generate the correct lock file. ooops. --- README.md | 6 +- package-lock.json | 6 +- package.json | 2 +- spec/DatabaseController.spec.js | 148 ++++++++------------------ src/Config.js | 3 +- src/Controllers/DatabaseController.js | 87 ++------------- src/Controllers/index.js | 4 +- src/Options/Definitions.js | 6 -- src/Options/docs.js | 1 - src/Options/index.js | 4 - 10 files changed, 60 insertions(+), 207 deletions(-) diff --git a/README.md b/README.md index e73f9b90..b935b83e 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,6 @@

- MongoDB 3.2 - MongoDB 3.4 MongoDB 3.6 MongoDB 4.0

@@ -80,7 +78,7 @@ The fastest and easiest way to get started is to run MongoDB and Parse Server lo Before you start make sure you have installed: -- [NodeJS](https://www.npmjs.com/) that includes `npm` +- [NodeJS](https://www.npmjs.com/) that includes `npm` - [MongoDB](https://www.mongodb.com/) or [PostgreSQL](https://www.postgresql.org/) - Optionally [Docker](https://www.docker.com/) @@ -337,7 +335,7 @@ It’s possible to change the default pages of the app and redirect the user to ```js var server = ParseServer({ ...otherOptions, - + customPages: { passwordResetSuccess: "http://yourapp.com/passwordResetSuccess", verifyEmailSuccess: "http://yourapp.com/verifyEmailSuccess", diff --git a/package-lock.json b/package-lock.json index f2d3f036..03b47a94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9032,9 +9032,9 @@ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, "mongodb": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.3.tgz", - "integrity": "sha512-II7P7A3XUdPiXRgcN96qIoRa1oesM6qLNZkzfPluNZjVkgQk3jnQwOT6/uDk4USRDTTLjNFw2vwfmbRGTA7msg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.4.tgz", + "integrity": "sha512-xGH41Ig4dkSH5ROGezkgDbsgt/v5zbNUwE3TcFsSbDc6Qn3Qil17dhLsESSDDPTiyFDCPJRpfd4887dtsPgKtA==", "requires": { "bl": "^2.2.0", "bson": "^1.1.1", diff --git a/package.json b/package.json index f6bb772d..d84b71e8 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "lodash": "4.17.15", "lru-cache": "5.1.1", "mime": "2.4.4", - "mongodb": "3.5.3", + "mongodb": "3.5.4", "node-rsa": "1.0.7", "parse": "2.11.0", "pg-promise": "10.4.4", diff --git a/spec/DatabaseController.spec.js b/spec/DatabaseController.spec.js index 70b554f8..af373e64 100644 --- a/spec/DatabaseController.spec.js +++ b/spec/DatabaseController.spec.js @@ -3,118 +3,56 @@ const validateQuery = DatabaseController._validateQuery; describe('DatabaseController', function() { describe('validateQuery', function() { - describe('with skipMongoDBServer13732Workaround disabled (the default)', function() { - it('should restructure simple cases of SERVER-13732', done => { - const query = { - $or: [{ a: 1 }, { a: 2 }], - _rperm: { $in: ['a', 'b'] }, - foo: 3, - }; - validateQuery(query, false); - expect(query).toEqual({ - $or: [ - { a: 1, _rperm: { $in: ['a', 'b'] }, foo: 3 }, - { a: 2, _rperm: { $in: ['a', 'b'] }, foo: 3 }, - ], - }); - done(); - }); - - it('should not restructure SERVER-13732 queries with $nears', done => { - let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } }; - validateQuery(query, false); - expect(query).toEqual({ - $or: [{ a: 1 }, { b: 1 }], - c: { $nearSphere: {} }, - }); - query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } }; - validateQuery(query, false); - expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } }); - done(); - }); - - it('should push refactored keys down a tree for SERVER-13732', done => { - const query = { - a: 1, - $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }], - }; - validateQuery(query, false); - expect(query).toEqual({ - $or: [ - { $or: [{ b: 1, a: 1 }, { b: 2, a: 1 }] }, - { $or: [{ c: 1, a: 1 }, { c: 2, a: 1 }] }, - ], - }); - done(); - }); - - it('should reject invalid queries', done => { - expect(() => validateQuery({ $or: { a: 1 } }, false)).toThrow(); - done(); - }); - - it('should accept valid queries', done => { - expect(() => - validateQuery({ $or: [{ a: 1 }, { b: 2 }] }, false) - ).not.toThrow(); - done(); + it('should not restructure simple cases of SERVER-13732', done => { + const query = { + $or: [{ a: 1 }, { a: 2 }], + _rperm: { $in: ['a', 'b'] }, + foo: 3, + }; + validateQuery(query); + expect(query).toEqual({ + $or: [{ a: 1 }, { a: 2 }], + _rperm: { $in: ['a', 'b'] }, + foo: 3, }); + done(); }); - describe('with skipMongoDBServer13732Workaround enabled', function() { - it('should not restructure simple cases of SERVER-13732', done => { - const query = { - $or: [{ a: 1 }, { a: 2 }], - _rperm: { $in: ['a', 'b'] }, - foo: 3, - }; - validateQuery(query, true); - expect(query).toEqual({ - $or: [{ a: 1 }, { a: 2 }], - _rperm: { $in: ['a', 'b'] }, - foo: 3, - }); - done(); + it('should not restructure SERVER-13732 queries with $nears', done => { + let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } }; + validateQuery(query); + expect(query).toEqual({ + $or: [{ a: 1 }, { b: 1 }], + c: { $nearSphere: {} }, + }); + query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } }; + validateQuery(query); + expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } }); + done(); + }); + + it('should not push refactored keys down a tree for SERVER-13732', done => { + const query = { + a: 1, + $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }], + }; + validateQuery(query); + expect(query).toEqual({ + a: 1, + $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }], }); - it('should not restructure SERVER-13732 queries with $nears', done => { - let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } }; - validateQuery(query, true); - expect(query).toEqual({ - $or: [{ a: 1 }, { b: 1 }], - c: { $nearSphere: {} }, - }); - query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } }; - validateQuery(query, true); - expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } }); - done(); - }); + done(); + }); - it('should not push refactored keys down a tree for SERVER-13732', done => { - const query = { - a: 1, - $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }], - }; - validateQuery(query, true); - expect(query).toEqual({ - a: 1, - $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }], - }); + it('should reject invalid queries', done => { + expect(() => validateQuery({ $or: { a: 1 } })).toThrow(); + done(); + }); - done(); - }); - - it('should reject invalid queries', done => { - expect(() => validateQuery({ $or: { a: 1 } }, true)).toThrow(); - done(); - }); - - it('should accept valid queries', done => { - expect(() => - validateQuery({ $or: [{ a: 1 }, { b: 2 }] }, true) - ).not.toThrow(); - done(); - }); + it('should accept valid queries', done => { + expect(() => validateQuery({ $or: [{ a: 1 }, { b: 2 }] })).not.toThrow(); + done(); }); }); }); diff --git a/src/Config.js b/src/Config.js index 8d31d3d9..2077626f 100644 --- a/src/Config.js +++ b/src/Config.js @@ -34,8 +34,7 @@ export class Config { ); config.database = new DatabaseController( cacheInfo.databaseController.adapter, - schemaCache, - cacheInfo.skipMongoDBServer13732Workaround + schemaCache ); } else { config[key] = cacheInfo[key]; diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 650781b5..5c3b8ab3 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -69,73 +69,14 @@ const isSpecialQueryKey = key => { return specialQuerykeys.indexOf(key) >= 0; }; -const validateQuery = ( - query: any, - skipMongoDBServer13732Workaround: boolean -): void => { +const validateQuery = (query: any): void => { if (query.ACL) { throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.'); } if (query.$or) { if (query.$or instanceof Array) { - query.$or.forEach(el => - validateQuery(el, skipMongoDBServer13732Workaround) - ); - - if (!skipMongoDBServer13732Workaround) { - /* In MongoDB 3.2 & 3.4, $or queries which are not alone at the top - * level of the query can not make efficient use of indexes due to a - * long standing bug known as SERVER-13732. - * - * This bug was fixed in MongoDB version 3.6. - * - * For versions pre-3.6, the below logic produces a substantial - * performance improvement inside the database by avoiding the bug. - * - * For versions 3.6 and above, there is no performance improvement and - * the logic is unnecessary. Some query patterns are even slowed by - * the below logic, due to the bug having been fixed and better - * query plans being chosen. - * - * When versions before 3.4 are no longer supported by this project, - * this logic, and the accompanying `skipMongoDBServer13732Workaround` - * flag, can be removed. - * - * This block restructures queries in which $or is not the sole top - * level element by moving all other top-level predicates inside every - * subdocument of the $or predicate, allowing MongoDB's query planner - * to make full use of the most relevant indexes. - * - * EG: {$or: [{a: 1}, {a: 2}], b: 2} - * Becomes: {$or: [{a: 1, b: 2}, {a: 2, b: 2}]} - * - * The only exceptions are $near and $nearSphere operators, which are - * constrained to only 1 operator per query. As a result, these ops - * remain at the top level - * - * https://jira.mongodb.org/browse/SERVER-13732 - * https://github.com/parse-community/parse-server/issues/3767 - */ - Object.keys(query).forEach(key => { - const noCollisions = !query.$or.some(subq => - Object.prototype.hasOwnProperty.call(subq, key) - ); - let hasNears = false; - if (query[key] != null && typeof query[key] == 'object') { - hasNears = '$near' in query[key] || '$nearSphere' in query[key]; - } - if (key != '$or' && noCollisions && !hasNears) { - query.$or.forEach(subquery => { - subquery[key] = query[key]; - }); - delete query[key]; - } - }); - query.$or.forEach(el => - validateQuery(el, skipMongoDBServer13732Workaround) - ); - } + query.$or.forEach(validateQuery); } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, @@ -146,9 +87,7 @@ const validateQuery = ( if (query.$and) { if (query.$and instanceof Array) { - query.$and.forEach(el => - validateQuery(el, skipMongoDBServer13732Workaround) - ); + query.$and.forEach(validateQuery); } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, @@ -159,9 +98,7 @@ const validateQuery = ( if (query.$nor) { if (query.$nor instanceof Array && query.$nor.length > 0) { - query.$nor.forEach(el => - validateQuery(el, skipMongoDBServer13732Workaround) - ); + query.$nor.forEach(validateQuery); } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, @@ -487,21 +424,15 @@ class DatabaseController { adapter: StorageAdapter; schemaCache: any; schemaPromise: ?Promise; - skipMongoDBServer13732Workaround: boolean; _transactionalSession: ?any; - constructor( - adapter: StorageAdapter, - schemaCache: any, - skipMongoDBServer13732Workaround: boolean - ) { + constructor(adapter: StorageAdapter, schemaCache: any) { this.adapter = adapter; this.schemaCache = schemaCache; // We don't want a mutable this.schema, because then you could have // one request that uses different schemas for different parts of // it. Instead, use loadSchema to get a schema. this.schemaPromise = null; - this.skipMongoDBServer13732Workaround = skipMongoDBServer13732Workaround; this._transactionalSession = null; } @@ -660,7 +591,7 @@ class DatabaseController { if (acl) { query = addWriteACL(query, acl); } - validateQuery(query, this.skipMongoDBServer13732Workaround); + validateQuery(query); return schemaController .getOneSchema(className, true) .catch(error => { @@ -939,7 +870,7 @@ class DatabaseController { if (acl) { query = addWriteACL(query, acl); } - validateQuery(query, this.skipMongoDBServer13732Workaround); + validateQuery(query); return schemaController .getOneSchema(className) .catch(error => { @@ -1460,7 +1391,7 @@ class DatabaseController { query = addReadACL(query, aclGroup); } } - validateQuery(query, this.skipMongoDBServer13732Workaround); + validateQuery(query); if (count) { if (!classExists) { return 0; @@ -1893,7 +1824,7 @@ class DatabaseController { ]); } - static _validateQuery: (any, boolean) => void; + static _validateQuery: any => void; } module.exports = DatabaseController; diff --git a/src/Controllers/index.js b/src/Controllers/index.js index 8ef76793..d10ad800 100644 --- a/src/Controllers/index.js +++ b/src/Controllers/index.js @@ -171,7 +171,6 @@ export function getDatabaseController( const { databaseURI, databaseOptions, - skipMongoDBServer13732Workaround, collectionPrefix, schemaCacheTTL, enableSingleSchemaCache, @@ -195,8 +194,7 @@ export function getDatabaseController( } return new DatabaseController( databaseAdapter, - new SchemaCache(cacheController, schemaCacheTTL, enableSingleSchemaCache), - skipMongoDBServer13732Workaround + new SchemaCache(cacheController, schemaCacheTTL, enableSingleSchemaCache) ); } diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 4bded75a..72500203 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -371,12 +371,6 @@ module.exports.ParseServerOptions = { help: 'Disables console output', action: parsers.booleanParser, }, - skipMongoDBServer13732Workaround: { - env: 'PARSE_SKIP_MONGODB_SERVER_13732_WORKAROUND', - help: 'Circumvent Parse workaround for historical MongoDB bug SERVER-13732', - action: parsers.booleanParser, - default: false, - }, startLiveQueryServer: { env: 'PARSE_SERVER_START_LIVE_QUERY_SERVER', help: 'Starts the liveQuery server', diff --git a/src/Options/docs.js b/src/Options/docs.js index 098fefb5..9e5b6ac8 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -67,7 +67,6 @@ * @property {String} serverURL URL to your parse server with http:// or https://. * @property {Number} sessionLength Session duration, in seconds, defaults to 1 year * @property {Boolean} silent Disables console output - * @property {Boolean} skipMongoDBServer13732Workaround Circumvent Parse workaround for historical MongoDB bug SERVER-13732 * @property {Boolean} startLiveQueryServer Starts the liveQuery server * @property {String[]} userSensitiveFields Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields * @property {Boolean} verbose Set the logging to verbose diff --git a/src/Options/index.js b/src/Options/index.js index 43922ed3..3aa5b7fc 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -64,10 +64,6 @@ export interface ParseServerOptions { databaseOptions: ?any; /* Adapter module for the database */ databaseAdapter: ?Adapter; - /* Circumvent Parse workaround for historical MongoDB bug SERVER-13732 - :ENV: PARSE_SKIP_MONGODB_SERVER_13732_WORKAROUND - :DEFAULT: false */ - skipMongoDBServer13732Workaround: ?boolean; /* Full path to your cloud code main.js */ cloud: ?string; /* A collection prefix for the classes