2017-12-30 20:44:18 -05:00
|
|
|
|
// @flow
|
|
|
|
|
|
// A database adapter that works with data exported from the hosted
|
2016-01-28 10:58:12 -08:00
|
|
|
|
// Parse database.
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// @flow-disable-next
|
2018-09-01 13:58:06 -04:00
|
|
|
|
import { Parse } from 'parse/node';
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// @flow-disable-next
|
2018-09-01 13:58:06 -04:00
|
|
|
|
import _ from 'lodash';
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// @flow-disable-next
|
2018-09-01 13:58:06 -04:00
|
|
|
|
import intersect from 'intersect';
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// @flow-disable-next
|
2018-09-01 13:58:06 -04:00
|
|
|
|
import deepcopy from 'deepcopy';
|
|
|
|
|
|
import logger from '../logger';
|
|
|
|
|
|
import * as SchemaController from './SchemaController';
|
|
|
|
|
|
import { StorageAdapter } from '../Adapters/Storage/StorageAdapter';
|
|
|
|
|
|
import type {
|
|
|
|
|
|
QueryOptions,
|
|
|
|
|
|
FullQueryOptions,
|
|
|
|
|
|
} from '../Adapters/Storage/StorageAdapter';
|
2016-01-28 10:58:12 -08:00
|
|
|
|
|
2016-04-22 18:44:03 -07:00
|
|
|
|
function addWriteACL(query, acl) {
|
2016-12-07 15:17:05 -08:00
|
|
|
|
const newQuery = _.cloneDeep(query);
|
2016-04-22 18:44:03 -07:00
|
|
|
|
//Can't be any existing '_wperm' query, we don't allow client queries on that, no need to $and
|
2018-09-01 13:58:06 -04:00
|
|
|
|
newQuery._wperm = { $in: [null, ...acl] };
|
2016-04-22 18:44:03 -07:00
|
|
|
|
return newQuery;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function addReadACL(query, acl) {
|
2016-12-07 15:17:05 -08:00
|
|
|
|
const newQuery = _.cloneDeep(query);
|
2016-04-22 18:44:03 -07:00
|
|
|
|
//Can't be any existing '_rperm' query, we don't allow client queries on that, no need to $and
|
2018-09-01 13:58:06 -04:00
|
|
|
|
newQuery._rperm = { $in: [null, '*', ...acl] };
|
2016-04-22 18:44:03 -07:00
|
|
|
|
return newQuery;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-06-12 13:39:41 -07:00
|
|
|
|
// Transforms a REST API formatted ACL object to our two-field mongo format.
|
|
|
|
|
|
const transformObjectACL = ({ ACL, ...result }) => {
|
|
|
|
|
|
if (!ACL) {
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result._wperm = [];
|
|
|
|
|
|
result._rperm = [];
|
|
|
|
|
|
|
2016-12-07 15:17:05 -08:00
|
|
|
|
for (const entry in ACL) {
|
2016-06-12 13:39:41 -07:00
|
|
|
|
if (ACL[entry].read) {
|
|
|
|
|
|
result._rperm.push(entry);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ACL[entry].write) {
|
|
|
|
|
|
result._wperm.push(entry);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
2018-09-01 13:58:06 -04:00
|
|
|
|
};
|
2016-06-12 13:39:41 -07:00
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const specialQuerykeys = [
|
|
|
|
|
|
'$and',
|
|
|
|
|
|
'$or',
|
|
|
|
|
|
'$nor',
|
|
|
|
|
|
'_rperm',
|
|
|
|
|
|
'_wperm',
|
|
|
|
|
|
'_perishable_token',
|
|
|
|
|
|
'_email_verify_token',
|
|
|
|
|
|
'_email_verify_token_expires_at',
|
|
|
|
|
|
'_account_lockout_expires_at',
|
|
|
|
|
|
'_failed_login_count',
|
|
|
|
|
|
];
|
2016-09-24 13:53:15 -04:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const isSpecialQueryKey = (key) => {
|
2016-09-24 13:53:15 -04:00
|
|
|
|
return specialQuerykeys.indexOf(key) >= 0;
|
2018-09-01 13:58:06 -04:00
|
|
|
|
};
|
2016-09-24 13:53:15 -04:00
|
|
|
|
|
2020-02-27 10:56:14 -08:00
|
|
|
|
const validateQuery = (query: any): void => {
|
2016-05-18 18:10:57 -07:00
|
|
|
|
if (query.ACL) {
|
|
|
|
|
|
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (query.$or) {
|
|
|
|
|
|
if (query.$or instanceof Array) {
|
2020-02-27 10:56:14 -08:00
|
|
|
|
query.$or.forEach(validateQuery);
|
2016-05-18 18:10:57 -07:00
|
|
|
|
} else {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_QUERY,
|
|
|
|
|
|
'Bad $or format - use an array value.'
|
|
|
|
|
|
);
|
2016-05-18 18:10:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (query.$and) {
|
|
|
|
|
|
if (query.$and instanceof Array) {
|
2020-02-27 10:56:14 -08:00
|
|
|
|
query.$and.forEach(validateQuery);
|
2016-05-18 18:10:57 -07:00
|
|
|
|
} else {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_QUERY,
|
|
|
|
|
|
'Bad $and format - use an array value.'
|
|
|
|
|
|
);
|
2016-05-18 18:10:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-18 15:26:33 -04:00
|
|
|
|
if (query.$nor) {
|
|
|
|
|
|
if (query.$nor instanceof Array && query.$nor.length > 0) {
|
2020-02-27 10:56:14 -08:00
|
|
|
|
query.$nor.forEach(validateQuery);
|
2018-05-18 15:26:33 -04:00
|
|
|
|
} else {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_QUERY,
|
|
|
|
|
|
'Bad $nor format - use an array of at least 1 value.'
|
|
|
|
|
|
);
|
2018-05-18 15:26:33 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
Object.keys(query).forEach((key) => {
|
2016-06-13 12:57:20 -07:00
|
|
|
|
if (query && query[key] && query[key].$regex) {
|
|
|
|
|
|
if (typeof query[key].$options === 'string') {
|
2016-06-13 01:14:26 -07:00
|
|
|
|
if (!query[key].$options.match(/^[imxs]+$/)) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_QUERY,
|
|
|
|
|
|
`Bad $options value for query: ${query[key].$options}`
|
|
|
|
|
|
);
|
2016-06-13 01:14:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-09-24 13:53:15 -04:00
|
|
|
|
if (!isSpecialQueryKey(key) && !key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/)) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_KEY_NAME,
|
|
|
|
|
|
`Invalid key name: ${key}`
|
|
|
|
|
|
);
|
2016-05-18 18:10:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
|
};
|
2016-05-18 18:10:57 -07:00
|
|
|
|
|
2016-01-28 10:58:12 -08:00
|
|
|
|
// Filters out any data that shouldn't be on this REST-formatted object.
|
2019-01-29 08:52:49 +00:00
|
|
|
|
const filterSensitiveData = (
|
2019-08-22 21:01:50 +02:00
|
|
|
|
isMaster: boolean,
|
|
|
|
|
|
aclGroup: any[],
|
|
|
|
|
|
auth: any,
|
|
|
|
|
|
operation: any,
|
|
|
|
|
|
schema: SchemaController.SchemaController,
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
protectedFields: null | Array<any>,
|
|
|
|
|
|
object: any
|
2019-01-29 08:52:49 +00:00
|
|
|
|
) => {
|
2019-08-22 21:01:50 +02:00
|
|
|
|
let userId = null;
|
|
|
|
|
|
if (auth && auth.user) userId = auth.user.id;
|
|
|
|
|
|
|
|
|
|
|
|
// replace protectedFields when using pointer-permissions
|
|
|
|
|
|
const perms = schema.getClassLevelPermissions(className);
|
|
|
|
|
|
if (perms) {
|
|
|
|
|
|
const isReadOperation = ['get', 'find'].indexOf(operation) > -1;
|
|
|
|
|
|
|
|
|
|
|
|
if (isReadOperation && perms.protectedFields) {
|
|
|
|
|
|
// extract protectedFields added with the pointer-permission prefix
|
|
|
|
|
|
const protectedFieldsPointerPerm = Object.keys(perms.protectedFields)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.filter((key) => key.startsWith('userField:'))
|
|
|
|
|
|
.map((key) => {
|
2019-08-22 21:01:50 +02:00
|
|
|
|
return { key: key.substring(10), value: perms.protectedFields[key] };
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2020-02-19 12:34:08 +03:00
|
|
|
|
const newProtectedFields: Array<string>[] = [];
|
2019-08-22 21:01:50 +02:00
|
|
|
|
let overrideProtectedFields = false;
|
|
|
|
|
|
|
|
|
|
|
|
// check if the object grants the current user access based on the extracted fields
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
protectedFieldsPointerPerm.forEach((pointerPerm) => {
|
2019-08-22 21:01:50 +02:00
|
|
|
|
let pointerPermIncludesUser = false;
|
|
|
|
|
|
const readUserFieldValue = object[pointerPerm.key];
|
|
|
|
|
|
if (readUserFieldValue) {
|
|
|
|
|
|
if (Array.isArray(readUserFieldValue)) {
|
|
|
|
|
|
pointerPermIncludesUser = readUserFieldValue.some(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(user) => user.objectId && user.objectId === userId
|
2019-08-22 21:01:50 +02:00
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
pointerPermIncludesUser =
|
|
|
|
|
|
readUserFieldValue.objectId &&
|
|
|
|
|
|
readUserFieldValue.objectId === userId;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (pointerPermIncludesUser) {
|
|
|
|
|
|
overrideProtectedFields = true;
|
2020-02-19 12:34:08 +03:00
|
|
|
|
newProtectedFields.push(pointerPerm.value);
|
2019-08-22 21:01:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2019-01-29 08:52:49 +00:00
|
|
|
|
|
2020-02-19 12:34:08 +03:00
|
|
|
|
// if at least one pointer-permission affected the current user
|
|
|
|
|
|
// intersect vs protectedFields from previous stage (@see addProtectedFields)
|
|
|
|
|
|
// Sets theory (intersections): A x (B x C) == (A x B) x C
|
|
|
|
|
|
if (overrideProtectedFields && protectedFields) {
|
|
|
|
|
|
newProtectedFields.push(protectedFields);
|
|
|
|
|
|
}
|
|
|
|
|
|
// intersect all sets of protectedFields
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
newProtectedFields.forEach((fields) => {
|
2020-02-19 12:34:08 +03:00
|
|
|
|
if (fields) {
|
|
|
|
|
|
// if there're no protctedFields by other criteria ( id / role / auth)
|
|
|
|
|
|
// then we must intersect each set (per userField)
|
|
|
|
|
|
if (!protectedFields) {
|
|
|
|
|
|
protectedFields = fields;
|
|
|
|
|
|
} else {
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
protectedFields = protectedFields.filter((v) => fields.includes(v));
|
2020-02-19 12:34:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2019-08-22 21:01:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const isUserClass = className === '_User';
|
|
|
|
|
|
|
|
|
|
|
|
/* special treat for the user class: don't filter protectedFields if currently loggedin user is
|
|
|
|
|
|
the retrieved user */
|
2020-02-19 12:34:08 +03:00
|
|
|
|
if (!(isUserClass && userId && object.objectId === userId)) {
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
protectedFields && protectedFields.forEach((k) => delete object[k]);
|
2019-08-22 21:01:50 +02:00
|
|
|
|
|
2020-02-19 12:34:08 +03:00
|
|
|
|
// fields not requested by client (excluded),
|
|
|
|
|
|
//but were needed to apply protecttedFields
|
|
|
|
|
|
perms.protectedFields &&
|
|
|
|
|
|
perms.protectedFields.temporaryKeys &&
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
perms.protectedFields.temporaryKeys.forEach((k) => delete object[k]);
|
2020-02-19 12:34:08 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-08-22 21:01:50 +02:00
|
|
|
|
if (!isUserClass) {
|
2016-01-28 10:58:12 -08:00
|
|
|
|
return object;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-05-27 19:41:09 -07:00
|
|
|
|
object.password = object._hashed_password;
|
|
|
|
|
|
delete object._hashed_password;
|
|
|
|
|
|
|
2016-04-14 14:50:16 -07:00
|
|
|
|
delete object.sessionToken;
|
|
|
|
|
|
|
2016-08-15 16:48:39 -04:00
|
|
|
|
if (isMaster) {
|
2016-01-28 10:58:12 -08:00
|
|
|
|
return object;
|
|
|
|
|
|
}
|
2016-08-15 16:48:39 -04:00
|
|
|
|
delete object._email_verify_token;
|
|
|
|
|
|
delete object._perishable_token;
|
2016-11-17 22:07:51 +05:30
|
|
|
|
delete object._perishable_token_expires_at;
|
2016-08-15 16:48:39 -04:00
|
|
|
|
delete object._tombstone;
|
|
|
|
|
|
delete object._email_verify_token_expires_at;
|
2016-09-02 17:00:47 -07:00
|
|
|
|
delete object._failed_login_count;
|
|
|
|
|
|
delete object._account_lockout_expires_at;
|
2016-11-21 21:16:38 +05:30
|
|
|
|
delete object._password_changed_at;
|
2018-04-11 01:54:35 +08:00
|
|
|
|
delete object._password_history;
|
2016-01-28 10:58:12 -08:00
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
|
if (aclGroup.indexOf(object.objectId) > -1) {
|
2016-08-15 16:48:39 -04:00
|
|
|
|
return object;
|
|
|
|
|
|
}
|
2016-04-20 15:42:18 -07:00
|
|
|
|
delete object.authData;
|
2016-01-28 10:58:12 -08:00
|
|
|
|
return object;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
import type { LoadSchemaOptions } from './types';
|
|
|
|
|
|
|
2016-01-28 10:58:12 -08:00
|
|
|
|
// Runs an update on the database.
|
|
|
|
|
|
// Returns a promise for an object with the new values for field
|
|
|
|
|
|
// modifications that don't know their results ahead of time, like
|
|
|
|
|
|
// 'increment'.
|
|
|
|
|
|
// Options:
|
|
|
|
|
|
// acl: a list of strings. If the object to be updated has an ACL,
|
|
|
|
|
|
// one of the provided strings must provide the caller with
|
|
|
|
|
|
// write permissions.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const specialKeysForUpdate = [
|
|
|
|
|
|
'_hashed_password',
|
|
|
|
|
|
'_perishable_token',
|
|
|
|
|
|
'_email_verify_token',
|
|
|
|
|
|
'_email_verify_token_expires_at',
|
|
|
|
|
|
'_account_lockout_expires_at',
|
|
|
|
|
|
'_failed_login_count',
|
|
|
|
|
|
'_perishable_token_expires_at',
|
|
|
|
|
|
'_password_changed_at',
|
|
|
|
|
|
'_password_history',
|
|
|
|
|
|
];
|
2016-09-24 13:53:15 -04:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const isSpecialUpdateKey = (key) => {
|
2016-09-24 13:53:15 -04:00
|
|
|
|
return specialKeysForUpdate.indexOf(key) >= 0;
|
2018-09-01 13:58:06 -04:00
|
|
|
|
};
|
2016-09-24 13:53:15 -04:00
|
|
|
|
|
2017-09-18 15:01:07 -04:00
|
|
|
|
function expandResultOnKeyPath(object, key, value) {
|
|
|
|
|
|
if (key.indexOf('.') < 0) {
|
|
|
|
|
|
object[key] = value[key];
|
|
|
|
|
|
return object;
|
|
|
|
|
|
}
|
|
|
|
|
|
const path = key.split('.');
|
|
|
|
|
|
const firstKey = path[0];
|
|
|
|
|
|
const nextPath = path.slice(1).join('.');
|
2018-09-01 13:58:06 -04:00
|
|
|
|
object[firstKey] = expandResultOnKeyPath(
|
|
|
|
|
|
object[firstKey] || {},
|
|
|
|
|
|
nextPath,
|
|
|
|
|
|
value[firstKey]
|
|
|
|
|
|
);
|
2017-09-18 15:01:07 -04:00
|
|
|
|
delete object[key];
|
|
|
|
|
|
return object;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
function sanitizeDatabaseResult(originalObject, result): Promise<any> {
|
2016-12-07 15:17:05 -08:00
|
|
|
|
const response = {};
|
2016-03-16 23:48:52 -04:00
|
|
|
|
if (!result) {
|
|
|
|
|
|
return Promise.resolve(response);
|
|
|
|
|
|
}
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
Object.keys(originalObject).forEach((key) => {
|
2016-12-07 15:17:05 -08:00
|
|
|
|
const keyUpdate = originalObject[key];
|
2016-03-16 23:48:52 -04:00
|
|
|
|
// determine if that was an op
|
2018-09-01 13:58:06 -04:00
|
|
|
|
if (
|
|
|
|
|
|
keyUpdate &&
|
|
|
|
|
|
typeof keyUpdate === 'object' &&
|
|
|
|
|
|
keyUpdate.__op &&
|
|
|
|
|
|
['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1
|
|
|
|
|
|
) {
|
2016-03-16 23:48:52 -04:00
|
|
|
|
// only valid ops that produce an actionable result
|
2017-09-18 15:01:07 -04:00
|
|
|
|
// the op may have happend on a keypath
|
|
|
|
|
|
expandResultOnKeyPath(response, key, result);
|
2016-03-16 23:48:52 -04:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return Promise.resolve(response);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
function joinTableName(className, key) {
|
|
|
|
|
|
return `_Join:${key}:${className}`;
|
2017-05-22 12:34:00 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const flattenUpdateOperatorsForCreate = (object) => {
|
2016-12-07 15:17:05 -08:00
|
|
|
|
for (const key in object) {
|
2016-06-17 09:59:16 -07:00
|
|
|
|
if (object[key] && object[key].__op) {
|
|
|
|
|
|
switch (object[key].__op) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
case 'Increment':
|
|
|
|
|
|
if (typeof object[key].amount !== 'number') {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_JSON,
|
|
|
|
|
|
'objects to add must be an array'
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
object[key] = object[key].amount;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'Add':
|
|
|
|
|
|
if (!(object[key].objects instanceof Array)) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_JSON,
|
|
|
|
|
|
'objects to add must be an array'
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
object[key] = object[key].objects;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'AddUnique':
|
|
|
|
|
|
if (!(object[key].objects instanceof Array)) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_JSON,
|
|
|
|
|
|
'objects to add must be an array'
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
object[key] = object[key].objects;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'Remove':
|
|
|
|
|
|
if (!(object[key].objects instanceof Array)) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_JSON,
|
|
|
|
|
|
'objects to add must be an array'
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
object[key] = [];
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'Delete':
|
|
|
|
|
|
delete object[key];
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.COMMAND_UNAVAILABLE,
|
|
|
|
|
|
`The ${object[key].__op} operator is not supported yet.`
|
|
|
|
|
|
);
|
2016-06-17 09:59:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
|
};
|
2016-06-17 09:59:16 -07:00
|
|
|
|
|
|
|
|
|
|
const transformAuthData = (className, object, schema) => {
|
|
|
|
|
|
if (object.authData && className === '_User') {
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
Object.keys(object.authData).forEach((provider) => {
|
2016-06-17 09:59:16 -07:00
|
|
|
|
const providerData = object.authData[provider];
|
|
|
|
|
|
const fieldName = `_auth_data_${provider}`;
|
|
|
|
|
|
if (providerData == null) {
|
|
|
|
|
|
object[fieldName] = {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
__op: 'Delete',
|
|
|
|
|
|
};
|
2016-06-17 09:59:16 -07:00
|
|
|
|
} else {
|
|
|
|
|
|
object[fieldName] = providerData;
|
2018-09-01 13:58:06 -04:00
|
|
|
|
schema.fields[fieldName] = { type: 'Object' };
|
2016-06-17 09:59:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
delete object.authData;
|
|
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
|
};
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Transforms a Database format ACL to a REST API format ACL
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const untransformObjectACL = ({ _rperm, _wperm, ...output }) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (_rperm || _wperm) {
|
|
|
|
|
|
output.ACL = {};
|
2016-06-17 09:59:16 -07:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(_rperm || []).forEach((entry) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (!output.ACL[entry]) {
|
|
|
|
|
|
output.ACL[entry] = { read: true };
|
|
|
|
|
|
} else {
|
|
|
|
|
|
output.ACL[entry]['read'] = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2016-01-28 10:58:12 -08:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(_wperm || []).forEach((entry) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (!output.ACL[entry]) {
|
|
|
|
|
|
output.ACL[entry] = { write: true };
|
|
|
|
|
|
} else {
|
|
|
|
|
|
output.ACL[entry]['write'] = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2016-03-07 23:07:24 -05:00
|
|
|
|
}
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return output;
|
2018-09-01 13:58:06 -04:00
|
|
|
|
};
|
2016-03-07 23:07:24 -05:00
|
|
|
|
|
2018-06-07 15:47:18 -07:00
|
|
|
|
/**
|
2018-06-07 16:27:11 -07:00
|
|
|
|
* When querying, the fieldName may be compound, extract the root fieldName
|
2018-06-07 15:47:18 -07:00
|
|
|
|
* `temperature.celsius` becomes `temperature`
|
|
|
|
|
|
* @param {string} fieldName that may be a compound field name
|
2018-06-07 16:27:11 -07:00
|
|
|
|
* @returns {string} the root name of the field
|
2018-06-07 15:47:18 -07:00
|
|
|
|
*/
|
2018-06-07 16:27:11 -07:00
|
|
|
|
const getRootFieldName = (fieldName: string): string => {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return fieldName.split('.')[0];
|
|
|
|
|
|
};
|
2018-06-07 15:47:18 -07:00
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const relationSchema = {
|
|
|
|
|
|
fields: { relatedId: { type: 'String' }, owningId: { type: 'String' } },
|
|
|
|
|
|
};
|
2016-01-28 10:58:12 -08:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
class DatabaseController {
|
|
|
|
|
|
adapter: StorageAdapter;
|
|
|
|
|
|
schemaCache: any;
|
|
|
|
|
|
schemaPromise: ?Promise<SchemaController.SchemaController>;
|
2019-07-31 02:41:07 -07:00
|
|
|
|
_transactionalSession: ?any;
|
2017-12-30 20:44:18 -05:00
|
|
|
|
|
2020-02-27 10:56:14 -08:00
|
|
|
|
constructor(adapter: StorageAdapter, schemaCache: any) {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
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;
|
2019-07-31 02:41:07 -07:00
|
|
|
|
this._transactionalSession = null;
|
2017-11-14 14:46:51 -05:00
|
|
|
|
}
|
2016-01-28 10:58:12 -08:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
collectionExists(className: string): Promise<boolean> {
|
|
|
|
|
|
return this.adapter.classExists(className);
|
|
|
|
|
|
}
|
2016-01-28 10:58:12 -08:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
purgeCollection(className: string): Promise<void> {
|
|
|
|
|
|
return this.loadSchema()
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((schemaController) => schemaController.getOneSchema(className))
|
|
|
|
|
|
.then((schema) =>
|
|
|
|
|
|
this.adapter.deleteObjectsByQuery(className, schema, {})
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2016-03-07 19:26:40 -08:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
validateClassName(className: string): Promise<void> {
|
|
|
|
|
|
if (!SchemaController.classNameIsValid(className)) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return Promise.reject(
|
|
|
|
|
|
new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_CLASS_NAME,
|
|
|
|
|
|
'invalid className: ' + className
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve();
|
2016-03-02 14:33:51 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Returns a promise for a schemaController.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
loadSchema(
|
|
|
|
|
|
options: LoadSchemaOptions = { clearCache: false }
|
|
|
|
|
|
): Promise<SchemaController.SchemaController> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (this.schemaPromise != null) {
|
|
|
|
|
|
return this.schemaPromise;
|
2017-06-21 09:23:20 -03:00
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
|
this.schemaPromise = SchemaController.load(
|
|
|
|
|
|
this.adapter,
|
|
|
|
|
|
this.schemaCache,
|
|
|
|
|
|
options
|
|
|
|
|
|
);
|
|
|
|
|
|
this.schemaPromise.then(
|
|
|
|
|
|
() => delete this.schemaPromise,
|
|
|
|
|
|
() => delete this.schemaPromise
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return this.loadSchema(options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-30 11:14:05 -05:00
|
|
|
|
loadSchemaIfNeeded(
|
|
|
|
|
|
schemaController: SchemaController.SchemaController,
|
|
|
|
|
|
options: LoadSchemaOptions = { clearCache: false }
|
|
|
|
|
|
): Promise<SchemaController.SchemaController> {
|
|
|
|
|
|
return schemaController
|
|
|
|
|
|
? Promise.resolve(schemaController)
|
|
|
|
|
|
: this.loadSchema(options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Returns a promise for the classname that is related to the given
|
|
|
|
|
|
// classname through the key.
|
|
|
|
|
|
// TODO: make this not in the DatabaseController interface
|
|
|
|
|
|
redirectClassNameForKey(className: string, key: string): Promise<?string> {
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
return this.loadSchema().then((schema) => {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
var t = schema.getExpectedType(className, key);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (t != null && typeof t !== 'string' && t.type === 'Relation') {
|
|
|
|
|
|
return t.targetClass;
|
|
|
|
|
|
}
|
|
|
|
|
|
return className;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Uses the schema to validate the object (REST API format).
|
|
|
|
|
|
// Returns a promise that resolves to the new schema.
|
|
|
|
|
|
// This does not update this.schema, because in a situation like a
|
|
|
|
|
|
// batch request, that could confuse other users of the schema.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
validateObject(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
object: any,
|
|
|
|
|
|
query: any,
|
2020-01-28 09:21:30 +03:00
|
|
|
|
runOptions: QueryOptions
|
2018-09-01 13:58:06 -04:00
|
|
|
|
): Promise<boolean> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
let schema;
|
2020-01-28 09:21:30 +03:00
|
|
|
|
const acl = runOptions.acl;
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const isMaster = acl === undefined;
|
2018-09-01 13:58:06 -04:00
|
|
|
|
var aclGroup: string[] = acl || [];
|
|
|
|
|
|
return this.loadSchema()
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((s) => {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
schema = s;
|
|
|
|
|
|
if (isMaster) {
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
2020-01-28 09:21:30 +03:00
|
|
|
|
return this.canAddField(
|
|
|
|
|
|
schema,
|
|
|
|
|
|
className,
|
|
|
|
|
|
object,
|
|
|
|
|
|
aclGroup,
|
|
|
|
|
|
runOptions
|
|
|
|
|
|
);
|
2018-09-01 13:58:06 -04:00
|
|
|
|
})
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
return schema.validateObject(className, object, query);
|
|
|
|
|
|
});
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
|
update(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
query: any,
|
|
|
|
|
|
update: any,
|
2020-01-28 09:21:30 +03:00
|
|
|
|
{ acl, many, upsert, addsField }: FullQueryOptions = {},
|
2019-05-11 10:37:27 -07:00
|
|
|
|
skipSanitization: boolean = false,
|
2019-05-30 11:14:05 -05:00
|
|
|
|
validateOnly: boolean = false,
|
|
|
|
|
|
validSchemaController: SchemaController.SchemaController
|
2018-09-01 13:58:06 -04:00
|
|
|
|
): Promise<any> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const originalQuery = query;
|
|
|
|
|
|
const originalUpdate = update;
|
|
|
|
|
|
// Make a copy of the object, so we don't mutate the incoming data.
|
|
|
|
|
|
update = deepcopy(update);
|
|
|
|
|
|
var relationUpdates = [];
|
|
|
|
|
|
var isMaster = acl === undefined;
|
|
|
|
|
|
var aclGroup = acl || [];
|
2019-05-30 11:14:05 -05:00
|
|
|
|
|
|
|
|
|
|
return this.loadSchemaIfNeeded(validSchemaController).then(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(schemaController) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
return (isMaster
|
|
|
|
|
|
? Promise.resolve()
|
|
|
|
|
|
: schemaController.validatePermission(className, aclGroup, 'update')
|
|
|
|
|
|
)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
relationUpdates = this.collectRelationUpdates(
|
2018-09-01 13:58:06 -04:00
|
|
|
|
className,
|
2019-05-30 11:14:05 -05:00
|
|
|
|
originalQuery.objectId,
|
|
|
|
|
|
update
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (!isMaster) {
|
|
|
|
|
|
query = this.addPointerPermissions(
|
|
|
|
|
|
schemaController,
|
|
|
|
|
|
className,
|
|
|
|
|
|
'update',
|
|
|
|
|
|
query,
|
|
|
|
|
|
aclGroup
|
|
|
|
|
|
);
|
2020-01-28 09:21:30 +03:00
|
|
|
|
|
|
|
|
|
|
if (addsField) {
|
|
|
|
|
|
query = {
|
|
|
|
|
|
$and: [
|
|
|
|
|
|
query,
|
|
|
|
|
|
this.addPointerPermissions(
|
|
|
|
|
|
schemaController,
|
|
|
|
|
|
className,
|
|
|
|
|
|
'addField',
|
|
|
|
|
|
query,
|
|
|
|
|
|
aclGroup
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
if (!query) {
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
|
|
|
|
|
if (acl) {
|
|
|
|
|
|
query = addWriteACL(query, acl);
|
|
|
|
|
|
}
|
2020-02-27 10:56:14 -08:00
|
|
|
|
validateQuery(query);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
return schemaController
|
|
|
|
|
|
.getOneSchema(className, true)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
// If the schema doesn't exist, pretend it exists with no fields. This behavior
|
|
|
|
|
|
// will likely need revisiting.
|
|
|
|
|
|
if (error === undefined) {
|
|
|
|
|
|
return { fields: {} };
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
throw error;
|
|
|
|
|
|
})
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((schema) => {
|
|
|
|
|
|
Object.keys(update).forEach((fieldName) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_KEY_NAME,
|
|
|
|
|
|
`Invalid field name for update: ${fieldName}`
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
const rootFieldName = getRootFieldName(fieldName);
|
|
|
|
|
|
if (
|
|
|
|
|
|
!SchemaController.fieldNameIsValid(rootFieldName) &&
|
|
|
|
|
|
!isSpecialUpdateKey(rootFieldName)
|
|
|
|
|
|
) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_KEY_NAME,
|
|
|
|
|
|
`Invalid field name for update: ${fieldName}`
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
for (const updateOperation in update) {
|
|
|
|
|
|
if (
|
|
|
|
|
|
update[updateOperation] &&
|
|
|
|
|
|
typeof update[updateOperation] === 'object' &&
|
|
|
|
|
|
Object.keys(update[updateOperation]).some(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(innerKey) =>
|
2019-05-30 11:14:05 -05:00
|
|
|
|
innerKey.includes('$') || innerKey.includes('.')
|
|
|
|
|
|
)
|
|
|
|
|
|
) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_NESTED_KEY,
|
|
|
|
|
|
"Nested keys should not contain the '$' or '.' characters"
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
update = transformObjectACL(update);
|
|
|
|
|
|
transformAuthData(className, update, schema);
|
|
|
|
|
|
if (validateOnly) {
|
|
|
|
|
|
return this.adapter
|
|
|
|
|
|
.find(className, schema, query, {})
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((result) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (!result || !result.length) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
|
|
|
|
|
'Object not found.'
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
return {};
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (many) {
|
|
|
|
|
|
return this.adapter.updateObjectsByQuery(
|
|
|
|
|
|
className,
|
|
|
|
|
|
schema,
|
|
|
|
|
|
query,
|
2019-07-31 02:41:07 -07:00
|
|
|
|
update,
|
|
|
|
|
|
this._transactionalSession
|
2019-05-30 11:14:05 -05:00
|
|
|
|
);
|
|
|
|
|
|
} else if (upsert) {
|
|
|
|
|
|
return this.adapter.upsertOneObject(
|
|
|
|
|
|
className,
|
|
|
|
|
|
schema,
|
|
|
|
|
|
query,
|
2019-07-31 02:41:07 -07:00
|
|
|
|
update,
|
|
|
|
|
|
this._transactionalSession
|
2019-05-30 11:14:05 -05:00
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return this.adapter.findOneAndUpdate(
|
|
|
|
|
|
className,
|
|
|
|
|
|
schema,
|
|
|
|
|
|
query,
|
2019-07-31 02:41:07 -07:00
|
|
|
|
update,
|
|
|
|
|
|
this._transactionalSession
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
|
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
});
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((result: any) => {
|
|
|
|
|
|
if (!result) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
|
|
|
|
|
'Object not found.'
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (validateOnly) {
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.handleRelationUpdates(
|
|
|
|
|
|
className,
|
|
|
|
|
|
originalQuery.objectId,
|
|
|
|
|
|
update,
|
|
|
|
|
|
relationUpdates
|
|
|
|
|
|
).then(() => {
|
|
|
|
|
|
return result;
|
2017-12-30 20:44:18 -05:00
|
|
|
|
});
|
2019-05-30 11:14:05 -05:00
|
|
|
|
})
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((result) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (skipSanitization) {
|
|
|
|
|
|
return Promise.resolve(result);
|
|
|
|
|
|
}
|
|
|
|
|
|
return sanitizeDatabaseResult(originalUpdate, result);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
});
|
2019-05-30 11:14:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Collect all relation-updating operations from a REST-format update.
|
|
|
|
|
|
// Returns a list of all relation updates to perform
|
|
|
|
|
|
// This mutates update.
|
|
|
|
|
|
collectRelationUpdates(className: string, objectId: ?string, update: any) {
|
|
|
|
|
|
var ops = [];
|
|
|
|
|
|
var deleteMe = [];
|
|
|
|
|
|
objectId = update.objectId || objectId;
|
|
|
|
|
|
|
|
|
|
|
|
var process = (op, key) => {
|
|
|
|
|
|
if (!op) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (op.__op == 'AddRelation') {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
ops.push({ key, op });
|
2017-12-30 20:44:18 -05:00
|
|
|
|
deleteMe.push(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (op.__op == 'RemoveRelation') {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
ops.push({ key, op });
|
2017-12-30 20:44:18 -05:00
|
|
|
|
deleteMe.push(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (op.__op == 'Batch') {
|
|
|
|
|
|
for (var x of op.ops) {
|
|
|
|
|
|
process(x, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (const key in update) {
|
|
|
|
|
|
process(update[key], key);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (const key of deleteMe) {
|
|
|
|
|
|
delete update[key];
|
2017-06-21 09:23:20 -03:00
|
|
|
|
}
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return ops;
|
|
|
|
|
|
}
|
2016-04-04 14:05:03 -04:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Processes relation-updating operations from a REST-format update.
|
|
|
|
|
|
// Returns a promise that resolves when all updates have been performed
|
2018-09-01 13:58:06 -04:00
|
|
|
|
handleRelationUpdates(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
objectId: string,
|
|
|
|
|
|
update: any,
|
|
|
|
|
|
ops: any
|
|
|
|
|
|
) {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
var pending = [];
|
|
|
|
|
|
objectId = update.objectId || objectId;
|
2018-09-01 13:58:06 -04:00
|
|
|
|
ops.forEach(({ key, op }) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (!op) {
|
|
|
|
|
|
return;
|
2017-06-21 09:23:20 -03:00
|
|
|
|
}
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (op.__op == 'AddRelation') {
|
|
|
|
|
|
for (const object of op.objects) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
pending.push(
|
|
|
|
|
|
this.addRelation(key, className, objectId, object.objectId)
|
|
|
|
|
|
);
|
2016-04-04 14:05:03 -04:00
|
|
|
|
}
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (op.__op == 'RemoveRelation') {
|
|
|
|
|
|
for (const object of op.objects) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
pending.push(
|
|
|
|
|
|
this.removeRelation(key, className, objectId, object.objectId)
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-06-21 09:23:20 -03:00
|
|
|
|
});
|
2016-04-04 14:05:03 -04:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return Promise.all(pending);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Adds a relation.
|
|
|
|
|
|
// Returns a promise that resolves successfully iff the add was successful.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
addRelation(
|
|
|
|
|
|
key: string,
|
|
|
|
|
|
fromClassName: string,
|
|
|
|
|
|
fromId: string,
|
|
|
|
|
|
toId: string
|
|
|
|
|
|
) {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const doc = {
|
|
|
|
|
|
relatedId: toId,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
owningId: fromId,
|
2017-12-30 20:44:18 -05:00
|
|
|
|
};
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return this.adapter.upsertOneObject(
|
|
|
|
|
|
`_Join:${key}:${fromClassName}`,
|
|
|
|
|
|
relationSchema,
|
|
|
|
|
|
doc,
|
2019-07-31 02:41:07 -07:00
|
|
|
|
doc,
|
|
|
|
|
|
this._transactionalSession
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Removes a relation.
|
|
|
|
|
|
// Returns a promise that resolves successfully iff the remove was
|
|
|
|
|
|
// successful.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
removeRelation(
|
|
|
|
|
|
key: string,
|
|
|
|
|
|
fromClassName: string,
|
|
|
|
|
|
fromId: string,
|
|
|
|
|
|
toId: string
|
|
|
|
|
|
) {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
var doc = {
|
|
|
|
|
|
relatedId: toId,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
owningId: fromId,
|
2017-12-30 20:44:18 -05:00
|
|
|
|
};
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return this.adapter
|
|
|
|
|
|
.deleteObjectsByQuery(
|
|
|
|
|
|
`_Join:${key}:${fromClassName}`,
|
|
|
|
|
|
relationSchema,
|
2019-07-31 02:41:07 -07:00
|
|
|
|
doc,
|
|
|
|
|
|
this._transactionalSession
|
2018-09-01 13:58:06 -04:00
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// We don't care if they try to delete a non-existent relation.
|
|
|
|
|
|
if (error.code == Parse.Error.OBJECT_NOT_FOUND) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Removes objects matches this query from the database.
|
|
|
|
|
|
// Returns a promise that resolves successfully iff the object was
|
|
|
|
|
|
// deleted.
|
|
|
|
|
|
// Options:
|
|
|
|
|
|
// acl: a list of strings. If the object to be updated has an ACL,
|
|
|
|
|
|
// one of the provided strings must provide the caller with
|
|
|
|
|
|
// write permissions.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
destroy(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
query: any,
|
2019-05-30 11:14:05 -05:00
|
|
|
|
{ acl }: QueryOptions = {},
|
|
|
|
|
|
validSchemaController: SchemaController.SchemaController
|
2018-09-01 13:58:06 -04:00
|
|
|
|
): Promise<any> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const isMaster = acl === undefined;
|
|
|
|
|
|
const aclGroup = acl || [];
|
|
|
|
|
|
|
2019-05-30 11:14:05 -05:00
|
|
|
|
return this.loadSchemaIfNeeded(validSchemaController).then(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(schemaController) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
return (isMaster
|
|
|
|
|
|
? Promise.resolve()
|
|
|
|
|
|
: schemaController.validatePermission(className, aclGroup, 'delete')
|
|
|
|
|
|
).then(() => {
|
|
|
|
|
|
if (!isMaster) {
|
|
|
|
|
|
query = this.addPointerPermissions(
|
|
|
|
|
|
schemaController,
|
|
|
|
|
|
className,
|
|
|
|
|
|
'delete',
|
|
|
|
|
|
query,
|
|
|
|
|
|
aclGroup
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (!query) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
|
|
|
|
|
'Object not found.'
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
}
|
|
|
|
|
|
// delete by query
|
|
|
|
|
|
if (acl) {
|
|
|
|
|
|
query = addWriteACL(query, acl);
|
|
|
|
|
|
}
|
2020-02-27 10:56:14 -08:00
|
|
|
|
validateQuery(query);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
return schemaController
|
|
|
|
|
|
.getOneSchema(className)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
// If the schema doesn't exist, pretend it exists with no fields. This behavior
|
|
|
|
|
|
// will likely need revisiting.
|
|
|
|
|
|
if (error === undefined) {
|
|
|
|
|
|
return { fields: {} };
|
|
|
|
|
|
}
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
})
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((parseFormatSchema) =>
|
2019-05-30 11:14:05 -05:00
|
|
|
|
this.adapter.deleteObjectsByQuery(
|
|
|
|
|
|
className,
|
|
|
|
|
|
parseFormatSchema,
|
2019-07-31 02:41:07 -07:00
|
|
|
|
query,
|
|
|
|
|
|
this._transactionalSession
|
2019-05-30 11:14:05 -05:00
|
|
|
|
)
|
2018-09-01 13:58:06 -04:00
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
// When deleting sessions while changing passwords, don't throw an error if they don't have any sessions.
|
|
|
|
|
|
if (
|
|
|
|
|
|
className === '_Session' &&
|
|
|
|
|
|
error.code === Parse.Error.OBJECT_NOT_FOUND
|
|
|
|
|
|
) {
|
|
|
|
|
|
return Promise.resolve({});
|
|
|
|
|
|
}
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Inserts an object into the database.
|
|
|
|
|
|
// Returns a promise that resolves successfully iff the object saved.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
create(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
object: any,
|
2019-05-11 10:37:27 -07:00
|
|
|
|
{ acl }: QueryOptions = {},
|
2019-05-30 11:14:05 -05:00
|
|
|
|
validateOnly: boolean = false,
|
|
|
|
|
|
validSchemaController: SchemaController.SchemaController
|
2018-09-01 13:58:06 -04:00
|
|
|
|
): Promise<any> {
|
|
|
|
|
|
// Make a copy of the object, so we don't mutate the incoming data.
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const originalObject = object;
|
|
|
|
|
|
object = transformObjectACL(object);
|
|
|
|
|
|
|
|
|
|
|
|
object.createdAt = { iso: object.createdAt, __type: 'Date' };
|
|
|
|
|
|
object.updatedAt = { iso: object.updatedAt, __type: 'Date' };
|
|
|
|
|
|
|
|
|
|
|
|
var isMaster = acl === undefined;
|
|
|
|
|
|
var aclGroup = acl || [];
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const relationUpdates = this.collectRelationUpdates(
|
|
|
|
|
|
className,
|
|
|
|
|
|
null,
|
|
|
|
|
|
object
|
|
|
|
|
|
);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return this.validateClassName(className)
|
2019-05-30 11:14:05 -05:00
|
|
|
|
.then(() => this.loadSchemaIfNeeded(validSchemaController))
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((schemaController) => {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return (isMaster
|
|
|
|
|
|
? Promise.resolve()
|
|
|
|
|
|
: schemaController.validatePermission(className, aclGroup, 'create')
|
|
|
|
|
|
)
|
2017-12-30 20:44:18 -05:00
|
|
|
|
.then(() => schemaController.enforceClassExists(className))
|
|
|
|
|
|
.then(() => schemaController.getOneSchema(className, true))
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((schema) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
transformAuthData(className, object, schema);
|
|
|
|
|
|
flattenUpdateOperatorsForCreate(object);
|
2019-05-11 10:37:27 -07:00
|
|
|
|
if (validateOnly) {
|
|
|
|
|
|
return {};
|
|
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return this.adapter.createObject(
|
|
|
|
|
|
className,
|
|
|
|
|
|
SchemaController.convertSchemaToAdapterSchema(schema),
|
2019-07-31 02:41:07 -07:00
|
|
|
|
object,
|
|
|
|
|
|
this._transactionalSession
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
})
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((result) => {
|
2019-05-11 10:37:27 -07:00
|
|
|
|
if (validateOnly) {
|
|
|
|
|
|
return originalObject;
|
|
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return this.handleRelationUpdates(
|
|
|
|
|
|
className,
|
|
|
|
|
|
object.objectId,
|
|
|
|
|
|
object,
|
|
|
|
|
|
relationUpdates
|
|
|
|
|
|
).then(() => {
|
|
|
|
|
|
return sanitizeDatabaseResult(originalObject, result.ops[0]);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
});
|
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
|
});
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-01 13:58:06 -04:00
|
|
|
|
canAddField(
|
|
|
|
|
|
schema: SchemaController.SchemaController,
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
object: any,
|
2020-01-28 09:21:30 +03:00
|
|
|
|
aclGroup: string[],
|
|
|
|
|
|
runOptions: QueryOptions
|
2018-09-01 13:58:06 -04:00
|
|
|
|
): Promise<void> {
|
2018-10-08 14:16:29 -04:00
|
|
|
|
const classSchema = schema.schemaData[className];
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (!classSchema) {
|
2017-06-21 09:23:20 -03:00
|
|
|
|
return Promise.resolve();
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
const fields = Object.keys(object);
|
2018-10-08 14:16:29 -04:00
|
|
|
|
const schemaFields = Object.keys(classSchema.fields);
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const newKeys = fields.filter((field) => {
|
2018-03-08 10:31:53 -06:00
|
|
|
|
// Skip fields that are unset
|
2018-09-01 13:58:06 -04:00
|
|
|
|
if (
|
|
|
|
|
|
object[field] &&
|
|
|
|
|
|
object[field].__op &&
|
|
|
|
|
|
object[field].__op === 'Delete'
|
|
|
|
|
|
) {
|
2018-03-08 10:31:53 -06:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return schemaFields.indexOf(field) < 0;
|
2018-03-08 10:31:53 -06:00
|
|
|
|
});
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (newKeys.length > 0) {
|
2020-01-28 09:21:30 +03:00
|
|
|
|
// adds a marker that new field is being adding during update
|
|
|
|
|
|
runOptions.addsField = true;
|
|
|
|
|
|
|
|
|
|
|
|
const action = runOptions.action;
|
|
|
|
|
|
return schema.validatePermission(className, aclGroup, 'addField', action);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
2016-04-04 14:05:03 -04:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Won't delete collections in the system namespace
|
2018-07-03 11:13:08 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Delete all classes and clears the schema cache
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {boolean} fast set to true if it's ok to just delete rows and not indexes
|
|
|
|
|
|
* @returns {Promise<void>} when the deletions completes
|
|
|
|
|
|
*/
|
2018-07-18 14:42:50 +00:00
|
|
|
|
deleteEverything(fast: boolean = false): Promise<any> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
this.schemaPromise = null;
|
|
|
|
|
|
return Promise.all([
|
2018-07-03 11:13:08 -04:00
|
|
|
|
this.adapter.deleteAllClasses(fast),
|
2018-09-01 13:58:06 -04:00
|
|
|
|
this.schemaCache.clear(),
|
2017-12-30 20:44:18 -05:00
|
|
|
|
]);
|
|
|
|
|
|
}
|
2016-03-07 19:26:40 -08:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Returns a promise for a list of related ids given an owning id.
|
|
|
|
|
|
// className here is the owning className.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
relatedIds(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
key: string,
|
|
|
|
|
|
owningId: string,
|
|
|
|
|
|
queryOptions: QueryOptions
|
|
|
|
|
|
): Promise<Array<string>> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const { skip, limit, sort } = queryOptions;
|
|
|
|
|
|
const findOptions = {};
|
|
|
|
|
|
if (sort && sort.createdAt && this.adapter.canSortOnJoinTables) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
findOptions.sort = { _id: sort.createdAt };
|
2017-12-30 20:44:18 -05:00
|
|
|
|
findOptions.limit = limit;
|
|
|
|
|
|
findOptions.skip = skip;
|
|
|
|
|
|
queryOptions.skip = 0;
|
|
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return this.adapter
|
|
|
|
|
|
.find(
|
|
|
|
|
|
joinTableName(className, key),
|
|
|
|
|
|
relationSchema,
|
|
|
|
|
|
{ owningId },
|
|
|
|
|
|
findOptions
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((results) => results.map((result) => result.relatedId));
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2016-03-07 19:26:40 -08:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Returns a promise for a list of owning ids given some related ids.
|
|
|
|
|
|
// className here is the owning className.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
owningIds(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
key: string,
|
|
|
|
|
|
relatedIds: string[]
|
|
|
|
|
|
): Promise<string[]> {
|
|
|
|
|
|
return this.adapter
|
|
|
|
|
|
.find(
|
|
|
|
|
|
joinTableName(className, key),
|
|
|
|
|
|
relationSchema,
|
|
|
|
|
|
{ relatedId: { $in: relatedIds } },
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
{ keys: ['owningId'] }
|
2018-09-01 13:58:06 -04:00
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((results) => results.map((result) => result.owningId));
|
2016-03-02 14:33:51 -05:00
|
|
|
|
}
|
2016-03-07 19:26:40 -08:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Modifies query so that it no longer has $in on relation fields, or
|
|
|
|
|
|
// equal-to-pointer constraints on relation fields.
|
|
|
|
|
|
// Returns a promise that resolves when query is mutated
|
|
|
|
|
|
reduceInRelation(className: string, query: any, schema: any): Promise<any> {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
// Search for an in-relation or equal-to-relation
|
|
|
|
|
|
// Make it sequential for now, not sure of paralleization side effects
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (query['$or']) {
|
|
|
|
|
|
const ors = query['$or'];
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return Promise.all(
|
|
|
|
|
|
ors.map((aQuery, index) => {
|
|
|
|
|
|
return this.reduceInRelation(className, aQuery, schema).then(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(aQuery) => {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
query['$or'][index] = aQuery;
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
})
|
|
|
|
|
|
).then(() => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return Promise.resolve(query);
|
2017-06-21 02:54:13 -03:00
|
|
|
|
});
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2016-01-28 10:58:12 -08:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const promises = Object.keys(query).map((key) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const t = schema.getExpectedType(className, key);
|
|
|
|
|
|
if (!t || t.type !== 'Relation') {
|
|
|
|
|
|
return Promise.resolve(query);
|
|
|
|
|
|
}
|
2018-09-01 13:58:06 -04:00
|
|
|
|
let queries: ?(any[]) = null;
|
|
|
|
|
|
if (
|
|
|
|
|
|
query[key] &&
|
|
|
|
|
|
(query[key]['$in'] ||
|
|
|
|
|
|
query[key]['$ne'] ||
|
|
|
|
|
|
query[key]['$nin'] ||
|
|
|
|
|
|
query[key].__type == 'Pointer')
|
|
|
|
|
|
) {
|
|
|
|
|
|
// Build the list of queries
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
queries = Object.keys(query[key]).map((constraintKey) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
let relatedIds;
|
|
|
|
|
|
let isNegation = false;
|
|
|
|
|
|
if (constraintKey === 'objectId') {
|
|
|
|
|
|
relatedIds = [query[key].objectId];
|
|
|
|
|
|
} else if (constraintKey == '$in') {
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
relatedIds = query[key]['$in'].map((r) => r.objectId);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
} else if (constraintKey == '$nin') {
|
|
|
|
|
|
isNegation = true;
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
relatedIds = query[key]['$nin'].map((r) => r.objectId);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
} else if (constraintKey == '$ne') {
|
|
|
|
|
|
isNegation = true;
|
|
|
|
|
|
relatedIds = [query[key]['$ne'].objectId];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
|
|
|
isNegation,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
relatedIds,
|
|
|
|
|
|
};
|
2017-12-30 20:44:18 -05:00
|
|
|
|
});
|
|
|
|
|
|
} else {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
queries = [{ isNegation: false, relatedIds: [] }];
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2016-03-30 19:35:54 -07:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// remove the current queryKey as we don,t need it anymore
|
|
|
|
|
|
delete query[key];
|
|
|
|
|
|
// execute each query independently to build the list of
|
|
|
|
|
|
// $in / $nin
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const promises = queries.map((q) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (!q) {
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
return this.owningIds(className, key, q.relatedIds).then((ids) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (q.isNegation) {
|
|
|
|
|
|
this.addNotInObjectIdsIds(ids, query);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.addInObjectIdsIds(ids, query);
|
|
|
|
|
|
}
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2016-03-30 19:35:54 -07:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return Promise.all(promises).then(() => {
|
|
|
|
|
|
return Promise.resolve();
|
2018-09-01 13:58:06 -04:00
|
|
|
|
});
|
|
|
|
|
|
});
|
2017-12-30 20:44:18 -05:00
|
|
|
|
|
|
|
|
|
|
return Promise.all(promises).then(() => {
|
|
|
|
|
|
return Promise.resolve(query);
|
2018-09-01 13:58:06 -04:00
|
|
|
|
});
|
2016-03-03 08:40:30 -05:00
|
|
|
|
}
|
2016-03-30 19:35:54 -07:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Modifies query so that it no longer has $relatedTo
|
|
|
|
|
|
// Returns a promise that resolves when query is mutated
|
2018-09-01 13:58:06 -04:00
|
|
|
|
reduceRelationKeys(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
query: any,
|
|
|
|
|
|
queryOptions: any
|
|
|
|
|
|
): ?Promise<void> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (query['$or']) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return Promise.all(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
query['$or'].map((aQuery) => {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
return this.reduceRelationKeys(className, aQuery, queryOptions);
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var relatedTo = query['$relatedTo'];
|
|
|
|
|
|
if (relatedTo) {
|
|
|
|
|
|
return this.relatedIds(
|
|
|
|
|
|
relatedTo.object.className,
|
|
|
|
|
|
relatedTo.key,
|
|
|
|
|
|
relatedTo.object.objectId,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
queryOptions
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((ids) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
delete query['$relatedTo'];
|
|
|
|
|
|
this.addInObjectIdsIds(ids, query);
|
|
|
|
|
|
return this.reduceRelationKeys(className, query, queryOptions);
|
2018-09-01 13:58:06 -04:00
|
|
|
|
})
|
|
|
|
|
|
.then(() => {});
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2016-03-30 19:35:54 -07:00
|
|
|
|
}
|
2016-03-07 08:26:35 -05:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
addInObjectIdsIds(ids: ?Array<string> = null, query: any) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const idsFromString: ?Array<string> =
|
|
|
|
|
|
typeof query.objectId === 'string' ? [query.objectId] : null;
|
|
|
|
|
|
const idsFromEq: ?Array<string> =
|
|
|
|
|
|
query.objectId && query.objectId['$eq'] ? [query.objectId['$eq']] : null;
|
|
|
|
|
|
const idsFromIn: ?Array<string> =
|
|
|
|
|
|
query.objectId && query.objectId['$in'] ? query.objectId['$in'] : null;
|
2016-03-03 08:40:30 -05:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// @flow-disable-next
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const allIds: Array<Array<string>> = [
|
|
|
|
|
|
idsFromString,
|
|
|
|
|
|
idsFromEq,
|
|
|
|
|
|
idsFromIn,
|
|
|
|
|
|
ids,
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
].filter((list) => list !== null);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const totalLength = allIds.reduce((memo, list) => memo + list.length, 0);
|
2016-04-04 14:05:03 -04:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
let idsIntersection = [];
|
|
|
|
|
|
if (totalLength > 125) {
|
|
|
|
|
|
idsIntersection = intersect.big(allIds);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
idsIntersection = intersect(allIds);
|
|
|
|
|
|
}
|
2016-04-04 14:05:03 -04:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Need to make sure we don't clobber existing shorthand $eq constraints on objectId.
|
|
|
|
|
|
if (!('objectId' in query)) {
|
|
|
|
|
|
query.objectId = {
|
|
|
|
|
|
$in: undefined,
|
|
|
|
|
|
};
|
|
|
|
|
|
} else if (typeof query.objectId === 'string') {
|
|
|
|
|
|
query.objectId = {
|
|
|
|
|
|
$in: undefined,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
$eq: query.objectId,
|
2017-12-30 20:44:18 -05:00
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
query.objectId['$in'] = idsIntersection;
|
|
|
|
|
|
|
|
|
|
|
|
return query;
|
2016-04-04 14:05:03 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
addNotInObjectIdsIds(ids: string[] = [], query: any) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const idsFromNin =
|
|
|
|
|
|
query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : [];
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
let allIds = [...idsFromNin, ...ids].filter((list) => list !== null);
|
2016-04-04 14:05:03 -04:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// make a set and spread to remove duplicates
|
|
|
|
|
|
allIds = [...new Set(allIds)];
|
|
|
|
|
|
|
|
|
|
|
|
// Need to make sure we don't clobber existing shorthand $eq constraints on objectId.
|
|
|
|
|
|
if (!('objectId' in query)) {
|
|
|
|
|
|
query.objectId = {
|
|
|
|
|
|
$nin: undefined,
|
|
|
|
|
|
};
|
|
|
|
|
|
} else if (typeof query.objectId === 'string') {
|
|
|
|
|
|
query.objectId = {
|
|
|
|
|
|
$nin: undefined,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
$eq: query.objectId,
|
2017-12-30 20:44:18 -05:00
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
query.objectId['$nin'] = allIds;
|
|
|
|
|
|
return query;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Runs a query on the database.
|
|
|
|
|
|
// Returns a promise that resolves to a list of items.
|
|
|
|
|
|
// Options:
|
|
|
|
|
|
// skip number of results to skip.
|
|
|
|
|
|
// limit limit to this number of results.
|
|
|
|
|
|
// sort an object where keys are the fields to sort by.
|
|
|
|
|
|
// the value is +1 for ascending, -1 for descending.
|
|
|
|
|
|
// count run a count instead of returning results.
|
|
|
|
|
|
// acl restrict this operation with an ACL for the provided array
|
|
|
|
|
|
// of user objectIds and roles. acl: null means no user.
|
|
|
|
|
|
// when this field is not present, don't do anything regarding ACLs.
|
2020-02-14 09:44:51 -08:00
|
|
|
|
// caseInsensitive make string comparisons case insensitive
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// TODO: make userIds not needed here. The db adapter shouldn't know
|
|
|
|
|
|
// anything about users, ideally. Then, improve the format of the ACL
|
|
|
|
|
|
// arg to work like the others.
|
2018-09-01 13:58:06 -04:00
|
|
|
|
find(
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
query: any,
|
|
|
|
|
|
{
|
|
|
|
|
|
skip,
|
|
|
|
|
|
limit,
|
|
|
|
|
|
acl,
|
|
|
|
|
|
sort = {},
|
|
|
|
|
|
count,
|
|
|
|
|
|
keys,
|
|
|
|
|
|
op,
|
|
|
|
|
|
distinct,
|
|
|
|
|
|
pipeline,
|
|
|
|
|
|
readPreference,
|
2020-01-14 01:14:43 -07:00
|
|
|
|
hint,
|
2020-02-14 09:44:51 -08:00
|
|
|
|
caseInsensitive = false,
|
2020-01-14 01:14:43 -07:00
|
|
|
|
explain,
|
2019-01-29 08:52:49 +00:00
|
|
|
|
}: any = {},
|
2019-05-30 11:14:05 -05:00
|
|
|
|
auth: any = {},
|
|
|
|
|
|
validSchemaController: SchemaController.SchemaController
|
2018-09-01 13:58:06 -04:00
|
|
|
|
): Promise<any> {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const isMaster = acl === undefined;
|
|
|
|
|
|
const aclGroup = acl || [];
|
2018-09-01 13:58:06 -04:00
|
|
|
|
op =
|
|
|
|
|
|
op ||
|
|
|
|
|
|
(typeof query.objectId == 'string' && Object.keys(query).length === 1
|
|
|
|
|
|
? 'get'
|
|
|
|
|
|
: 'find');
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Count operation if counting
|
2018-09-01 13:58:06 -04:00
|
|
|
|
op = count === true ? 'count' : op;
|
2017-12-30 20:44:18 -05:00
|
|
|
|
|
|
|
|
|
|
let classExists = true;
|
2019-05-30 11:14:05 -05:00
|
|
|
|
return this.loadSchemaIfNeeded(validSchemaController).then(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(schemaController) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
//Allow volatile classes if querying with Master (for _PushStatus)
|
|
|
|
|
|
//TODO: Move volatile classes concept into mongo adapter, postgres adapter shouldn't care
|
|
|
|
|
|
//that api.parse.com breaks when _PushStatus exists in mongo.
|
|
|
|
|
|
return schemaController
|
|
|
|
|
|
.getOneSchema(className, isMaster)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
// Behavior for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much.
|
|
|
|
|
|
// For now, pretend the class exists but has no objects,
|
|
|
|
|
|
if (error === undefined) {
|
|
|
|
|
|
classExists = false;
|
|
|
|
|
|
return { fields: {} };
|
2017-06-20 09:15:26 -07:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
throw error;
|
|
|
|
|
|
})
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((schema) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
|
|
|
|
|
|
// so duplicate that behavior here. If both are specified, the correct behavior to match Parse.com is to
|
|
|
|
|
|
// use the one that appears first in the sort list.
|
|
|
|
|
|
if (sort._created_at) {
|
|
|
|
|
|
sort.createdAt = sort._created_at;
|
|
|
|
|
|
delete sort._created_at;
|
2017-06-20 09:15:26 -07:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (sort._updated_at) {
|
|
|
|
|
|
sort.updatedAt = sort._updated_at;
|
|
|
|
|
|
delete sort._updated_at;
|
|
|
|
|
|
}
|
2020-01-14 01:14:43 -07:00
|
|
|
|
const queryOptions = {
|
|
|
|
|
|
skip,
|
|
|
|
|
|
limit,
|
|
|
|
|
|
sort,
|
|
|
|
|
|
keys,
|
|
|
|
|
|
readPreference,
|
|
|
|
|
|
hint,
|
2020-02-14 09:44:51 -08:00
|
|
|
|
caseInsensitive,
|
2020-01-14 01:14:43 -07:00
|
|
|
|
explain,
|
|
|
|
|
|
};
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
Object.keys(sort).forEach((fieldName) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_KEY_NAME,
|
|
|
|
|
|
`Cannot sort by ${fieldName}`
|
2019-01-29 08:52:49 +00:00
|
|
|
|
);
|
2017-06-20 09:15:26 -07:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
const rootFieldName = getRootFieldName(fieldName);
|
|
|
|
|
|
if (!SchemaController.fieldNameIsValid(rootFieldName)) {
|
|
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INVALID_KEY_NAME,
|
|
|
|
|
|
`Invalid field name: ${fieldName}.`
|
|
|
|
|
|
);
|
2018-09-01 13:58:06 -04:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
});
|
|
|
|
|
|
return (isMaster
|
|
|
|
|
|
? Promise.resolve()
|
|
|
|
|
|
: schemaController.validatePermission(className, aclGroup, op)
|
|
|
|
|
|
)
|
|
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.reduceRelationKeys(className, query, queryOptions)
|
|
|
|
|
|
)
|
|
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.reduceInRelation(className, query, schemaController)
|
|
|
|
|
|
)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
let protectedFields;
|
|
|
|
|
|
if (!isMaster) {
|
|
|
|
|
|
query = this.addPointerPermissions(
|
|
|
|
|
|
schemaController,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
className,
|
2019-05-30 11:14:05 -05:00
|
|
|
|
op,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
query,
|
2019-05-30 11:14:05 -05:00
|
|
|
|
aclGroup
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
2019-08-22 21:01:50 +02:00
|
|
|
|
/* Don't use projections to optimize the protectedFields since the protectedFields
|
|
|
|
|
|
based on pointer-permissions are determined after querying. The filtering can
|
|
|
|
|
|
overwrite the protected fields. */
|
2019-05-30 11:14:05 -05:00
|
|
|
|
protectedFields = this.addProtectedFields(
|
|
|
|
|
|
schemaController,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
className,
|
|
|
|
|
|
query,
|
2019-05-30 11:14:05 -05:00
|
|
|
|
aclGroup,
|
2020-02-19 12:34:08 +03:00
|
|
|
|
auth,
|
|
|
|
|
|
queryOptions
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
2017-06-20 09:15:26 -07:00
|
|
|
|
}
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (!query) {
|
|
|
|
|
|
if (op === 'get') {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
throw new Parse.Error(
|
2019-05-30 11:14:05 -05:00
|
|
|
|
Parse.Error.OBJECT_NOT_FOUND,
|
|
|
|
|
|
'Object not found.'
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
} else {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!isMaster) {
|
|
|
|
|
|
if (op === 'update' || op === 'delete') {
|
|
|
|
|
|
query = addWriteACL(query, aclGroup);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
query = addReadACL(query, aclGroup);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-02-27 10:56:14 -08:00
|
|
|
|
validateQuery(query);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
if (count) {
|
|
|
|
|
|
if (!classExists) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return this.adapter.count(
|
|
|
|
|
|
className,
|
|
|
|
|
|
schema,
|
|
|
|
|
|
query,
|
2020-01-14 01:14:43 -07:00
|
|
|
|
readPreference,
|
|
|
|
|
|
undefined,
|
|
|
|
|
|
hint
|
2019-05-30 11:14:05 -05:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (distinct) {
|
|
|
|
|
|
if (!classExists) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return this.adapter.distinct(
|
|
|
|
|
|
className,
|
|
|
|
|
|
schema,
|
|
|
|
|
|
query,
|
|
|
|
|
|
distinct
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (pipeline) {
|
|
|
|
|
|
if (!classExists) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return this.adapter.aggregate(
|
|
|
|
|
|
className,
|
|
|
|
|
|
schema,
|
|
|
|
|
|
pipeline,
|
2020-01-14 01:14:43 -07:00
|
|
|
|
readPreference,
|
|
|
|
|
|
hint,
|
|
|
|
|
|
explain
|
2019-05-30 11:14:05 -05:00
|
|
|
|
);
|
|
|
|
|
|
}
|
2020-01-14 01:14:43 -07:00
|
|
|
|
} else if (explain) {
|
|
|
|
|
|
return this.adapter.find(
|
|
|
|
|
|
className,
|
|
|
|
|
|
schema,
|
|
|
|
|
|
query,
|
|
|
|
|
|
queryOptions
|
|
|
|
|
|
);
|
2019-05-30 11:14:05 -05:00
|
|
|
|
} else {
|
|
|
|
|
|
return this.adapter
|
|
|
|
|
|
.find(className, schema, query, queryOptions)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((objects) =>
|
|
|
|
|
|
objects.map((object) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
object = untransformObjectACL(object);
|
|
|
|
|
|
return filterSensitiveData(
|
|
|
|
|
|
isMaster,
|
|
|
|
|
|
aclGroup,
|
2019-08-22 21:01:50 +02:00
|
|
|
|
auth,
|
|
|
|
|
|
op,
|
|
|
|
|
|
schemaController,
|
2019-05-30 11:14:05 -05:00
|
|
|
|
className,
|
|
|
|
|
|
protectedFields,
|
|
|
|
|
|
object
|
|
|
|
|
|
);
|
|
|
|
|
|
})
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2019-05-30 11:14:05 -05:00
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
Parse.Error.INTERNAL_SERVER_ERROR,
|
|
|
|
|
|
error
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2016-06-12 13:39:41 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
deleteSchema(className: string): Promise<void> {
|
|
|
|
|
|
return this.loadSchema({ clearCache: true })
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((schemaController) =>
|
|
|
|
|
|
schemaController.getOneSchema(className, true)
|
|
|
|
|
|
)
|
|
|
|
|
|
.catch((error) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (error === undefined) {
|
|
|
|
|
|
return { fields: {} };
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((schema: any) => {
|
|
|
|
|
|
return this.collectionExists(className)
|
2019-04-08 15:59:15 -07:00
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.adapter.count(className, { fields: {} }, null, '', false)
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((count) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (count > 0) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
throw new Parse.Error(
|
|
|
|
|
|
255,
|
|
|
|
|
|
`Class ${className} is not empty, contains ${count} objects, cannot drop schema.`
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
return this.adapter.deleteClass(className);
|
|
|
|
|
|
})
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((wasParseCollection) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (wasParseCollection) {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const relationFieldNames = Object.keys(schema.fields).filter(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
(fieldName) => schema.fields[fieldName].type === 'Relation'
|
2018-09-01 13:58:06 -04:00
|
|
|
|
);
|
|
|
|
|
|
return Promise.all(
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
relationFieldNames.map((name) =>
|
2018-09-01 13:58:06 -04:00
|
|
|
|
this.adapter.deleteClass(joinTableName(className, name))
|
|
|
|
|
|
)
|
|
|
|
|
|
).then(() => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return;
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2018-09-01 13:58:06 -04:00
|
|
|
|
});
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2016-04-14 19:24:56 -04:00
|
|
|
|
|
2020-01-28 09:21:30 +03:00
|
|
|
|
// Constraints query using CLP's pointer permissions (PP) if any.
|
|
|
|
|
|
// 1. Etract the user id from caller's ACLgroup;
|
|
|
|
|
|
// 2. Exctract a list of field names that are PP for target collection and operation;
|
|
|
|
|
|
// 3. Constraint the original query so that each PP field must
|
|
|
|
|
|
// point to caller's id (or contain it in case of PP field being an array)
|
2018-09-01 13:58:06 -04:00
|
|
|
|
addPointerPermissions(
|
2018-10-17 17:53:49 -04:00
|
|
|
|
schema: SchemaController.SchemaController,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
className: string,
|
|
|
|
|
|
operation: string,
|
|
|
|
|
|
query: any,
|
|
|
|
|
|
aclGroup: any[] = []
|
2020-01-28 09:21:30 +03:00
|
|
|
|
): any {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
// Check if class has public permission for operation
|
|
|
|
|
|
// If the BaseCLP pass, let go through
|
2018-10-17 17:53:49 -04:00
|
|
|
|
if (schema.testPermissionsForClassName(className, aclGroup, operation)) {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return query;
|
|
|
|
|
|
}
|
2018-10-17 17:53:49 -04:00
|
|
|
|
const perms = schema.getClassLevelPermissions(className);
|
2020-01-28 09:21:30 +03:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const userACL = aclGroup.filter((acl) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
return acl.indexOf('role:') != 0 && acl != '*';
|
|
|
|
|
|
});
|
2020-01-28 09:21:30 +03:00
|
|
|
|
|
|
|
|
|
|
const groupKey =
|
|
|
|
|
|
['get', 'find', 'count'].indexOf(operation) > -1
|
|
|
|
|
|
? 'readUserFields'
|
|
|
|
|
|
: 'writeUserFields';
|
|
|
|
|
|
|
|
|
|
|
|
const permFields = [];
|
|
|
|
|
|
|
|
|
|
|
|
if (perms[operation] && perms[operation].pointerFields) {
|
|
|
|
|
|
permFields.push(...perms[operation].pointerFields);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (perms[groupKey]) {
|
|
|
|
|
|
for (const field of perms[groupKey]) {
|
|
|
|
|
|
if (!permFields.includes(field)) {
|
|
|
|
|
|
permFields.push(field);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// the ACL should have exactly 1 user
|
2020-01-28 09:21:30 +03:00
|
|
|
|
if (permFields.length > 0) {
|
|
|
|
|
|
// the ACL should have exactly 1 user
|
2018-09-01 13:58:06 -04:00
|
|
|
|
// No user set return undefined
|
|
|
|
|
|
// If the length is > 1, that means we didn't de-dupe users correctly
|
2017-12-30 20:44:18 -05:00
|
|
|
|
if (userACL.length != 1) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const userId = userACL[0];
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const userPointer = {
|
|
|
|
|
|
__type: 'Pointer',
|
|
|
|
|
|
className: '_User',
|
|
|
|
|
|
objectId: userId,
|
2016-04-20 21:51:11 -04:00
|
|
|
|
};
|
2017-12-30 20:44:18 -05:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const ors = permFields.flatMap((key) => {
|
2019-08-16 06:55:12 +02:00
|
|
|
|
// constraint for single pointer setup
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const q = {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
[key]: userPointer,
|
2017-12-30 20:44:18 -05:00
|
|
|
|
};
|
2019-08-16 06:55:12 +02:00
|
|
|
|
// constraint for users-array setup
|
|
|
|
|
|
const qa = {
|
|
|
|
|
|
[key]: { $all: [userPointer] },
|
|
|
|
|
|
};
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// if we already have a constraint on the key, use the $and
|
2019-08-14 16:57:00 -05:00
|
|
|
|
if (Object.prototype.hasOwnProperty.call(query, key)) {
|
2019-08-16 06:55:12 +02:00
|
|
|
|
return [{ $and: [q, query] }, { $and: [qa, query] }];
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
|
|
|
|
|
// otherwise just add the constaint
|
2019-08-16 06:55:12 +02:00
|
|
|
|
return [Object.assign({}, query, q), Object.assign({}, query, qa)];
|
2017-12-30 20:44:18 -05:00
|
|
|
|
});
|
2019-08-16 06:55:12 +02:00
|
|
|
|
return { $or: ors };
|
2017-12-30 20:44:18 -05:00
|
|
|
|
} else {
|
|
|
|
|
|
return query;
|
2016-04-20 21:51:11 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-01-29 08:52:49 +00:00
|
|
|
|
addProtectedFields(
|
|
|
|
|
|
schema: SchemaController.SchemaController,
|
|
|
|
|
|
className: string,
|
|
|
|
|
|
query: any = {},
|
|
|
|
|
|
aclGroup: any[] = [],
|
2020-02-19 12:34:08 +03:00
|
|
|
|
auth: any = {},
|
|
|
|
|
|
queryOptions: FullQueryOptions = {}
|
2020-01-28 09:21:30 +03:00
|
|
|
|
): null | string[] {
|
2019-01-29 08:52:49 +00:00
|
|
|
|
const perms = schema.getClassLevelPermissions(className);
|
|
|
|
|
|
if (!perms) return null;
|
|
|
|
|
|
|
|
|
|
|
|
const protectedFields = perms.protectedFields;
|
|
|
|
|
|
if (!protectedFields) return null;
|
|
|
|
|
|
|
|
|
|
|
|
if (aclGroup.indexOf(query.objectId) > -1) return null;
|
2019-08-22 21:01:50 +02:00
|
|
|
|
|
2020-02-19 12:34:08 +03:00
|
|
|
|
// for queries where "keys" are set and do not include all 'userField':{field},
|
|
|
|
|
|
// we have to transparently include it, and then remove before returning to client
|
|
|
|
|
|
// Because if such key not projected the permission won't be enforced properly
|
|
|
|
|
|
// PS this is called when 'excludeKeys' already reduced to 'keys'
|
|
|
|
|
|
const preserveKeys = queryOptions.keys;
|
|
|
|
|
|
|
|
|
|
|
|
// these are keys that need to be included only
|
|
|
|
|
|
// to be able to apply protectedFields by pointer
|
|
|
|
|
|
// and then unset before returning to client (later in filterSensitiveFields)
|
|
|
|
|
|
const serverOnlyKeys = [];
|
|
|
|
|
|
|
|
|
|
|
|
const authenticated = auth.user;
|
|
|
|
|
|
|
|
|
|
|
|
// map to allow check without array search
|
|
|
|
|
|
const roles = (auth.userRoles || []).reduce((acc, r) => {
|
|
|
|
|
|
acc[r] = protectedFields[r];
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
|
|
|
|
// array of sets of protected fields. separate item for each applicable criteria
|
|
|
|
|
|
const protectedKeysSets = [];
|
|
|
|
|
|
|
|
|
|
|
|
for (const key in protectedFields) {
|
|
|
|
|
|
// skip userFields
|
|
|
|
|
|
if (key.startsWith('userField:')) {
|
|
|
|
|
|
if (preserveKeys) {
|
|
|
|
|
|
const fieldName = key.substring(10);
|
|
|
|
|
|
if (!preserveKeys.includes(fieldName)) {
|
|
|
|
|
|
// 1. put it there temporarily
|
|
|
|
|
|
queryOptions.keys && queryOptions.keys.push(fieldName);
|
|
|
|
|
|
// 2. preserve it delete later
|
|
|
|
|
|
serverOnlyKeys.push(fieldName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// add public tier
|
|
|
|
|
|
if (key === '*') {
|
|
|
|
|
|
protectedKeysSets.push(protectedFields[key]);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (authenticated) {
|
|
|
|
|
|
if (key === 'authenticated') {
|
|
|
|
|
|
// for logged in users
|
|
|
|
|
|
protectedKeysSets.push(protectedFields[key]);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (roles[key] && key.startsWith('role:')) {
|
|
|
|
|
|
// add applicable roles
|
|
|
|
|
|
protectedKeysSets.push(roles[key]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// check if there's a rule for current user's id
|
|
|
|
|
|
if (authenticated) {
|
|
|
|
|
|
const userId = auth.user.id;
|
|
|
|
|
|
if (perms.protectedFields[userId]) {
|
|
|
|
|
|
protectedKeysSets.push(perms.protectedFields[userId]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// preserve fields to be removed before sending response to client
|
|
|
|
|
|
if (serverOnlyKeys.length > 0) {
|
|
|
|
|
|
perms.protectedFields.temporaryKeys = serverOnlyKeys;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let protectedKeys = protectedKeysSets.reduce((acc, next) => {
|
|
|
|
|
|
if (next) {
|
|
|
|
|
|
acc.push(...next);
|
|
|
|
|
|
}
|
|
|
|
|
|
return acc;
|
2019-08-22 21:01:50 +02:00
|
|
|
|
}, []);
|
|
|
|
|
|
|
2020-02-19 12:34:08 +03:00
|
|
|
|
// intersect all sets of protectedFields
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
protectedKeysSets.forEach((fields) => {
|
2019-01-31 21:44:24 +00:00
|
|
|
|
if (fields) {
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
protectedKeys = protectedKeys.filter((v) => fields.includes(v));
|
2019-01-29 08:52:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return protectedKeys;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-07-31 02:41:07 -07:00
|
|
|
|
createTransactionalSession() {
|
|
|
|
|
|
return this.adapter
|
|
|
|
|
|
.createTransactionalSession()
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.then((transactionalSession) => {
|
2019-07-31 02:41:07 -07:00
|
|
|
|
this._transactionalSession = transactionalSession;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
commitTransactionalSession() {
|
|
|
|
|
|
if (!this._transactionalSession) {
|
|
|
|
|
|
throw new Error('There is no transactional session to commit');
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.adapter
|
|
|
|
|
|
.commitTransactionalSession(this._transactionalSession)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
this._transactionalSession = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
abortTransactionalSession() {
|
|
|
|
|
|
if (!this._transactionalSession) {
|
|
|
|
|
|
throw new Error('There is no transactional session to abort');
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.adapter
|
|
|
|
|
|
.abortTransactionalSession(this._transactionalSession)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
this._transactionalSession = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// TODO: create indexes on first creation of a _User object. Otherwise it's impossible to
|
|
|
|
|
|
// have a Parse app without it having a _User collection.
|
|
|
|
|
|
performInitialization() {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const requiredUserFields = {
|
|
|
|
|
|
fields: {
|
|
|
|
|
|
...SchemaController.defaultColumns._Default,
|
|
|
|
|
|
...SchemaController.defaultColumns._User,
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
const requiredRoleFields = {
|
|
|
|
|
|
fields: {
|
|
|
|
|
|
...SchemaController.defaultColumns._Default,
|
|
|
|
|
|
...SchemaController.defaultColumns._Role,
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
2017-12-30 20:44:18 -05:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const userClassPromise = this.loadSchema().then((schema) =>
|
2018-09-01 13:58:06 -04:00
|
|
|
|
schema.enforceClassExists('_User')
|
|
|
|
|
|
);
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
const roleClassPromise = this.loadSchema().then((schema) =>
|
2018-09-01 13:58:06 -04:00
|
|
|
|
schema.enforceClassExists('_Role')
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
|
|
|
|
|
|
const usernameUniqueness = userClassPromise
|
2018-09-01 13:58:06 -04:00
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.adapter.ensureUniqueness('_User', requiredUserFields, ['username'])
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
logger.warn('Unable to ensure uniqueness for usernames: ', error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
});
|
2016-08-07 23:02:53 -04:00
|
|
|
|
|
2020-02-14 09:44:51 -08:00
|
|
|
|
const usernameCaseInsensitiveIndex = userClassPromise
|
|
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.adapter.ensureIndex(
|
|
|
|
|
|
'_User',
|
|
|
|
|
|
requiredUserFields,
|
|
|
|
|
|
['username'],
|
|
|
|
|
|
'case_insensitive_username',
|
|
|
|
|
|
true
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2020-02-14 09:44:51 -08:00
|
|
|
|
logger.warn(
|
|
|
|
|
|
'Unable to create case insensitive username index: ',
|
|
|
|
|
|
error
|
|
|
|
|
|
);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const emailUniqueness = userClassPromise
|
2018-09-01 13:58:06 -04:00
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.adapter.ensureUniqueness('_User', requiredUserFields, ['email'])
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2018-09-01 13:58:06 -04:00
|
|
|
|
logger.warn(
|
|
|
|
|
|
'Unable to ensure uniqueness for user email addresses: ',
|
|
|
|
|
|
error
|
|
|
|
|
|
);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
throw error;
|
|
|
|
|
|
});
|
2017-03-04 22:42:19 +02:00
|
|
|
|
|
2020-02-14 09:44:51 -08:00
|
|
|
|
const emailCaseInsensitiveIndex = userClassPromise
|
|
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.adapter.ensureIndex(
|
|
|
|
|
|
'_User',
|
|
|
|
|
|
requiredUserFields,
|
|
|
|
|
|
['email'],
|
|
|
|
|
|
'case_insensitive_email',
|
|
|
|
|
|
true
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2020-02-14 09:44:51 -08:00
|
|
|
|
logger.warn('Unable to create case insensitive email index: ', error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const roleUniqueness = roleClassPromise
|
2018-09-01 13:58:06 -04:00
|
|
|
|
.then(() =>
|
|
|
|
|
|
this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name'])
|
|
|
|
|
|
)
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
.catch((error) => {
|
2017-12-30 20:44:18 -05:00
|
|
|
|
logger.warn('Unable to ensure uniqueness for role name: ', error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
});
|
2016-08-15 16:48:39 -04:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
const indexPromise = this.adapter.updateSchemaWithIndexes();
|
2017-11-25 13:55:34 -06:00
|
|
|
|
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Create tables for volatile classes
|
2018-09-01 13:58:06 -04:00
|
|
|
|
const adapterInit = this.adapter.performInitialization({
|
|
|
|
|
|
VolatileClassesSchemas: SchemaController.VolatileClassesSchemas,
|
|
|
|
|
|
});
|
|
|
|
|
|
return Promise.all([
|
|
|
|
|
|
usernameUniqueness,
|
2020-02-14 09:44:51 -08:00
|
|
|
|
usernameCaseInsensitiveIndex,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
emailUniqueness,
|
2020-02-14 09:44:51 -08:00
|
|
|
|
emailCaseInsensitiveIndex,
|
2018-09-01 13:58:06 -04:00
|
|
|
|
roleUniqueness,
|
|
|
|
|
|
adapterInit,
|
|
|
|
|
|
indexPromise,
|
|
|
|
|
|
]);
|
2017-12-30 20:44:18 -05:00
|
|
|
|
}
|
2016-08-07 23:02:53 -04:00
|
|
|
|
|
perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update
My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.
* perf: Allow covering relation queries with minimal index
When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```
From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
.find({ relatedId: { $in: [...] } })
.project({ owningId: 1 });
```
This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.
For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
{ relatedId: 1, owningId: 1 },
{ background: true }
);
```
One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
|
|
|
|
static _validateQuery: (any) => void;
|
2016-03-01 20:04:51 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-27 02:02:33 -08:00
|
|
|
|
module.exports = DatabaseController;
|
2017-12-30 20:44:18 -05:00
|
|
|
|
// Expose validateQuery for tests
|
|
|
|
|
|
module.exports._validateQuery = validateQuery;
|