Skip to content
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

chore(gatsby): create single filter cache key generator #22716

Merged
merged 2 commits into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 9 additions & 17 deletions packages/gatsby/src/redux/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { store } from "./"
import { IGatsbyNode } from "./types"
import { createPageDependency } from "./actions/add-page-dependency"

export type FilterCacheKey = string

/**
* Get all nodes from redux store.
*/
Expand Down Expand Up @@ -155,27 +157,22 @@ export const addResolvedNodes = (
* looping over all the nodes, when the number of pages (/nodes) scale up.
*/
export const ensureIndexByTypedChain = (
cacheKey: FilterCacheKey,
chain: string[],
nodeTypeNames: string[],
typedKeyValueIndexes: Map<
string,
FilterCacheKey,
Map<string | number | boolean, Set<IGatsbyNode>>
>
): void => {
const chained = chain.join(`+`)

const nodeTypeNamePrefix = nodeTypeNames.join(`,`) + `/`
// The format of the typedKey is `type,type/path+to+eqobj`
const typedKey = nodeTypeNamePrefix + chained

if (typedKeyValueIndexes.has(typedKey)) {
if (typedKeyValueIndexes.has(cacheKey)) {
return
}

const { nodes, resolvedNodesCache } = store.getState()

const byKeyValue = new Map<string | number | boolean, Set<IGatsbyNode>>()
typedKeyValueIndexes.set(typedKey, byKeyValue)
typedKeyValueIndexes.set(cacheKey, byKeyValue)

nodes.forEach(node => {
if (!nodeTypeNames.includes(node.internal.type)) {
Expand Down Expand Up @@ -232,18 +229,13 @@ export const ensureIndexByTypedChain = (
* per `id` so there's a minor optimization for that (no need for Sets).
*/
export const getNodesByTypedChain = (
chain: string[],
cacheKey: FilterCacheKey,
value: boolean | number | string,
nodeTypeNames: string[],
typedKeyValueIndexes: Map<
string,
FilterCacheKey,
Map<string | number | boolean, Set<IGatsbyNode>>
>
): Set<IGatsbyNode> | undefined => {
const key = chain.join(`+`)

const typedKey = nodeTypeNames.join(`,`) + `/` + key

const byTypedKey = typedKeyValueIndexes?.get(typedKey)
const byTypedKey = typedKeyValueIndexes?.get(cacheKey)
return byTypedKey?.get(value)
}
52 changes: 44 additions & 8 deletions packages/gatsby/src/redux/run-sift.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @flow

const { default: sift } = require(`sift`)
const { prepareRegex } = require(`../utils/prepare-regex`)
const { makeRe } = require(`micromatch`)
Expand All @@ -19,6 +20,35 @@ const {
getNode: siftGetNode,
} = require(`./nodes`)

/**
* Creates a key for the filterCache
*
* @param {Array<string>} typeNames
* @param {DbQuery} filter
* @returns {FilterCacheKey} (a string: `types.join()/path.join()/operator` )
*/
const createTypedFilterCacheKey = (typeNames, filter) => {
// Note: while `elemMatch` is a special case, in the key it's just `elemMatch`
// (This function is future proof for elemMatch support, won't receive it yet)
let f = filter
let comparator = ``
let paths /*: Array<string>*/ = []
while (f) {
paths.push(...f.path)
if (f.type === `elemMatch`) {
let q /*: IDbQueryElemMatch*/ = f
f = q.nestedQuery
} else {
let q /*: IDbQueryQuery*/ = f
comparator = q.query.comparator
break
}
}

// Note: the separators (`,` and `/`) are arbitrary but must be different
return typeNames.join(`,`) + `/` + comparator + `/` + paths.join(`,`)
}

/////////////////////////////////////////////////////////////////////
// Parse filter
/////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -105,7 +135,7 @@ function handleMany(siftArgs, nodes) {
*
* @param {Array<DbQuery>} filters Resolved. (Should be checked by caller to exist)
* @param {Array<string>} nodeTypeNames
* @param {Map<string, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @param {Map<FilterCacheKey, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @returns {Array<IGatsbyNode> | undefined}
*/
const runFlatFiltersWithoutSift = (
Expand Down Expand Up @@ -149,7 +179,7 @@ const runFlatFiltersWithoutSift = (
/**
* @param {Array<DbQuery>} filters
* @param {Array<string>} nodeTypeNames
* @param {Map<string, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @param {Map<FilterCacheKey, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @returns {Array<Set<IGatsbyNode>> | undefined} Undefined means at least one
* cache was not found. Must fallback to sift.
*/
Expand All @@ -163,15 +193,21 @@ const getBucketsForFilters = (filters, nodeTypeNames, typedKeyValueIndexes) => {
query: { value: targetValue },
} = filter

ensureIndexByTypedChain(chain, nodeTypeNames, typedKeyValueIndexes)
let cacheKey = createTypedFilterCacheKey(nodeTypeNames, filter)
pvdz marked this conversation as resolved.
Show resolved Hide resolved

const nodesByKeyValue = getNodesByTypedChain(
ensureIndexByTypedChain(
cacheKey,
chain,
targetValue,
nodeTypeNames,
typedKeyValueIndexes
)

const nodesByKeyValue = getNodesByTypedChain(
cacheKey,
targetValue,
typedKeyValueIndexes
)

// If we couldn't find the needle then maybe sift can, for example if the
// schema contained a proxy; `slug: String @proxy(from: "slugInternal")`
// There are also cases (and tests) where id exists with a different type
Expand Down Expand Up @@ -202,7 +238,7 @@ const getBucketsForFilters = (filters, nodeTypeNames, typedKeyValueIndexes) => {
* @property {boolean} args.firstOnly true if you want to return only the first
* result found. This will return a collection of size 1. Not a single element
* @property {{filter?: Object, sort?: Object} | undefined} args.queryArgs
* @property {undefined | Map<string, Map<string | number | boolean, Set<IGatsbyNode>>>} args.typedKeyValueIndexes
* @property {undefined | Map<FilterCacheKey, Map<string | number | boolean, Set<IGatsbyNode>>>} args.typedKeyValueIndexes
* May be undefined. A cache of indexes where you can look up Nodes grouped
* by a key: `types.join(',')+'/'+filterPath.join('+')`, which yields a Map
* which holds a Set of Nodes for the value that the filter is trying to eq
Expand Down Expand Up @@ -243,7 +279,7 @@ exports.runSift = runFilterAndSort
* @param {Array<DbQuery> | undefined} filterFields
* @param {boolean} firstOnly
* @param {Array<string>} nodeTypeNames
* @param {undefined | Map<string, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @param {undefined | Map<FilterCacheKey, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @param resolvedFields
* @returns {Array<IGatsbyNode> | undefined} Collection of results. Collection
* will be limited to 1 if `firstOnly` is true
Expand Down Expand Up @@ -318,7 +354,7 @@ const filterToStats = (
*
* @param {Array<DbQuery>} filters Resolved. (Should be checked by caller to exist)
* @param {Array<string>} nodeTypeNames
* @param {Map<string, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @param {Map<FilterCacheKey, Map<string | number | boolean, Set<IGatsbyNode>>>} typedKeyValueIndexes
* @returns {Array|undefined} Collection of results
*/
const filterWithoutSift = (filters, nodeTypeNames, typedKeyValueIndexes) => {
Expand Down