Path: blob/master/node_modules/@adiwajshing/keyed-db/lib/KeyedDB.js
1126 views
"use strict";1var __importDefault = (this && this.__importDefault) || function (mod) {2return (mod && mod.__esModule) ? mod : { "default": mod };3};4Object.defineProperty(exports, "__esModule", { value: true });5const BinarySearch_1 = __importDefault(require("./BinarySearch"));6class KeyedDB {7/**8* @param key Return the unique key used to sort items9* @param id The unique ID for the items10*/11constructor(key, id) {12this.key = key;13this.idGetter = id || (v => this.key.key(v).toString());14this.dict = {};15this.array = [];16}17get length() {18return this.array.length;19}20get first() {21return this.array[0];22}23get last() {24return this.array[this.array.length - 1];25}26toJSON() {27return this.array;28}29/**30* Inserts items into the DB in klogN time.31* Where k is the number of items being inserted.32* @param values33*/34insert(...values) {35values.forEach(v => this._insertSingle(v));36}37/**38* Upserts items into the DB in 2klogN time.39* Where k is the number of items being inserted.40*41* If a duplicate is found, it is deleted first and then the new one is inserted42* @param values43* @returns list of updated values44*/45upsert(...values) {46const updates = [];47values.forEach(v => {48if (!v)49return;50const deleted = this.deleteById(this.idGetter(v), false);51this._insertSingle(v);52// add to updates53deleted && updates.push(v);54});55return updates;56}57/**58* Inserts items only if they are not present in the DB59* @param values60* @returns list of all the inserted values61*/62insertIfAbsent(...values) {63const insertions = [];64values.forEach(v => {65if (!v)66return;67// if ID is present68const presentValue = this.get(this.idGetter(v));69if (presentValue)70return;71// if key is present72const presentKey = this.firstIndex(v);73if (this.array[presentKey] && this.key.key(this.array[presentKey]) === this.key.key(v))74return;75this.insert(v);76insertions.push(v);77});78return insertions;79}80/**81* Deletes an item indexed by the ID82* @param id83* @param assertPresent84*/85deleteById(id, assertPresent = true) {86const value = this.get(id);87if (!value) {88if (assertPresent)89throw new Error(`Value not found`);90else91return;92}93return this.delete(value);94}95delete(value) {96const index = this.firstIndex(value);97if (index < 0 || index >= this.array.length || this.key.key(value) !== this.key.key(this.array[index])) {98return null;99}100delete this.dict[this.idGetter(value)];101return this.array.splice(index, 1)[0];102}103slice(start, end) {104const db = new KeyedDB(this.key, this.idGetter);105db.array = this.array.slice(start, end);106db.array.forEach(item => db.dict[this.idGetter(item)] = item);107return db;108}109/** Clears the DB */110clear() {111this.array = [];112this.dict = {};113}114get(id) {115return this.dict[id];116}117all() {118return this.array;119}120/**121* Updates a value specified by the ID122* and adjusts its position in the DB after an update if required123* @param id124* @param update125*/126update(id, update) {127const value = this.get(id);128if (value) {129const idx = this.firstIndex(value);130if (idx >= 0 && idx < this.array.length && this.idGetter(this.array[idx]) === id) {131const oldKey = this.key.key(value);132update(value);133const newKey = this.key.key(value);134if (newKey !== oldKey) {135delete this.dict[id];136this.array.splice(idx, 1);137this._insertSingle(value);138return 2;139}140return 1;141}142}143}144/**145* @deprecated see `update`146*/147updateKey(value, update) {148return this.update(this.idGetter(value), update);149}150filter(predicate) {151const db = new KeyedDB(this.key, this.idGetter);152db.array = this.array.filter((value, index) => {153if (predicate(value, index)) {154db.dict[this.idGetter(value)] = value;155return true;156}157});158return db;159}160/**161* Get the values of the data in a paginated manner162* @param value the value itself beyond which the content is to be retreived163* @param limit max number of items to retreive164* @param predicate optional filter165* @param mode whether to get the content `before` the cursor or `after` the cursor; default=`after`166*/167paginatedByValue(value, limit, predicate, mode = 'after') {168return this.paginated(value && this.key.key(value), limit, predicate, mode);169}170/**171* Get the values of the data in a paginated manner172* @param value the cursor beyond which the content is to be retreived173* @param limit max number of items to retreive174* @param predicate optional filter175* @param mode whether to get the content `before` the cursor or `after` the cursor; default=`after`176*/177paginated(cursor, limit, predicate, mode = 'after') {178let index = mode === 'after' ? 0 : this.array.length;179if (cursor !== null && typeof cursor !== 'undefined') {180index = BinarySearch_1.default(this.array, v => this.key.compare(cursor, this.key.key(v)));181if (index < 0)182index = 0;183if (this.key.key(this.array[index]) === cursor)184index += (mode === 'after' ? 1 : 0);185}186return this.filtered(index, limit, mode, predicate);187}188_insertSingle(value) {189if (!value)190throw new Error('falsey value');191const valueID = this.idGetter(value);192if (this.get(valueID)) {193throw new Error('duplicate ID being inserted: ' + valueID);194}195if (this.array.length > 0) {196const index = this.firstIndex(value);197if (index >= this.array.length)198this.array.push(value);199else if (index < 0)200this.array.unshift(value);201else if (this.key.key(value) !== this.key.key(this.array[index]))202this.array.splice(index, 0, value);203else204throw new Error(`duplicate key: ${this.key.key(value)}, of inserting: ${valueID}, present: ${this.idGetter(this.array[index])}`);205}206else {207this.array.push(value);208}209this.dict[valueID] = value;210}211filtered(start, count, mode, predicate) {212let arr;213if (mode === 'after') {214if (predicate) {215arr = [];216for (let item of this.array.slice(start)) {217predicate(item, start + arr.length) && arr.push(item);218if (arr.length >= count)219break;220}221}222else223arr = this.array.slice(start, start + count);224}225else if (mode === 'before') {226if (predicate) {227arr = [];228for (let i = start - 1; i >= 0; i--) {229let item = this.array[i];230predicate(item, start + arr.length) && arr.unshift(item);231if (arr.length >= count)232break;233}234}235else236arr = this.array.slice(Math.max(start - count, 0), start);237}238return arr;239}240firstIndex(value) {241const valueKey = this.key.key(value);242return BinarySearch_1.default(this.array, v => this.key.compare(valueKey, this.key.key(v)));243}244}245exports.default = KeyedDB;246247248