/**
 * Created by tsturzl on 4/11/17.
 */ import Datastore from "./datastore" ; import { IRange , IindexOptions } from "./types" ; import { compareArray } from "./utils" ; import { AVLTree , ASNDBS , SNDBSA } from "binary-type-tree" ; import { getObjValue , rmArrDups } from "tedb-utils" ; /**
 * Index interface used for the datastore indices
 * Inherits types from binary-type-tree
 *
 * ~~~
 * Array String Number, Date, Boolean, -> symbol was redacted.
 * ASNDBS = Array<any[]|string|number|Date|boolean|null>|string|number|Date|boolean|null
 * -> redacted symbol, Number, Date, Boolean, String, Array
 * SNDBSA = Array<{}|any[]|string|number|Date|boolean|null>;
 * ~~~
 */ export interface IIndex { insert ( doc ) : Promise < any > ; insertMany ( key , indices ) : Promise < null > ; updateKey ( key , newKey ) : Promise < any > ; remove ( doc ) : Promise < any > ; removeByPair ( key , value ) : Promise < any > ; toJSON ( ) : Promise < string > ; search ( key ) : Promise < SNDBSA > ; traverse ( fn ) : Promise < any > ; searchRange ( range ) : Promise < SNDBSA > ; } export default class Index implements IIndex { /** Field Name for Index */ protected fieldName : string ; /** ALV Tree for indexing */ private avl : AVLTree ; /** Reference to Datastore */ private datastore : Datastore ; /** Is the index holding an array */ private isArray : boolean ; param datastore param options constructor ( datastore , options ) { this . avl = options . unique ? new AVLTree ( { unique : true } ) : new AVLTree ( { } ) ; if ( options . compareKeys ) { this . avl . compareKeys = options . compareKeys ; } if ( options . checkKeyEquality ) { this . avl . checkKeyEquality = options . checkKeyEquality ; } this . isArray = false ; this . fieldName = options . fieldName ; this . datastore = datastore ; } public traverse ( fn ) { return this . avl . tree . executeOnEveryNode ( fn ) ; } param doc returns { Promise < any > } public insert ( doc ) { return new Promise < any > ( ( resolve , reject ) => { if ( doc === undefined ) { return reject ( new Error ( "No document ot insert index" ) ) ; } if ( ! doc . hasOwnProperty ( "_id" ) ) { return reject ( new Error ( "Document is missing _id field" ) ) ; } if ( typeof doc . _id !== "string" ) { return reject ( new Error ( "_id field needs to be type `string`" ) ) ; } const key = getObjValue ( doc , this . fieldName ) ; if ( key !== undefined && key !== null ) { if ( Object . prototype . toString . call ( key ) === "[object Array]" && ! this . isArray ) { this . avl . compareKeys = compareArray ; this . isArray = true ; } } else { return reject ( new Error ( "Key was not retrieved from document, or key was set to null. No null key indices" ) ) ; } try { this . avl . insert ( key , [ doc . _id ] ) ; } catch ( e ) { return reject ( e ) ; } resolve ( doc ) ; } ) ; } param key param indices returns { Promise < null > } public insertMany ( key , indices ) { return new Promise < null > ( ( resolve , reject ) => { if ( key !== undefined && key !== null ) { if ( Object . prototype . toString . call ( key ) === "[object Array]" && ! this . isArray ) { this . avl . compareKeys = compareArray ; this . isArray = true ; } } else { return reject ( new Error ( "Key was not retrieved" ) ) ; } try { for ( const item of indices ) { this . avl . insert ( item . key , rmArrDups ( item . value ) ) ; } } catch ( e ) { return reject ( e ) ; } resolve ( ) ; } ) ; } param key param newKey returns { Promise < null > } public updateKey ( key , newKey ) { return new Promise < null > ( ( resolve , reject ) => { if ( this . avl . tree . search ( key ) . length === 0 ) { return reject ( new Error ( "This key does not exist" ) ) ; } try { this . avl . updateKey ( key , newKey ) ; } catch ( e ) { return reject ( e ) ; } resolve ( ) ; } ) ; } param doc returns { Promise < any > } public remove ( doc ) { return new Promise < any > ( ( resolve , reject ) => { if ( doc === undefined ) { return reject ( new Error ( "There is no document to remove" ) ) ; } if ( ! doc . hasOwnProperty ( "_id" ) ) { return reject ( new Error ( "There is no _id to reference this document" ) ) ; } const key = getObjValue ( doc , this . fieldName ) ; try { this . avl . Delete ( key , [ doc . _id ] ) ; } catch ( e ) { return reject ( e ) ; } resolve ( doc ) ; } ) ; } param key param { string } value returns { Promise < any > } public removeByPair ( key , value ) { return new Promise ( ( resolve , reject ) => { try { this . avl . Delete ( key , [ value ] ) ; } catch ( e ) { return reject ( e ) ; } resolve ( ) ; } ) ; } returns { Promise < string > } public toJSON ( ) { return this . avl . tree . toJSON ( ) ; } param key returns { Promise < SNDBSA > } public search ( key ) { return new Promise < SNDBSA > ( ( resolve ) => { resolve ( this . avl . tree . search ( key ) ) ; } ) ; } param range returns { Promise < SNDBSA > } public searchRange ( range ) { return new Promise < SNDBSA > ( ( resolve ) => { resolve ( this . avl . tree . query ( range ) ) ; } ) ; } }