@signaldb/core
Version:
SignalDB is a client-side database that provides a simple MongoDB-like interface to the data with first-class typescript support to achieve an optimistic UI. Data persistence can be achieved by using storage providers that store the data through a JSON in
99 lines (98 loc) • 3.92 kB
JavaScript
const require_intersection = require("./index17.cjs.js");
//#region src/Collection/getIndexInfo.ts
/**
* Retrieves merged index information for a given flat selector by querying multiple
* index providers. Combines results from all index providers to determine matched positions
* and an optimized selector.
* @template T - The type of the items in the collection.
* @template I - The type of the unique identifier for the items.
* @param indexProviders - An array of index providers to query.
* @param selector - The flat selector used to filter items.
* @returns An object containing:
* - `matched`: A boolean indicating if the selector matched any items.
* - `positions`: An array of matched item positions.
* - `optimizedSelector`: A flat selector optimized based on the index results.
*/
function getMergedIndexInfo(indexProviders, selector) {
return indexProviders.reduce((memo, indexProvider) => {
const info = indexProvider.query(selector);
if (!info.matched) return memo;
const optimizedSelector = info.keepSelector ? memo.optimizedSelector : Object.fromEntries(Object.entries(memo.optimizedSelector).filter(([key]) => !info.fields.includes(key)));
return {
matched: true,
positions: [...new Set(memo.matched ? require_intersection.default(memo.positions, info.positions) : info.positions)],
optimizedSelector
};
}, {
matched: false,
positions: [],
optimizedSelector: { ...selector }
});
}
/**
* Retrieves index information for a given complex selector by querying multiple
* index providers. Handles nested `$and` and `$or` conditions in the selector and
* optimizes the selector to minimize processing overhead.
* @template T - The type of the items in the collection.
* @template I - The type of the unique identifier for the items.
* @param indexProviders - An array of index providers to query.
* @param selector - The complex selector used to filter items.
* @returns An object containing:
* - `matched`: A boolean indicating if the selector matched any items.
* - `positions`: An array of matched item positions.
* - `optimizedSelector`: A selector optimized based on the index results, with unused
* conditions removed.
*/
function getIndexInfo(indexProviders, selector) {
if (selector == null || Object.keys(selector).length <= 0) return {
matched: false,
positions: [],
optimizedSelector: selector
};
const { $and, $or, ...rest } = selector;
const flatInfo = getMergedIndexInfo(indexProviders, rest);
let { matched, positions } = flatInfo;
const newSelector = flatInfo.optimizedSelector;
if (Array.isArray($and)) {
const $andNew = [];
for (const sel of $and) {
const { matched: selMatched, positions: selPositions, optimizedSelector } = getIndexInfo(indexProviders, sel);
if (selMatched) {
positions = matched ? require_intersection.default(positions, selPositions) : selPositions;
matched = true;
if (Object.keys(optimizedSelector).length > 0) $andNew.push(optimizedSelector);
} else $andNew.push(sel);
}
if ($andNew.length > 0) newSelector.$and = $andNew;
}
if (Array.isArray($or)) {
const $orNew = [];
const matchedBefore = matched;
const positionsBefore = positions;
let hasNonIndexField = false;
for (const sel of $or) {
const { matched: selMatched, positions: selPositions, optimizedSelector } = getIndexInfo(indexProviders, sel);
if (selMatched) {
positions = [...new Set([...positions, ...selPositions])];
matched = true;
if (Object.keys(optimizedSelector).length > 0) $orNew.push(optimizedSelector);
} else {
$orNew.push(sel);
hasNonIndexField = true;
}
}
if ($orNew.length > 0) newSelector.$or = $orNew;
if (hasNonIndexField) {
newSelector.$or = $or;
matched = matchedBefore;
positions = positionsBefore;
}
}
return {
matched,
positions: positions || [],
optimizedSelector: newSelector
};
}
//#endregion
exports.default = getIndexInfo;