UNPKG

@tldraw/store

Version:

tldraw infinite canvas SDK (store).

68 lines (61 loc) 2.25 kB
import { IdOf, UnknownRecord } from './BaseRecord' import { intersectSets } from './setUtils' import { StoreQueries } from './StoreQueries' /** @public */ export type QueryValueMatcher<T> = { eq: T } | { neq: T } | { gt: number } /** @public */ export type QueryExpression<R extends object> = { [k in keyof R & string]?: QueryValueMatcher<R[k]> // todo: handle nesting // | (R[k] extends object ? { match: QueryExpression<R[k]> } : never) } export function objectMatchesQuery<T extends object>(query: QueryExpression<T>, object: T) { for (const [key, _matcher] of Object.entries(query)) { const matcher = _matcher as QueryValueMatcher<T> const value = object[key as keyof T] // if you add matching logic here, make sure you also update executeQuery, // where initial data is pulled out of the indexes, since that requires different // matching logic if ('eq' in matcher && value !== matcher.eq) return false if ('neq' in matcher && value === matcher.neq) return false if ('gt' in matcher && (typeof value !== 'number' || value <= matcher.gt)) return false } return true } export function executeQuery<R extends UnknownRecord, TypeName extends R['typeName']>( store: StoreQueries<R>, typeName: TypeName, query: QueryExpression<Extract<R, { typeName: TypeName }>> ): Set<IdOf<Extract<R, { typeName: TypeName }>>> { const matchIds = Object.fromEntries(Object.keys(query).map((key) => [key, new Set()])) for (const [k, matcher] of Object.entries(query)) { if ('eq' in matcher) { const index = store.index(typeName, k as any) const ids = index.get().get(matcher.eq) if (ids) { for (const id of ids) { matchIds[k].add(id) } } } else if ('neq' in matcher) { const index = store.index(typeName, k as any) for (const [value, ids] of index.get()) { if (value !== matcher.neq) { for (const id of ids) { matchIds[k].add(id) } } } } else if ('gt' in matcher) { const index = store.index(typeName, k as any) for (const [value, ids] of index.get()) { if (value > matcher.gt) { for (const id of ids) { matchIds[k].add(id) } } } } } return intersectSets(Object.values(matchIds)) as Set<IdOf<Extract<R, { typeName: TypeName }>>> }