tinybase
Version:
A reactive data store and sync engine.
1,211 lines (1,184 loc) • 41.5 kB
text/typescript
/**
* The indexes module of the TinyBase project provides the ability to create and
* track indexes of the data in Store objects.
*
* The main entry point to this module is the createIndexes function, which
* returns a new Indexes object. From there, you can create new Index
* definitions, access the contents of those Indexes directly, and register
* listeners for when they change.
* @packageDocumentation
* @module indexes
* @since v1.0.0
*/
import type {
CellIdFromSchema,
TableIdFromSchema,
} from '../../_internal/store/with-schemas/index.d.cts';
import type {
Id,
IdOrNull,
Ids,
SortKey,
} from '../../common/with-schemas/index.d.cts';
import type {
GetCell,
OptionalSchemas,
OptionalTablesSchema,
RowCallback,
Store,
} from '../../store/with-schemas/index.d.cts';
/**
* The Index type represents the concept of a map of Slice objects, keyed by Id.
*
* The Ids in a Slice represent Row objects from a Table that all have a derived
* string value in common, as described by the setIndexDefinition method. Those
* values are used as the key for each Slice in the overall Index object.
*
* Note that the Index type is not actually used in the API, and you instead
* enumerate and access its structure with the getSliceIds method and
* getSliceRowIds method.
* @category Concept
* @since v1.0.0
*/
export type Index = {[sliceId: Id]: Slice};
/**
* The Slice type represents the concept of a set of Row objects that comprise
* part of an Index.
*
* The Ids in a Slice represent Row objects from a Table that all have a derived
* string value in common, as described by the setIndexDefinition method.
*
* Note that the Slice type is not actually used in the API, and you instead get
* Row Ids directly with the getSliceRowIds method.
* @category Concept
* @since v1.0.0
*/
export type Slice = Ids;
/**
* The IndexCallback type describes a function that takes an Index's Id and a
* callback to loop over each Slice within it.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* (
* indexId: Id,
* forEachSlice: (sliceCallback: SliceCallback) => void,
* ) => void;
* ```
*
* A IndexCallback is provided when using the forEachIndex method, so that you
* can do something based on every Index in the Indexes object. See that method
* for specific examples.
* @param indexId The Id of the Index that the callback can operate on.
* @param forEachSlice A function that will let you iterate over the Slice
* objects in this Index.
* @category Callback
* @since v1.0.0
*/
export type IndexCallback<Schema extends OptionalTablesSchema> = (
indexId: Id,
forEachSlice: (sliceCallback: SliceCallback<Schema>) => void,
) => void;
/**
* The SliceCallback type describes a function that takes a Slice's Id and a
* callback to loop over each Row within it.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* (
* sliceId: Id,
* forEachRow: (rowCallback: RowCallback) => void,
* ) => void;
* ```
*
* A SliceCallback is provided when using the forEachSlice method, so that you
* can do something based on every Slice in an Index. See that method for
* specific examples.
* @param sliceId The Id of the Slice that the callback can operate on.
* @param forEachRow A function that will let you iterate over the Row objects
* in this Slice.
* @category Callback
* @since v1.0.0
*/
export type SliceCallback<Schema extends OptionalTablesSchema> = (
sliceId: Id,
forEachRow: (rowCallback: RowCallback<Schema>) => void,
) => void;
/**
* The IndexIdsListener type describes a function that is used to listen to
* Index definitions being added or removed.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* (indexes: Indexes) => void;
* ```
*
* A IndexIdsListener is provided when using the addIndexIdsListener method.
* See that method for specific examples.
*
* When called, an IndexIdsListener is given a reference to the Indexes object.
* @param indexes A reference to the Indexes object that changed.
* @category Listener
* @since v1.0.0
*/
export type IndexIdsListener<Schemas extends OptionalSchemas> = (
indexes: Indexes<Schemas>,
) => void;
/**
* The SliceIdsListener type describes a function that is used to listen to
* changes to the Slice Ids in an Index.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* (indexes: Indexes, indexId: Id) => void;
* ```
*
* A SliceIdsListener is provided when using the addSliceIdsListener method. See
* that method for specific examples.
*
* When called, a SliceIdsListener is given a reference to the Indexes object,
* and the Id of the Index that changed.
* @param indexes A reference to the Indexes object that changed.
* @param indexId The Id of the Index that changed.
* @category Listener
* @since v1.0.0
*/
export type SliceIdsListener<Schemas extends OptionalSchemas> = (
indexes: Indexes<Schemas>,
indexId: Id,
) => void;
/**
* The SliceRowIdsListener type describes a function that is used to listen to
* changes to the Row Ids in a Slice.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* (
* indexes: Indexes,
* indexId: Id,
* sliceId: Id,
* ) => void;
* ```
*
* A SliceRowIdsListener is provided when using the addSliceRowIdsListener
* method. See that method for specific examples.
*
* When called, a SliceRowIdsListener is given a reference to the Indexes
* object, the Id of the Index that changed, and the Id of the Slice whose Row
* Ids changed.
* @param indexes A reference to the Indexes object that changed.
* @param indexId The Id of the Index that changed.
* @param sliceId The Id of the Slice that changed.
* @category Listener
* @since v1.0.0
*/
export type SliceRowIdsListener<Schemas extends OptionalSchemas> = (
indexes: Indexes<Schemas>,
indexId: Id,
sliceId: Id,
) => void;
/**
* The IndexesListenerStats type describes the number of listeners registered
* with the Indexes object, and can be used for debugging purposes.
*
* A IndexesListenerStats object is returned from the getListenerStats method.
* @category Development
* @since v1.0.0
*/
export type IndexesListenerStats = {
/**
* The number of SlideIdsListener functions registered with the Indexes
* object.
* @category Stat
* @since v1.0.0
*/
sliceIds: number;
/**
* The number of SliceRowIdsListener functions registered with the Indexes
* object.
* @category Stat
* @since v1.0.0
*/
sliceRowIds: number;
};
/**
* An Indexes object lets you look up all the Row objects in a Table that have a
* certain Cell value.
*
* This is useful for creating filtered views of a Table, or simple search
* functionality.
*
* Create an Indexes object easily with the createIndexes function. From there,
* you can add new Index definitions (with the setIndexDefinition method), query
* their contents (with the getSliceIds method and getSliceRowIds method), and
* add listeners for when they change (with the addSliceIdsListener method and
* addSliceRowIdsListener method).
*
* This module defaults to indexing Row objects by one of their Cell values.
* However, far more complex indexes can be configured with a custom function.
* @example
* This example shows a very simple lifecycle of an Indexes object: from
* creation, to adding a definition, getting its contents, and then registering
* and removing a listener for it.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition(
* 'bySpecies', // indexId
* 'pets', // tableId to index
* 'species', // cellId to index
* );
*
* console.log(indexes.getSliceIds('bySpecies'));
* // -> ['dog', 'cat']
* console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
* // -> ['fido', 'cujo']
*
* const listenerId = indexes.addSliceIdsListener('bySpecies', () => {
* console.log(indexes.getSliceIds('bySpecies'));
* });
* store.setRow('pets', 'lowly', {species: 'worm'});
* // -> ['dog', 'cat', 'worm']
*
* indexes.delListener(listenerId);
* indexes.destroy();
* ```
* @see Using Indexes guides
* @see Rolling Dice demos
* @see Country demo
* @see Todo App demos
* @see Word Frequencies demo
* @category Indexes
* @since v1.0.0
*/
export interface Indexes<in out Schemas extends OptionalSchemas> {
/**
* The setIndexDefinition method lets you set the definition of an Index.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* setIndexDefinition(
* indexId: Id,
* tableId: Id,
* getSliceIdOrIds?: Id | ((getCell: GetCell, rowId: Id) => Id | Ids),
* getSortKey?: Id | ((getCell: GetCell, rowId: Id) => SortKey),
* sliceIdSorter?: (sliceId1: Id, sliceId2: Id) => number,
* rowIdSorter?: (sortKey1: SortKey, sortKey2: SortKey, sliceId: Id) => number,
* ): Indexes;
* ```
*
* Every Index definition is identified by a unique Id, and if you re-use an
* existing Id with this method, the previous definition is overwritten.
*
* An Index is a keyed map of Slice objects, each of which is a list of Row
* Ids from a given Table. Therefore the definition must specify the Table (by
* its Id) to be indexed.
*
* The Ids in a Slice represent Row objects from a Table that all have a
* derived string value in common, as described by this method. Those values
* are used as the key for each Slice in the overall Index object.
*
* Without the third `getSliceIdOrIds` parameter, the Index will simply have a
* single Slice, keyed by an empty string. But more often you will specify a
* Cell value containing the Slice Id that the Row should belong to.
* Alternatively, a custom function can be provided that produces your own
* Slice Id from the local Row as a whole. Since v2.1, the custom function can
* return an array of Slice Ids, each of which the Row will then belong to.
*
* The fourth `getSortKey` parameter specifies a Cell Id to get a value (or a
* function that processes a whole Row to get a value) that is used to sort
* the Row Ids within each Slice in the Index.
*
* The fifth parameter, `sliceIdSorter`, lets you specify a way to sort the
* Slice Ids when you access the Index, which may be useful if you are trying
* to create an alphabetic Index of Row entries. If not specified, the order
* of the Slice Ids will match the order of Row insertion.
*
* The final parameter, `rowIdSorter`, lets you specify a way to sort the Row
* Ids within each Slice, based on the `getSortKey` parameter. This may be
* useful if you are trying to keep Rows in a determined order relative to
* each other in the Index. If omitted, the Row Ids are sorted alphabetically,
* based on the `getSortKey` parameter.
*
* The two 'sorter' parameters, `sliceIdSorter` and `rowIdSorter`, are
* functions that take two values and return a positive or negative number for
* when they are in the wrong or right order, respectively. This is exactly
* the same as the 'compareFunction' that is used in the standard JavaScript
* array `sort` method, with the addition that `rowIdSorter` also takes the
* Slice Id parameter, in case you want to sort Row Ids differently in each
* Slice. You can use the convenient defaultSorter function to default this to
* be alphanumeric.
* @param indexId The Id of the Index to define.
* @param tableId The Id of the Table the Index will be generated from.
* @param getSliceIdOrIds Either the Id of the Cell containing, or a function
* that produces, the Id that is used to indicate which Slice in the Index the
* Row Id should be in. Defaults to a function that returns `''` (meaning that
* if this `getSliceIdOrIds` parameter is omitted, the Index will simply
* contain a single Slice containing all the Row Ids in the Table). Since
* v2.1, this can return an array of Slice Ids, each of which the Row will
* then belong to.
* @param getSortKey Either the Id of the Cell containing, or a function that
* produces, the value that is used to sort the Row Ids in each Slice.
* @param sliceIdSorter A function that takes two Slice Id values and returns
* a positive or negative number to indicate how they should be sorted.
* @param rowIdSorter A function that takes two Row Id values (and a slice Id)
* and returns a positive or negative number to indicate how they should be
* sorted.
* @returns A reference to the Indexes object.
* @example
* This example creates a Store, creates an Indexes object, and defines a
* simple Index based on the values in the `species` Cell.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* console.log(indexes.getSliceIds('bySpecies'));
* // -> ['dog', 'cat']
* console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
* // -> ['fido', 'cujo']
* ```
* @example
* This example creates a Store, creates an Indexes object, and defines an
* Index based on the first letter of the pets' names.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('byFirst', 'pets', (_, rowId) => rowId[0]);
*
* console.log(indexes.getSliceIds('byFirst'));
* // -> ['f', 'c']
* console.log(indexes.getSliceRowIds('byFirst', 'f'));
* // -> ['fido', 'felix']
* ```
* @example
* This example creates a Store, creates an Indexes object, and defines an
* Index based on each of the letters present in the pets' names.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* rex: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('containsLetter', 'pets', (_, rowId) =>
* rowId.split(''),
* );
*
* console.log(indexes.getSliceIds('containsLetter'));
* // -> ['f', 'i', 'd', 'o', 'e', 'l', 'x', 'r']
* console.log(indexes.getSliceRowIds('containsLetter', 'i'));
* // -> ['fido', 'felix']
* console.log(indexes.getSliceRowIds('containsLetter', 'x'));
* // -> ['felix', 'rex']
* ```
* @example
* This example creates a Store, creates an Indexes object, and defines an
* Index based on the first letter of the pets' names. The Slice Ids (and Row
* Ids within them) are alphabetically sorted.
*
* ```js
* import {createIndexes, createStore, defaultSorter} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition(
* 'byFirst', // indexId
* 'pets', // tableId
* (_, rowId) => rowId[0], // each Row's sliceId
* (_, rowId) => rowId, // each Row's sort key
* defaultSorter, // sort Slice Ids
* defaultSorter, // sort Row Ids
* );
*
* console.log(indexes.getSliceIds('byFirst'));
* // -> ['c', 'f']
* console.log(indexes.getSliceRowIds('byFirst', 'f'));
* // -> ['felix', 'fido']
* ```
* @category Configuration
* @since v1.0.0
*/
setIndexDefinition<TableId extends TableIdFromSchema<Schemas[0]>>(
indexId: Id,
tableId: TableId,
getSliceIdOrIds?:
| CellIdFromSchema<Schemas[0], TableId>
| ((getCell: GetCell<Schemas[0], TableId>, rowId: Id) => Id | Ids),
getSortKey?:
| CellIdFromSchema<Schemas[0], TableId>
| ((getCell: GetCell<Schemas[0], TableId>, rowId: Id) => SortKey),
sliceIdSorter?: (sliceId1: Id, sliceId2: Id) => number,
rowIdSorter?: (sortKey1: SortKey, sortKey2: SortKey, sliceId: Id) => number,
): Indexes<Schemas>;
/**
* The delIndexDefinition method removes an existing Index definition.
* @param indexId The Id of the Index to remove.
* @returns A reference to the Indexes object.
* @example
* This example creates a Store, creates an Indexes object, defines a simple
* Index, and then removes it.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* delIndexDefinition(indexId: Id): Indexes;
* ```
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
* console.log(indexes.getIndexIds());
* // -> ['bySpecies']
*
* indexes.delIndexDefinition('bySpecies');
* console.log(indexes.getIndexIds());
* // -> []
* ```
* @category Configuration
* @since v1.0.0
*/
delIndexDefinition(indexId: Id): Indexes<Schemas>;
/**
* The getStore method returns a reference to the underlying Store that is
* backing this Indexes object.
* @returns A reference to the Store.
* @example
* This example creates an Indexes object against a newly-created Store and
* then gets its reference in order to update its data.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getStore(): Store;
* ```
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const indexes = createIndexes(createStore());
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
* indexes.getStore().setCell('pets', 'fido', 'species', 'dog');
* console.log(indexes.getSliceIds('bySpecies'));
* // -> ['dog']
* ```
* @category Getter
* @since v1.0.0
*/
getStore(): Store<Schemas>;
/**
* The getIndexIds method returns an array of the Index Ids registered with
* this Indexes object.
* @returns An array of Ids.
* @example
* This example creates an Indexes object with two definitions, and then gets
* the Ids of the definitions.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const indexes = createIndexes(createStore())
* .setIndexDefinition('bySpecies', 'pets', 'species')
* .setIndexDefinition('byColor', 'pets', 'color');
*
* console.log(indexes.getIndexIds());
* // -> ['bySpecies', 'byColor']
* ```
* @category Getter
* @since v1.0.0
*/
getIndexIds(): Ids;
/**
* The forEachIndex method takes a function that it will then call for each
* Index in a specified Indexes object.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* forEachIndex(indexCallback: IndexCallback): void;
* ```
*
* This method is useful for iterating over the structure of the Indexes
* object in a functional style. The `indexCallback` parameter is a
* IndexCallback function that will be called with the Id of each Index, and
* with a function that can then be used to iterate over each Slice of the
* Index, should you wish.
* @param indexCallback The function that should be called for every Index.
* @example
* This example iterates over each Index in an Indexes object, and lists each
* Slice Id within them.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* cujo: {species: 'dog', color: 'black'},
* });
* const indexes = createIndexes(store)
* .setIndexDefinition('bySpecies', 'pets', 'species')
* .setIndexDefinition('byColor', 'pets', 'color');
*
* indexes.forEachIndex((indexId, forEachSlice) => {
* console.log(indexId);
* forEachSlice((sliceId) => console.log(`- ${sliceId}`));
* });
* // -> 'bySpecies'
* // -> '- dog'
* // -> '- cat'
* // -> 'byColor'
* // -> '- brown'
* // -> '- black'
* ```
* @category Iterator
* @since v1.0.0
*/
forEachIndex(indexCallback: IndexCallback<Schemas[0]>): void;
/**
* The forEachSlice method takes a function that it will then call for each
* Slice in a specified Index.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* forEachSlice(indexId: Id, sliceCallback: SliceCallback): void;
* ```
*
* This method is useful for iterating over the Slice structure of the Index
* in a functional style. The `rowCallback` parameter is a RowCallback
* function that will be called with the Id and value of each Row in the
* Slice.
* @param indexId The Id of the Index to iterate over.
* @param sliceCallback The function that should be called for every Slice.
* @example
* This example iterates over each Row in a Slice, and lists its Id.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* indexes.forEachSlice('bySpecies', (sliceId, forEachRow) => {
* console.log(sliceId);
* forEachRow((rowId) => console.log(`- ${rowId}`));
* });
* // -> 'dog'
* // -> '- fido'
* // -> '- cujo'
* // -> 'cat'
* // -> '- felix'
* ```
* @category Iterator
* @since v1.0.0
*/
forEachSlice(indexId: Id, sliceCallback: SliceCallback<Schemas[0]>): void;
/**
* The hasIndex method returns a boolean indicating whether a given Index
* exists in the Indexes object.
* @param indexId The Id of a possible Index in the Indexes object.
* @returns Whether an Index with that Id exists.
* @example
* This example shows two simple Index existence checks.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const indexes = createIndexes(createStore());
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
* console.log(indexes.hasIndex('bySpecies'));
* // -> true
* console.log(indexes.hasIndex('byColor'));
* // -> false
* ```
* @category Getter
* @since v1.0.0
*/
hasIndex(indexId: Id): boolean;
/**
* The hasSlice method returns a boolean indicating whether a given Slice
* exists in the Indexes object.
* @param indexId The Id of a possible Index in the Indexes object.
* @param sliceId The Id of a possible Slice in the Index.
* @returns Whether a Slice with that Id exists.
* @example
* This example shows two simple Index existence checks.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
* console.log(indexes.hasSlice('bySpecies', 'dog'));
* // -> true
* console.log(indexes.hasSlice('bySpecies', 'worm'));
* // -> false
* ```
* @category Getter
* @since v1.0.0
*/
hasSlice(indexId: Id, sliceId: Id): boolean;
/**
* The getTableId method returns the Id of the underlying Table that is
* backing an Index.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* getTableId(indexId: Id): Id | undefined;
* ```
*
* If the Index Id is invalid, the method returns `undefined`.
* @param indexId The Id of an Index.
* @returns The Id of the Table backing the Index, or `undefined`.
* @example
* This example creates an Indexes object, a single Index definition, and then
* queries it (and a non-existent definition) to get the underlying Table Id.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const indexes = createIndexes(createStore());
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* console.log(indexes.getTableId('bySpecies'));
* // -> 'pets'
* console.log(indexes.getTableId('byColor'));
* // -> undefined
* ```
* @category Getter
* @since v1.0.0
*/
getTableId<TableId extends TableIdFromSchema<Schemas[0]>>(
indexId: Id,
): TableId | undefined;
/**
* The getSliceIds method gets the list of Slice Ids in an Index.
*
* If the identified Index does not exist (or if the definition references a
* Table that does not exist) then an empty array is returned.
* @param indexId The Id of the Index.
* @returns The Slice Ids in the Index, or an empty array.
* @example
* This example creates a Store, creates an Indexes object, and defines a
* simple Index. It then uses getSliceIds to see the available Slice Ids in
* the Index (and also the Slice Ids in an Index that has not been defined).
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* console.log(indexes.getSliceIds('bySpecies'));
* // -> ['dog', 'cat']
* console.log(indexes.getSliceIds('byColor'));
* // -> []
* ```
* @category Getter
* @since v1.0.0
*/
getSliceIds(indexId: Id): Ids;
/**
* The getSliceRowIds method gets the list of Row Ids in a given Slice, within
* a given Index.
*
* If the identified Index or Slice do not exist (or if the definition
* references a Table that does not exist) then an empty array is returned.
* @param indexId The Id of the Index.
* @param sliceId The Id of the Slice in the Index.
* @returns The Row Ids in the Slice, or an empty array.
* @example
* This example creates a Store, creates an Indexes object, and defines a
* simple Index. It then uses getSliceRowIds to see the Row Ids in the Slice
* (and also the Row Ids in Slices that do not exist).
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
* // -> ['fido', 'cujo']
* console.log(indexes.getSliceRowIds('bySpecies', 'worm'));
* // -> []
* console.log(indexes.getSliceRowIds('byColor', 'brown'));
* // -> []
* ```
* @category Getter
* @since v1.0.0
*/
getSliceRowIds(indexId: Id, sliceId: Id): Ids;
/**
* The addIndexIdsListener method registers a listener function with the
* Indexes object that will be called whenever an Index definition is added or
* removed.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* addIndexIdsListener(listener: IndexIdsListener): Id;
* ```
*
* The provided listener is an IndexIdsListener function, and will be called
* with a reference to the Indexes object.
* @param listener The function that will be called whenever an Index
* definition is added or removed.
* @example
* This example creates a Store, an Indexes object, and then registers a
* listener that responds to the addition and the removal of an Index
* definition.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* const listenerId = indexes.addIndexIdsListener((indexes) => {
* console.log(indexes.getIndexIds());
* });
*
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
* // -> ['bySpecies']
* indexes.delIndexDefinition('bySpecies');
* // -> []
*
* indexes.delListener(listenerId);
* ```
* @category Listener
* @since v4.1.0
*/
addIndexIdsListener(listener: IndexIdsListener<Schemas>): Id;
/**
* The addSliceIdsListener method registers a listener function with the
* Indexes object that will be called whenever the Slice Ids in an Index
* change.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* addSliceIdsListener(indexId: IdOrNull, listener: SliceIdsListener): Id;
* ```
*
* You can either listen to a single Index (by specifying the Index Id as the
* method's first parameter), or changes to any Index (by providing a `null`
* wildcard).
*
* The provided listener is a SliceIdsListener function, and will be called
* with a reference to the Indexes object, and the Id of the Index that
* changed.
* @param indexId The Id of the Index to listen to, or `null` as a wildcard.
* @param listener The function that will be called whenever the Slice Ids in
* the Index change.
* @returns A unique Id for the listener that can later be used to remove it.
* @example
* This example creates a Store, an Indexes object, and then registers a
* listener that responds to any changes to a specific Index.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* const listenerId = indexes.addSliceIdsListener('bySpecies', (indexes) => {
* console.log('Slice Ids for bySpecies index changed');
* console.log(indexes.getSliceIds('bySpecies'));
* });
*
* store.setRow('pets', 'lowly', {species: 'worm'});
* // -> 'Slice Ids for bySpecies index changed'
* // -> ['dog', 'cat', 'worm']
*
* indexes.delListener(listenerId);
* ```
* @example
* This example creates a Store, an Indexes object, and then registers a
* listener that responds to any changes to any Index.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* cujo: {species: 'dog', color: 'brown'},
* });
*
* const indexes = createIndexes(store)
* .setIndexDefinition('bySpecies', 'pets', 'species')
* .setIndexDefinition('byColor', 'pets', 'color');
*
* const listenerId = indexes.addSliceIdsListener(
* null,
* (indexes, indexId) => {
* console.log(`Slice Ids for ${indexId} index changed`);
* console.log(indexes.getSliceIds(indexId));
* },
* );
*
* store.setRow('pets', 'lowly', {species: 'worm', color: 'pink'});
* // -> 'Slice Ids for bySpecies index changed'
* // -> ['dog', 'cat', 'worm']
* // -> 'Slice Ids for byColor index changed'
* // -> ['brown', 'black', 'pink']
*
* indexes.delListener(listenerId);
* ```
* @category Listener
* @since v1.0.0
*/
addSliceIdsListener(
indexId: IdOrNull,
listener: SliceIdsListener<Schemas>,
): Id;
/**
* The addSliceRowIdsListener method registers a listener function with the
* Indexes object that will be called whenever the Row Ids in a Slice change.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* addSliceRowIdsListener(
* indexId: IdOrNull,
* sliceId: IdOrNull,
* listener: SliceRowIdsListener,
* ): Id;
* ```
*
* You can either listen to a single Slice (by specifying the Index Id and
* Slice Id as the method's first two parameters), or changes to any Slice (by
* providing `null` wildcards).
*
* Both, either, or neither of the `indexId` and `sliceId` parameters can be
* wildcarded with `null`. You can listen to a specific Slice in a specific
* Index, any Slice in a specific Index, a specific Slice in any Index, or any
* Slice in any Index.
*
* The provided listener is a SliceRowIdsListener function, and will be called
* with a reference to the Indexes object, the Id of the Index, and the Id of
* the Slice that changed.
* @param indexId The Id of the Index to listen to, or `null` as a wildcard.
* @param sliceId The Id of the Slice to listen to, or `null` as a wildcard.
* @param listener The function that will be called whenever the Row Ids in
* the Slice change.
* @returns A unique Id for the listener that can later be used to remove it.
* @example
* This example creates a Store, an Indexes object, and then registers a
* listener that responds to any changes to a specific Slice.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* const listenerId = indexes.addSliceRowIdsListener(
* 'bySpecies',
* 'dog',
* (indexes) => {
* console.log('Row Ids for dog slice in bySpecies index changed');
* console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
* },
* );
*
* store.setRow('pets', 'toto', {species: 'dog'});
* // -> 'Row Ids for dog slice in bySpecies index changed'
* // -> ['fido', 'cujo', 'toto']
*
* indexes.delListener(listenerId);
* ```
* @example
* This example creates a Store, an Indexes object, and then registers a
* listener that responds to any changes to any Slice.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog', color: 'brown'},
* felix: {species: 'cat', color: 'black'},
* cujo: {species: 'dog', color: 'black'},
* });
*
* const indexes = createIndexes(store)
* .setIndexDefinition('bySpecies', 'pets', 'species')
* .setIndexDefinition('byColor', 'pets', 'color');
*
* const listenerId = indexes.addSliceRowIdsListener(
* null,
* null,
* (indexes, indexId, sliceId) => {
* console.log(
* `Row Ids for ${sliceId} slice in ${indexId} index changed`,
* );
* console.log(indexes.getSliceRowIds(indexId, sliceId));
* },
* );
*
* store.setRow('pets', 'toto', {species: 'dog', color: 'brown'});
* // -> 'Row Ids for dog slice in bySpecies index changed'
* // -> ['fido', 'cujo', 'toto']
* // -> 'Row Ids for brown slice in byColor index changed'
* // -> ['fido', 'toto']
*
* indexes.delListener(listenerId);
* ```
* @category Listener
* @since v1.0.0
*/
addSliceRowIdsListener(
indexId: IdOrNull,
sliceId: IdOrNull,
listener: SliceRowIdsListener<Schemas>,
): Id;
/**
* The delListener method removes a listener that was previously added to the
* Indexes object.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* delListener(listenerId: Id): Indexes;
* ```
*
* Use the Id returned by whichever method was used to add the listener. Note
* that the Indexes object may re-use this Id for future listeners added to
* it.
* @param listenerId The Id of the listener to remove.
* @returns A reference to the Indexes object.
* @example
* This example creates a Store, an Indexes object, registers a listener, and
* then removes it.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
*
* const listenerId = indexes.addSliceIdsListener('bySpecies', () => {
* console.log('Slice Ids for bySpecies index changed');
* });
*
* store.setRow('pets', 'lowly', {species: 'worm'});
* // -> 'Slice Ids for bySpecies index changed'
*
* indexes.delListener(listenerId);
*
* store.setRow('pets', 'toto', {species: 'dog'});
* // -> undefined
* // The listener is not called.
* ```
* @category Listener
* @since v1.0.0
*/
delListener(listenerId: Id): Indexes<Schemas>;
/**
* The destroy method should be called when this Indexes object is no longer
* used.
*
* This guarantees that all of the listeners that the object registered with
* the underlying Store are removed and it can be correctly garbage collected.
* @example
* This example creates a Store, adds an Indexes object with a
* definition (that registers a RowListener with the underlying Store),
* and then destroys it again, removing the listener.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore().setTable('pets', {
* fido: {species: 'dog'},
* felix: {species: 'cat'},
* cujo: {species: 'dog'},
* });
*
* const indexes = createIndexes(store);
* indexes.setIndexDefinition('bySpecies', 'pets', 'species');
* console.log(store.getListenerStats().row);
* // -> 1
*
* indexes.destroy();
*
* console.log(store.getListenerStats().row);
* // -> 0
* ```
* @category Lifecycle
* @since v1.0.0
*/
destroy(): void;
/**
* The getListenerStats method provides a set of statistics about the
* listeners registered with the Indexes object, and is used for debugging
* purposes.
*
* The IndexesListenerStats object contains a breakdown of the different types
* of listener.
*
* The method is intended to be used during development to ensure your
* application is not leaking listener registrations, for example.
* @returns A IndexesListenerStats object containing Indexes listener
* statistics.
* @example
* This example gets the listener statistics of an Indexes object.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore();
* const indexes = createIndexes(store);
* indexes.addSliceIdsListener(null, () => {
* console.log('Slice Ids changed');
* });
* indexes.addSliceRowIdsListener(null, null, () => {
* console.log('Slice Row Ids changed');
* });
*
* console.log(indexes.getListenerStats());
* // -> {sliceIds: 1, sliceRowIds: 1}
* ```
* @category Development
* @since v1.0.0
*/
getListenerStats(): IndexesListenerStats;
}
/**
* The createIndexes function creates an Indexes object, and is the main entry
* point into the indexes module.
*
* This has schema-based typing. The following is a simplified representation:
*
* ```ts override
* createIndexes(store: Store): Indexes;
* ```
*
* A given Store can only have one Indexes object associated with it. If you
* call this function twice on the same Store, your second call will return a
* reference to the Indexes object created by the first.
* @param store The Store for which to register Index definitions.
* @returns A reference to the new Indexes object.
* @example
* This example creates an Indexes object.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore();
* const indexes = createIndexes(store);
* console.log(indexes.getIndexIds());
* // -> []
* ```
* @example
* This example creates an Indexes object, and calls the method a second time
* for the same Store to return the same object.
*
* ```js
* import {createIndexes, createStore} from 'tinybase';
*
* const store = createStore();
* const indexes1 = createIndexes(store);
* const indexes2 = createIndexes(store);
* console.log(indexes1 === indexes2);
* // -> true
* ```
* @category Creation
* @since v1.0.0
*/
export function createIndexes<Schemas extends OptionalSchemas>(
store: Store<Schemas>,
): Indexes<Schemas>;