UNPKG

rxdb

Version:

A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/

174 lines (157 loc) 5.72 kB
import { ActionName, calculateActionName, runAction, QueryParams, QueryMatcher, DeterministicSortComparator, StateResolveFunctionInput, ChangeEvent } from 'event-reduce-js'; import type { RxQuery, MangoQuery, StringKeys, RxDocumentData, RxStorageChangeEvent } from './types/index.d.ts'; import { rxChangeEventToEventReduceChangeEvent } from './rx-change-event.ts'; import { clone, ensureNotFalsy, getFromMapOrCreate } from './plugins/utils/index.ts'; import { getQueryMatcher, getSortComparator, normalizeMangoQuery } from './rx-query-helper.ts'; export type EventReduceResultNeg = { runFullQueryAgain: true; }; export type EventReduceResultPos<RxDocumentType> = { runFullQueryAgain: false; changed: boolean; newResults: RxDocumentType[]; }; export type EventReduceResult<RxDocumentType> = EventReduceResultNeg | EventReduceResultPos<RxDocumentType>; export function getSortFieldsOfQuery<RxDocType>( primaryKey: StringKeys<RxDocumentData<RxDocType>>, query: MangoQuery<RxDocType> ): (string | StringKeys<RxDocType>)[] { if (!query.sort || query.sort.length === 0) { return [primaryKey]; } else { return query.sort.map(part => Object.keys(part)[0]); } } export const RXQUERY_QUERY_PARAMS_CACHE: WeakMap<RxQuery, QueryParams<any>> = new WeakMap(); export function getQueryParams<RxDocType>( rxQuery: RxQuery<RxDocType> ): QueryParams<RxDocType> { return getFromMapOrCreate( RXQUERY_QUERY_PARAMS_CACHE, rxQuery, () => { const collection = rxQuery.collection; const normalizedMangoQuery = normalizeMangoQuery( collection.storageInstance.schema, clone(rxQuery.mangoQuery) ); const primaryKey = collection.schema.primaryPath; /** * Create a custom sort comparator * that uses the hooks to ensure * we send for example compressed documents to be sorted by compressed queries. */ const sortComparator = getSortComparator( collection.schema.jsonSchema, normalizedMangoQuery ); const useSortComparator: DeterministicSortComparator<RxDocType> = (docA: RxDocType, docB: RxDocType) => { const sortComparatorData = { docA, docB, rxQuery }; return sortComparator(sortComparatorData.docA, sortComparatorData.docB); }; /** * Create a custom query matcher * that uses the hooks to ensure * we send for example compressed documents to match compressed queries. */ const queryMatcher = getQueryMatcher( collection.schema.jsonSchema, normalizedMangoQuery ); const useQueryMatcher: QueryMatcher<RxDocumentData<RxDocType>> = (doc: RxDocumentData<RxDocType>) => { const queryMatcherData = { doc, rxQuery }; return queryMatcher(queryMatcherData.doc); }; const ret: QueryParams<any> = { primaryKey: rxQuery.collection.schema.primaryPath as any, skip: normalizedMangoQuery.skip, limit: normalizedMangoQuery.limit, sortFields: getSortFieldsOfQuery(primaryKey, normalizedMangoQuery) as string[], sortComparator: useSortComparator, queryMatcher: useQueryMatcher }; return ret; } ); } export function calculateNewResults<RxDocumentType>( rxQuery: RxQuery<RxDocumentType>, rxChangeEvents: RxStorageChangeEvent<RxDocumentType>[] ): EventReduceResult<RxDocumentType> { if (!rxQuery.collection.database.eventReduce) { return { runFullQueryAgain: true }; } const queryParams = getQueryParams(rxQuery); const previousResults: RxDocumentType[] = ensureNotFalsy(rxQuery._result).docsData.slice(0); const previousResultsMap: Map<string, RxDocumentType> = ensureNotFalsy(rxQuery._result).docsDataMap; let changed: boolean = false; const eventReduceEvents: ChangeEvent<RxDocumentType>[] = []; for (let index = 0; index < rxChangeEvents.length; index++) { const cE = rxChangeEvents[index]; const eventReduceEvent = rxChangeEventToEventReduceChangeEvent(cE); if (eventReduceEvent) { eventReduceEvents.push(eventReduceEvent); } } const foundNonOptimizeable = eventReduceEvents.find(eventReduceEvent => { const stateResolveFunctionInput: StateResolveFunctionInput<RxDocumentType> = { queryParams, changeEvent: eventReduceEvent, previousResults, keyDocumentMap: previousResultsMap }; const actionName: ActionName = calculateActionName(stateResolveFunctionInput); if (actionName === 'runFullQueryAgain') { return true; } else if (actionName !== 'doNothing') { changed = true; runAction( actionName, queryParams, eventReduceEvent, previousResults, previousResultsMap ); return false; } }); if (foundNonOptimizeable) { return { runFullQueryAgain: true, }; } else { return { runFullQueryAgain: false, changed, newResults: previousResults }; } }