-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(gatsby): use GatsbyIterable + extract common tools for querying #32172
Conversation
d6345ce
to
c56a0e0
Compare
export type FilterValueNullable = // TODO: merge with DbComparatorValue | ||
| string | ||
| number | ||
| boolean | ||
| null | ||
| undefined | ||
| RegExp // Only valid for $regex | ||
| Array<string | number | boolean | null | undefined> | ||
|
||
// This is filter value in most cases | ||
export type FilterValue = | ||
| string | ||
| number | ||
| boolean | ||
| RegExp // Only valid for $regex | ||
| Array<string | number | boolean> | ||
|
||
// The value is an object with arbitrary keys that are either filter values or, | ||
// recursively, an object with the same struct. Ie. `{a: {a: {a: 2}}}` | ||
export interface IInputQuery { | ||
[key: string]: FilterValueNullable | IInputQuery | ||
} | ||
// Similar to IInputQuery except the comparator leaf nodes will have their | ||
// key prefixed with `$` and their value, in some cases, normalized. | ||
export interface IPreparedQueryArg { | ||
[key: string]: FilterValueNullable | IPreparedQueryArg | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thos types are moved here from datastore/in-memory/indexing.ts
and datastore/in-memory/run-fast-filters.ts
/** | ||
* Transforms filters coming from input GraphQL query to mongodb-compatible format | ||
* (by prefixing comparators with "$"). | ||
* | ||
* Example: | ||
* { foo: { eq: 5 } } -> { foo: { $eq: 5 }} | ||
*/ | ||
export function prepareQueryArgs( | ||
filterFields: Array<IInputQuery> | IInputQuery = {} | ||
): IPreparedQueryArg { | ||
const filters = {} | ||
Object.keys(filterFields).forEach(key => { | ||
const value = filterFields[key] | ||
if (_.isPlainObject(value)) { | ||
filters[key === `elemMatch` ? `$elemMatch` : key] = prepareQueryArgs( | ||
value as IInputQuery | ||
) | ||
} else { | ||
switch (key) { | ||
case `regex`: | ||
if (typeof value !== `string`) { | ||
throw new Error( | ||
`The $regex comparator is expecting the regex as a string, not an actual regex or anything else` | ||
) | ||
} | ||
filters[`$regex`] = prepareRegex(value) | ||
break | ||
case `glob`: | ||
filters[`$regex`] = makeRe(value) | ||
break | ||
default: | ||
filters[`$${key}`] = value | ||
} | ||
} | ||
}) | ||
return filters | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also moved from datastore/in-memory/run-fast-filters.ts
@@ -297,7 +297,7 @@ class LocalNodeModel { | |||
runQueryActivity.start() | |||
} | |||
|
|||
const { entries, totalCount } = runFastFiltersAndSort({ | |||
const { entries, totalCount } = await getDataStore().runQuery({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now querying through datastore - the basis for future lmdb runQuery
implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just checking if I'm reading things correctly and understand the plan - in this PR both "redux" and lmdb have same implementation (returning Promise.resolve(runFastFiltersAndSort(args))
). In next PR(s) lmdb datastore will use its own (potentially falling back to same thing if something is impossible or just not implemented yet)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct
return { | ||
gqlType, | ||
entries: entries.map(node => { | ||
// With GatsbyIterable it happens lazily as we iterate | ||
this.trackInlineObjectsInRootNode(node) | ||
return node | ||
}), | ||
totalCount, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behavior change: switching from eager to lazy tracking of inline objects during iteration.
Now entries
is an instance of GatsbyIterable
, so we can apply this lazily. This makes a difference for lmdb
because each iteration is essentially a separate query (reset cursor, re-apply filters, etc, which can be slow).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! LGTM
Description
This PR mostly affects various utilities related to querying:
lmdb
runQuery
implementation.GatsbyIterable
GatsbyIterable
forrunQuery
and various node iteration methodsThere are several minor behavior changes, I'll comment on them inline.