src/app/core/database/database.ts

Description

Based upon PouchDb changes feed format.

Index

Properties

Indexable

[key: string]: any

Properties

_deleted
_deleted: boolean
Type : boolean
Optional
_id
_id: string
Type : string
_rev
_rev: string
Type : string
import { Observable } from "rxjs";

/**
 * An implementation of this abstract class provides functions for direct database access.
 * This interface is an extension of the [PouchDB API](https://pouchdb.com/api.html).
 *
 * PLEASE NOTE:
 * Direct access to the Database layer is rarely necessary, and you should probably use EntityMapperService instead.
 * Database is not an Angular Service and has to be accessed through the DatabaseResolverService.
 */
export abstract class Database {
  constructor(protected dbName: string) {}

  /**
   * Initialize the database fully after its initial creation,
   * e.g. once user details are available.
   * @param dbName A special database name, if different from the default name passed in the constructor
   */
  abstract init(dbName?: string);

  /**
   * Whether the database is already initialized and ready for use.
   */
  abstract isInitialized(): boolean;

  /**
   * Load a single document by id from the database.
   * @param id The primary key of the document to be loaded
   * @param options Optional options for the database engine (PouchDB)
   */
  abstract get(id: string, options?: GetOptions): Promise<any>;

  /**
   * Load all documents (matching the given PouchDB options) from the database.
   *
   * Normally you should rather use "getAll()" or another well typed method of this class
   * instead of passing PouchDB specific options here
   * because that will make your code tightly coupled with PouchDB rather than any other database provider.
   *
   * @param options PouchDB options object as in the normal PouchDB library
   */
  abstract allDocs(options?: GetAllOptions): Promise<any>;

  /**
   * Save a document to the database.
   * @param object The document to be saved
   * @param forceUpdate (Optional) Whether conflicts should be ignored and an existing conflicting document forcefully overwritten.
   */
  abstract put(object: any, forceUpdate?: boolean): Promise<any>;

  /**
   * Save a bunch of documents at once to the database
   * @param objects The documents to be saved
   * @param forceUpdate (Optional) Whether conflicts should be ignored and existing conflicting documents forcefully overwritten.
   * @returns array holding success responses or errors depending on the success of the operation
   */
  abstract putAll(objects: any[], forceUpdate?: boolean): Promise<any[]>;

  /**
   * Delete a document from the database
   * @param object The document to be deleted (usually this object must at least contain the _id and _rev)
   */
  abstract remove(object: any): Promise<any>;

  /**
   * Permanently remove a document from the local database without syncing the deletion to the server.
   *
   * Unlike {@link remove}, purge leaves no tombstone and does not affect the remote database.
   * Throws if not supported by the current implementation.
   *
   * @param id The document ID to purge
   */
  purge(_id: string): Promise<boolean> {
    throw new Error("purge() is not supported by this database implementation");
  }

  /**
   * Uses the PouchDB-find plugin {@link https://github.com/apache/pouchdb/tree/master/packages/node_modules/pouchdb-find}
   * This supports querying using the Mango Query Language {@link https://pouchdb.com/guides/mango-queries.html#query-language}
   * There might be differences in queries between a local PouchDB and the CouchDB.
   *
   * Pagination uses an opaque `bookmark` cursor rather than `skip`: passing the
   * `bookmark` returned by a previous call continues right after those
   * results. Unlike `skip`, this also works correctly when the remote CouchDB
   * applies permission filtering to the query (see
   * {@link https://github.com/Aam-Digital/replication-backend/pull/330}).
   * A bookmark is forward-only - it cannot be used to jump backwards - so
   * callers that need to revisit earlier pages must cache them themselves.
   *
   * @param prefix of the entity to be queried
   * @param query the Mango Query Language query
   * @param page additional pagination options
   * @param sort additional sorting options
   */
  abstract find(
    prefix: string,
    query: any,
    page?: { limit?: number; bookmark?: string },
    sort?: { prop?: string; dir?: "asc" | "desc" },
  ): Promise<{ docs: any[]; bookmark?: string }>;

  /**
   * Query data from the database based on a more complex, indexed request.
   *
   * This is directly calling the PouchDB implementation of this function.
   * Also see the documentation there: {@link https://pouchdb.com/api.html#query_database}
   *
   * @param fun The name of a previously saved database index
   * @param options Additional options for the query, like a `key`. See the PouchDB docs for details.
   */
  abstract query(fun: any, options?: QueryOptions): Promise<any>;

  /**
   * Create a database index to `query()` certain data more efficiently in the future.
   *
   * Also see the PouchDB documentation regarding indices and queries: {@link https://pouchdb.com/api.html#query_database}
   *
   * @param designDoc The PouchDB style design document for the map/reduce query
   */
  abstract saveDatabaseIndex(designDoc: any): Promise<any>;

  /**
   * Load all documents (with the given prefix) from the database.
   * @param prefix The string prefix of document ids that should be retrieved
   */
  getAll(prefix = ""): Promise<Array<any>> {
    return this.allDocs({
      include_docs: true,
      startkey: prefix,
      endkey: prefix + "\ufff0",
    });
  }

  /**
   * Delete all documents of the database matching the given filter.
   *
   * Note that `getAll()` also returns the database's own "_design/..." index
   * documents, so a filter that does not exclude them deletes the indices as well.
   * They are only recreated when the app starts up again
   * (see {@link DatabaseIndexingService}), so only delete them if the calling code
   * reloads the app or restores the documents immediately afterwards.
   *
   * @param shouldDelete filter deciding for each document whether it is deleted.
   *        Pass a specialized document type if the filter reads fields other than `_id`.
   * @returns the number of deleted documents
   */
  async removeAll<T extends { _id: string }>(
    shouldDelete: (doc: T) => boolean,
  ): Promise<number> {
    const allDocs: T[] = await this.getAll();
    const docsToDelete = allDocs.filter(shouldDelete);
    for (const doc of docsToDelete) {
      await this.remove(doc);
    }
    return docsToDelete.length;
  }

  /**
   * @returns true if there are no documents in the database
   */
  abstract isEmpty(): Promise<boolean>;

  /**
   * Closes open connections and un-initializes the database
   * (without deleting persisted data, see destroy() for that)
   */
  abstract reset(): Promise<any>;

  /**
   * Closes all open connections to the database base and destroys it (clearing all data)
   */
  abstract destroy(): Promise<any>;

  abstract changes(): Observable<DatabaseDocChange>;
}

/**
 * Based upon PouchDb changes feed format.
 */
export interface DatabaseDocChange {
  _id: string;
  _rev: string;
  _deleted?: boolean;

  [key: string]: any;
}

/**
 * Basic query options supported by {@link Database}.
 *
 * also see https://pouchdb.com/guides/queries.html
 */
export type QueryOptions = PouchDB.Query.Options<any, any>;

/**
 * Basic database read options supported by {@link Database}.
 *
 * also see https://pouchdb.com/api.html#fetch_document
 */
export type GetAllOptions =
  | PouchDB.Core.AllDocsWithKeyOptions
  | PouchDB.Core.AllDocsWithKeysOptions
  | PouchDB.Core.AllDocsWithinRangeOptions
  | PouchDB.Core.AllDocsOptions;

/**
 * Basic database read options supported by {@link Database}.
 *
 * also see https://pouchdb.com/api.html#fetch_document
 */
export type GetOptions = PouchDB.Core.GetOptions;

results matching ""

    No results matching ""