rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
312 lines (285 loc) • 10.5 kB
TypeScript
import type {
BulkWriteRow,
EventBulk,
RxDocumentData,
RxStorageBulkWriteResponse,
RxStorageChangeEvent,
RxStorageCountResult,
RxStorageInstanceCreationParams,
RxStorageQueryResult
} from './rx-storage.ts';
import type {
MangoQuerySelector,
MangoQuerySortPart,
RxJsonSchema,
RxQueryPlan
} from './index.d.ts';
import type {
Observable
} from 'rxjs';
/**
* RxStorage
* This is an interface that abstracts the storage engine.
* This allows us to use RxDB with different storage engines.
*
* @link https://rxdb.info/rx-storage.html
* @link https://github.com/pubkey/rxdb/issues/1636
*/
/**
* A RxStorage is a module that acts
* as a factory that can create multiple RxStorageInstance
* objects.
*
* All data inputs and outputs of a StorageInstance must be plain json objects.
* Do not use Map, Set or anything else that cannot be JSON.stringify-ed.
* This will ensure that the storage can exchange data
* when it is a WebWorker or a WASM process or data is send via BroadcastChannel.
*/
export interface RxStorage<Internals, InstanceCreationOptions> {
/**
* name of the storage engine
* used to detect if plugins do not work so we can throw proper errors.
*/
readonly name: string;
/**
* RxDB version is part of the storage
* so we can have fallbacks and stuff when
* multiple storages with different version are in use
* like in the storage migration plugin.
*/
readonly rxdbVersion: string;
/**
* Creates a storage instance
* that can contain the NoSQL documents of a collection.
*/
createStorageInstance<RxDocType>(
params: RxStorageInstanceCreationParams<RxDocType, InstanceCreationOptions>
): Promise<RxStorageInstance<RxDocType, Internals, InstanceCreationOptions>>;
}
/**
* User provided mango queries will be filled up by RxDB via normalizeMangoQuery()
* so we do not have to do many if-field-exist tests in the internals.
*/
export type FilledMangoQuery<RxDocType> = {
/**
* The selector is required here.
*/
selector: MangoQuerySelector<RxDocumentData<RxDocType>>;
/**
* In contrast to the user-provided MangoQuery,
* the sorting is required here because
* RxDB has to ensure that the primary key is always
* part of the sort params.
*/
sort: MangoQuerySortPart<RxDocumentData<RxDocType>>[];
/**
* In the normalized mango query,
* the index must always be a string[],
* never just a string.
* This makes it easier to use the query because
* we do not have to do an array check.
*/
index?: string[];
/**
* Skip must be set which defaults to 0
*/
skip: number;
limit?: number;
};
/**
* Before sending a query to the storageInstance.query()
* we run it through the query planner and do some normalization
* stuff. Notice that the queryPlan is a hint for the storage and
* it is not required to use it when running queries. Some storages
* might use their own query planning instead.
*/
export type PreparedQuery<RxDocType> = {
// original query from the input
query: FilledMangoQuery<RxDocType>;
queryPlan: RxQueryPlan;
};
export interface RxStorageInstance<
/**
* The type of the documents that can be stored in this instance.
* All documents in an instance must comply to the same schema.
* Also all documents are RxDocumentData with the meta properties like
* _deleted or _rev etc.
*/
RxDocType,
Internals,
InstanceCreationOptions,
CheckpointType = any
> {
readonly databaseName: string;
/**
* Returns the internal data that is used by the storage engine.
*/
readonly internals: Readonly<Internals>;
readonly options: Readonly<InstanceCreationOptions>;
/**
* The schema that defines the documents that are stored in this instance.
* Notice that the schema must be enhanced with the meta properties like
* _meta, _rev and _deleted etc. which are added by fillWithDefaultSettings()
*/
readonly schema: Readonly<RxJsonSchema<RxDocumentData<RxDocType>>>;
readonly collectionName: string;
/**
* (Optional) reference to the underlying persistent storage instance.
* If set, things like replication will run on that storageInstance instead of the parent.
* This is mostly used in things like the memory-synced storage where we want to
* run replications and migrations on the persistent storage instead of the in-memory storage.
*
* Having this is the least hacky option. The only other option would be to toggle all calls to the
* storageInstance by checking the givent context-string. But this would make it impossible
* to run a replication on the parentStorage itself.
*/
readonly underlyingPersistentStorage?: RxStorageInstance<RxDocType, any, any, any>;
/**
* Writes multiple documents to the storage instance.
* The write for each single document is atomic, there
* is no transaction around all documents.
* The written documents must be the newest revision of that documents data.
* If the previous document is not the current newest revision, a conflict error
* must be returned.
* It must be possible that some document writes succeed
* and others error. We need this to have a similar behavior as most NoSQL databases.
*/
bulkWrite(
documentWrites: BulkWriteRow<RxDocType>[],
/**
* Context will be used in all
* changeStream()-events that are emitted as a result
* of that bulkWrite() operation.
* Used in plugins so that we can detect that event X
* comes from operation Y.
*/
context: string
): Promise<RxStorageBulkWriteResponse<RxDocType>>;
/**
* Get Multiple documents by their primary value.
* This must also return deleted documents.
*/
findDocumentsById(
/**
* List of primary values
* of the documents to find.
*/
ids: string[],
/**
* If set to true, deleted documents will also be returned.
*/
withDeleted: boolean
): Promise<
/**
* For better performance, we return an array
* instead of an indexed object because most consumers
* of this anyway have to fill a Map() instance or
* even do only need the list at all.
*/
RxDocumentData<RxDocType>[]
>;
/**
* Runs a NoSQL 'mango' query over the storage
* and returns the found documents data.
* Having all storage instances behave similar
* is likely the most difficult thing when creating a new
* rx-storage implementation.
*/
query(
preparedQuery: PreparedQuery<RxDocType>
): Promise<RxStorageQueryResult<RxDocType>>;
/**
* Returns the amount of non-deleted documents
* that match the given query.
* Sort, skip and limit of the query must be ignored!
*/
count(
preparedQuery: PreparedQuery<RxDocType>
): Promise<RxStorageCountResult>;
/**
* Returns the plain data of a single attachment.
*/
getAttachmentData(
documentId: string,
attachmentId: string,
digest: string
): Promise<string>;
/**
* Returns the current (not the old!) data of all documents that have been changed AFTER the given checkpoint.
* If the returned array does not reach the limit, it can be assumed that the "end" is reached, when paginating over the changes.
* Also returns a new checkpoint for each document which can be used to continue with the pagination from that change on.
* Must never return the same document multiple times in the same call operation.
* This is used by RxDB to known what has changed since X so these docs can be handled by the backup or the replication
* plugin.
*
* Important: This method is optional. If not defined,
* RxDB will manually run a query and use the last returned document
* for checkpointing. In the future we might even remove this method completely
* and let RxDB do the work instead of the RxStorage.
*/
getChangedDocumentsSince?(
limit: number,
/**
* The checkpoint from with to start
* when the events are sorted in time.
* If we want to start from the beginning,
* undefined is used as a checkpoint.
*/
checkpoint?: CheckpointType
): Promise<{
documents: RxDocumentData<RxDocType>[];
/**
* The checkpoint contains data so that another
* call to getChangedDocumentsSince() will continue
* from exactly the last document that was returned before.
*/
checkpoint: CheckpointType;
}>;
/**
* Returns an ongoing stream
* of all changes that happen to the
* storage instance.
* Do not forget to unsubscribe.
*
* If the RxStorage support multi-instance,
* and the storage is persistent,
* then the emitted changes of one RxStorageInstance
* must be also emitted to other instances with the same databaseName+collectionName.
* See ./rx-storage-multiinstance.ts
*/
changeStream(): Observable<EventBulk<RxStorageChangeEvent<RxDocType>, CheckpointType>>;
/**
* Runs a cleanup that removes all tompstones
* of documents that have _deleted set to true
* to free up disc space.
*
* Returns true if all cleanable documents have been removed.
* Returns false if there are more documents to be cleaned up,
* but not all have been purged because that would block the storage for too long.
*/
cleanup(
/**
* The minimum time in milliseconds
* of how long a document must have been deleted
* until it is purged by the cleanup.
*/
minimumDeletedTime: number
): Promise<
/**
* True if all docs cleaned up,
* false if there are more docs to clean up
*/
boolean
>;
/**
* Closes the storage instance so it cannot be used
* anymore and should clear all memory.
* The returned promise must resolve when everything is cleaned up.
*/
close(): Promise<void>;
/**
* Remove the database and
* deletes all of its data.
*/
remove(): Promise<void>;
}