Fix for count being very slow on large Parse Classes' collections (Postgres) (#5330)
* Changed count to be approximate. Should help with postgres slowness * refactored last commit to only fall back to estimate if no complex query * handlign variables correctly * Trying again because it was casting to lowercase table names which doesnt work for us/ * syntax error * Adding quotations to pg query * hopefully final pg fix * Postgres will now use an approximate count unless there is a more complex query specified * handling edge case * Fix for count being very slow on large Parse Classes' collections in Postgres. Replicating fix for Mongo in issue 5264 * Fixed silly spelling error resulting from copying over notes * Lint fixes * limiting results to 1 on approximation * suppress test that we can no longer run for postgres * removed tests from Postgres that no longer apply * made changes requested by dplewis * fixed count errors * updated package.json * removed test exclude for pg * removed object types from method * test disabled for postgres * returned type * add estimate count test * fix mongo test
This commit is contained in:
committed by
Diamond Lewis
parent
e396612254
commit
c7eb7daeae
75
package-lock.json
generated
75
package-lock.json
generated
@@ -2402,14 +2402,14 @@
|
|||||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
},
|
},
|
||||||
"cosmiconfig": {
|
"cosmiconfig": {
|
||||||
"version": "5.1.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz",
|
||||||
"integrity": "sha512-kCNPvthka8gvLtzAxQXvWo4FxqRB+ftRZyPZNuab5ngvM9Y7yw7hbEysglptLgpkGX9nAOKTBVkHUAe8xtYR6Q==",
|
"integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"import-fresh": "^2.0.0",
|
"import-fresh": "^2.0.0",
|
||||||
"is-directory": "^0.3.1",
|
"is-directory": "^0.3.1",
|
||||||
"js-yaml": "^3.9.0",
|
"js-yaml": "^3.13.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"parse-json": "^4.0.0"
|
"parse-json": "^4.0.0"
|
||||||
}
|
}
|
||||||
@@ -2947,14 +2947,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es5-ext": {
|
"es5-ext": {
|
||||||
"version": "0.10.48",
|
"version": "0.10.49",
|
||||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.48.tgz",
|
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.49.tgz",
|
||||||
"integrity": "sha512-CdRvPlX/24Mj5L4NVxTs4804sxiS2CjVprgCmrgoDkdmjdY4D+ySHa7K3jJf8R40dFg0tIm3z/dk326LrnuSGw==",
|
"integrity": "sha512-3NMEhi57E31qdzmYp2jwRArIUsj1HI/RxbQ4bgnSB+AIKIxsAmTiK83bYMifIcpWvEc3P1X30DhUKOqEtF/kvg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"es6-iterator": "~2.0.3",
|
"es6-iterator": "~2.0.3",
|
||||||
"es6-symbol": "~3.1.1",
|
"es6-symbol": "~3.1.1",
|
||||||
"next-tick": "1"
|
"next-tick": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es6-iterator": {
|
"es6-iterator": {
|
||||||
@@ -3759,6 +3759,12 @@
|
|||||||
"integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=",
|
"integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"fn-name": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
|
||||||
@@ -4678,6 +4684,32 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.5.5",
|
"ajv": "^6.5.5",
|
||||||
"har-schema": "^2.0.0"
|
"har-schema": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": {
|
||||||
|
"version": "6.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
|
||||||
|
"integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^2.0.1",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fast-deep-equal": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"has-ansi": {
|
"has-ansi": {
|
||||||
@@ -5491,9 +5523,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "3.12.2",
|
"version": "3.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz",
|
||||||
"integrity": "sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==",
|
"integrity": "sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"argparse": "^1.0.7",
|
"argparse": "^1.0.7",
|
||||||
@@ -5692,6 +5724,15 @@
|
|||||||
"graceful-fs": "^4.1.9"
|
"graceful-fs": "^4.1.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lcid": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"invert-kv": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"levn": {
|
"levn": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
|
||||||
@@ -9204,9 +9245,9 @@
|
|||||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
|
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
|
||||||
},
|
},
|
||||||
"simple-git": {
|
"simple-git": {
|
||||||
"version": "1.107.0",
|
"version": "1.110.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.107.0.tgz",
|
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.110.0.tgz",
|
||||||
"integrity": "sha512-t4OK1JRlp4ayKRfcW6owrWcRVLyHRUlhGd0uN6ZZTqfDq8a5XpcUdOKiGRNobHEuMtNqzp0vcJNvhYWwh5PsQA==",
|
"integrity": "sha512-UYY0rQkknk0P5eb+KW+03F4TevZ9ou0H+LoGaj7iiVgpnZH4wdj/HTViy/1tNNkmIPcmtxuBqXWiYt2YwlRKOQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^4.0.1"
|
"debug": "^4.0.1"
|
||||||
@@ -9651,6 +9692,12 @@
|
|||||||
"integrity": "sha512-TyOuWLwkmtPL49LHCX1caIwHjRzcVd62+GF6h8W/jHOeZUFHpnd2XJDVuUlaTaLPH1nuu2M69mfHr5XbQJnf/g==",
|
"integrity": "sha512-TyOuWLwkmtPL49LHCX1caIwHjRzcVd62+GF6h8W/jHOeZUFHpnd2XJDVuUlaTaLPH1nuu2M69mfHr5XbQJnf/g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"synchronous-promise": {
|
||||||
|
"version": "2.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.6.tgz",
|
||||||
|
"integrity": "sha512-TyOuWLwkmtPL49LHCX1caIwHjRzcVd62+GF6h8W/jHOeZUFHpnd2XJDVuUlaTaLPH1nuu2M69mfHr5XbQJnf/g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"table": {
|
"table": {
|
||||||
"version": "5.2.3",
|
"version": "5.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ describe('AudiencesRouter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('query installations with count = 1', done => {
|
it_exclude_dbs(['postgres'])('query installations with count = 1', done => {
|
||||||
const config = Config.get('test');
|
const config = Config.get('test');
|
||||||
const androidAudienceRequest = {
|
const androidAudienceRequest = {
|
||||||
name: 'Android Users',
|
name: 'Android Users',
|
||||||
@@ -189,7 +189,7 @@ describe('AudiencesRouter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('query installations with limit = 0 and count = 1', done => {
|
it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => {
|
||||||
const config = Config.get('test');
|
const config = Config.get('test');
|
||||||
const androidAudienceRequest = {
|
const androidAudienceRequest = {
|
||||||
name: 'Android Users',
|
name: 'Android Users',
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ describe('InstallationsRouter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('query installations with count = 1', done => {
|
it_exclude_dbs(['postgres'])('query installations with count = 1', done => {
|
||||||
const config = Config.get('test');
|
const config = Config.get('test');
|
||||||
const androidDeviceRequest = {
|
const androidDeviceRequest = {
|
||||||
installationId: '12345678-abcd-abcd-abcd-123456789abc',
|
installationId: '12345678-abcd-abcd-abcd-123456789abc',
|
||||||
@@ -209,7 +209,7 @@ describe('InstallationsRouter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('query installations with limit = 0 and count = 1', done => {
|
it_only_db('postgres')('query installations with count = 1', async () => {
|
||||||
const config = Config.get('test');
|
const config = Config.get('test');
|
||||||
const androidDeviceRequest = {
|
const androidDeviceRequest = {
|
||||||
installationId: '12345678-abcd-abcd-abcd-123456789abc',
|
installationId: '12345678-abcd-abcd-abcd-123456789abc',
|
||||||
@@ -224,40 +224,90 @@ describe('InstallationsRouter', () => {
|
|||||||
auth: auth.master(config),
|
auth: auth.master(config),
|
||||||
body: {},
|
body: {},
|
||||||
query: {
|
query: {
|
||||||
limit: 0,
|
|
||||||
count: 1,
|
count: 1,
|
||||||
},
|
},
|
||||||
info: {},
|
info: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = new InstallationsRouter();
|
const router = new InstallationsRouter();
|
||||||
rest
|
await rest.create(
|
||||||
.create(
|
config,
|
||||||
config,
|
auth.nobody(config),
|
||||||
auth.nobody(config),
|
'_Installation',
|
||||||
'_Installation',
|
androidDeviceRequest
|
||||||
androidDeviceRequest
|
);
|
||||||
)
|
await rest.create(
|
||||||
.then(() => {
|
config,
|
||||||
return rest.create(
|
auth.nobody(config),
|
||||||
|
'_Installation',
|
||||||
|
iosDeviceRequest
|
||||||
|
);
|
||||||
|
let res = await router.handleFind(request);
|
||||||
|
let response = res.response;
|
||||||
|
expect(response.results.length).toEqual(2);
|
||||||
|
expect(response.count).toEqual(0); // estimate count is zero
|
||||||
|
|
||||||
|
const pgAdapter = config.database.adapter;
|
||||||
|
await pgAdapter.updateEstimatedCount('_Installation');
|
||||||
|
|
||||||
|
res = await router.handleFind(request);
|
||||||
|
response = res.response;
|
||||||
|
expect(response.results.length).toEqual(2);
|
||||||
|
expect(response.count).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it_exclude_dbs(['postgres'])(
|
||||||
|
'query installations with limit = 0 and count = 1',
|
||||||
|
done => {
|
||||||
|
const config = Config.get('test');
|
||||||
|
const androidDeviceRequest = {
|
||||||
|
installationId: '12345678-abcd-abcd-abcd-123456789abc',
|
||||||
|
deviceType: 'android',
|
||||||
|
};
|
||||||
|
const iosDeviceRequest = {
|
||||||
|
installationId: '12345678-abcd-abcd-abcd-123456789abd',
|
||||||
|
deviceType: 'ios',
|
||||||
|
};
|
||||||
|
const request = {
|
||||||
|
config: config,
|
||||||
|
auth: auth.master(config),
|
||||||
|
body: {},
|
||||||
|
query: {
|
||||||
|
limit: 0,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
info: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = new InstallationsRouter();
|
||||||
|
rest
|
||||||
|
.create(
|
||||||
config,
|
config,
|
||||||
auth.nobody(config),
|
auth.nobody(config),
|
||||||
'_Installation',
|
'_Installation',
|
||||||
iosDeviceRequest
|
androidDeviceRequest
|
||||||
);
|
)
|
||||||
})
|
.then(() => {
|
||||||
.then(() => {
|
return rest.create(
|
||||||
return router.handleFind(request);
|
config,
|
||||||
})
|
auth.nobody(config),
|
||||||
.then(res => {
|
'_Installation',
|
||||||
const response = res.response;
|
iosDeviceRequest
|
||||||
expect(response.results.length).toEqual(0);
|
);
|
||||||
expect(response.count).toEqual(2);
|
})
|
||||||
done();
|
.then(() => {
|
||||||
})
|
return router.handleFind(request);
|
||||||
.catch(err => {
|
})
|
||||||
fail(JSON.stringify(err));
|
.then(res => {
|
||||||
done();
|
const response = res.response;
|
||||||
});
|
expect(response.results.length).toEqual(0);
|
||||||
});
|
expect(response.count).toEqual(2);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
fail(JSON.stringify(err));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -152,10 +152,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
// encoded
|
// encoded
|
||||||
const encodedUri = formatUrl(parseUrl(this._uri));
|
const encodedUri = formatUrl(parseUrl(this._uri));
|
||||||
|
|
||||||
this.connectionPromise = MongoClient.connect(
|
this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions)
|
||||||
encodedUri,
|
|
||||||
this._mongoOptions
|
|
||||||
)
|
|
||||||
.then(client => {
|
.then(client => {
|
||||||
// Starting mongoDB 3.0, the MongoClient.connect don't return a DB anymore but a client
|
// Starting mongoDB 3.0, the MongoClient.connect don't return a DB anymore but a client
|
||||||
// Fortunately, we can get back the options and use them to select the proper DB.
|
// Fortunately, we can get back the options and use them to select the proper DB.
|
||||||
@@ -385,8 +382,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
deleteAllClasses(fast: boolean) {
|
deleteAllClasses(fast: boolean) {
|
||||||
return storageAdapterAllCollections(this).then(collections =>
|
return storageAdapterAllCollections(this).then(collections =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
collections.map(
|
collections.map(collection =>
|
||||||
collection => (fast ? collection.deleteMany({}) : collection.drop())
|
fast ? collection.deleteMany({}) : collection.drop()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -952,6 +949,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
readPreference = ReadPreference.NEAREST;
|
readPreference = ReadPreference.NEAREST;
|
||||||
break;
|
break;
|
||||||
case undefined:
|
case undefined:
|
||||||
|
case null:
|
||||||
|
case '':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
|
|||||||
@@ -1962,17 +1962,37 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Executes a count.
|
// Executes a count.
|
||||||
count(className: string, schema: SchemaType, query: QueryType) {
|
count(
|
||||||
debug('count', className, query);
|
className: string,
|
||||||
|
schema: SchemaType,
|
||||||
|
query: QueryType,
|
||||||
|
readPreference?: string,
|
||||||
|
estimate?: boolean = true
|
||||||
|
) {
|
||||||
|
debug('count', className, query, readPreference, estimate);
|
||||||
const values = [className];
|
const values = [className];
|
||||||
const where = buildWhereClause({ schema, query, index: 2 });
|
const where = buildWhereClause({ schema, query, index: 2 });
|
||||||
values.push(...where.values);
|
values.push(...where.values);
|
||||||
|
|
||||||
const wherePattern =
|
const wherePattern =
|
||||||
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
|
||||||
const qs = `SELECT count(*) FROM $1:name ${wherePattern}`;
|
let qs = '';
|
||||||
|
|
||||||
|
if (where.pattern.length > 0 || !estimate) {
|
||||||
|
qs = `SELECT count(*) FROM $1:name ${wherePattern}`;
|
||||||
|
} else {
|
||||||
|
qs =
|
||||||
|
'SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = $1';
|
||||||
|
}
|
||||||
|
|
||||||
return this._client
|
return this._client
|
||||||
.one(qs, values, a => +a.count)
|
.one(qs, values, a => {
|
||||||
|
if (a.approximate_row_count != null) {
|
||||||
|
return +a.approximate_row_count;
|
||||||
|
} else {
|
||||||
|
return +a.count;
|
||||||
|
}
|
||||||
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (error.code !== PostgresRelationDoesNotExistError) {
|
if (error.code !== PostgresRelationDoesNotExistError) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -2327,6 +2347,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
|||||||
updateSchemaWithIndexes(): Promise<void> {
|
updateSchemaWithIndexes(): Promise<void> {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for testing purposes
|
||||||
|
updateEstimatedCount(className: string) {
|
||||||
|
return this._client.none('ANALYZE $1:name', [className]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertPolygonToSQL(polygon) {
|
function convertPolygonToSQL(polygon) {
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ export interface StorageAdapter {
|
|||||||
className: string,
|
className: string,
|
||||||
schema: SchemaType,
|
schema: SchemaType,
|
||||||
query: QueryType,
|
query: QueryType,
|
||||||
readPreference: ?string
|
readPreference?: string,
|
||||||
|
estimate?: boolean
|
||||||
): Promise<number>;
|
): Promise<number>;
|
||||||
distinct(
|
distinct(
|
||||||
className: string,
|
className: string,
|
||||||
|
|||||||
@@ -1324,7 +1324,9 @@ class DatabaseController {
|
|||||||
})
|
})
|
||||||
.then((schema: any) => {
|
.then((schema: any) => {
|
||||||
return this.collectionExists(className)
|
return this.collectionExists(className)
|
||||||
.then(() => this.adapter.count(className, { fields: {} }))
|
.then(() =>
|
||||||
|
this.adapter.count(className, { fields: {} }, null, '', false)
|
||||||
|
)
|
||||||
.then(count => {
|
.then(count => {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
|
|||||||
Reference in New Issue
Block a user