diff --git a/changelog.md b/changelog.md index e1a7ae0..5fe12bc 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,26 @@ ## 🕘 | Changelog +**v2.2.0** +- Added `QuickMongo.values()` method; it works the same way as `QuickMongo.keys()`, but returns the object **values** instead of object **keys**. +- Added **6** new **array-like** methods in `QuickMongo` class that simplify the **read** operations on **database object values**: + - `QuickMongo.find()` + - `QuickMongo.map()` + - `QuickMongo.findIndex()` + - `QuickMongo.filter()` + - `QuickMongo.some()` + - `QuickMongo.every()` +- Fixed **caching** bug when **all** falsy values were represented as `null` in the cache. +- Fixed any **falsy** result being represented as `null` in `QuickMongo.random()` method. +- Fully reworked some of the existing **unit tests**. +- Added **references** to used **types/classes/interfaces/built-ins** in documentation. +- Fixed **documentation** mismatches. +- Fixed **JSDoc** mismatches. +- Small documentation improvements. +- Improved **descriptions** of some types. +- Added **missing JSDoc** in `TypedObject` class and in `IDatabaseInternalStructure` and `IDatabaseRequestsLatencyData` types. +- Fixed typos. + **v2.1.0** - Added a `size` property in `QuickMongo` class that determines the number of keys in the root of the database. Equivalent to `QuickMongo.keys().length`. - Added a `TKeys` type parameter in `QuickMongo.keys()` method that determines the type of returned keys. diff --git a/docs/classes/QuickMongo.md b/docs/classes/QuickMongo.md index fa87636..89fe4bc 100644 --- a/docs/classes/QuickMongo.md +++ b/docs/classes/QuickMongo.md @@ -1,12 +1,33 @@ # **`QuickMongo` Class** # Intro -This is the **full** documentation of all the database mathods of `QuickMongo` class. +This is the **full** documentation of all the database methods of `QuickMongo` class. This includes all the methods, types, descriptions and **brief** examples on how each method could be used. You can see the **detailed** examples on usage of each method in both **JavaScript** and **TypeScript** [here](https://github.com/shadowplay1/quick-mongo-super/tree/main/examples). + +# References in this doc +- Classes: + - [`QuickMongoClient`](../classes/QuickMongoClient.md) +- Types: + - [`Maybe`](../types/Maybe.md) + - [`If`](../types/If.md) + - [`IsObject`](../types/IsObject.md) + - [`RestOrArray`](../types/RestOrArray.md) + - [`TupleOrArray`](../types/TupleOrArray.md) + - [`ExtractFromArray`](../types/ExtractFromArray.md) + - [`QueryFunction`](../types/QueryFunction.md) +- Interfaces: + - [`IDatabaseConfiguration`](../interfaces/IDatabaseConfiguration.md) + - [`IDatabaseInternalStructure`](../interfaces/IDatabaseInternalStructure.md) +- External Classes: + - [`Model`](https://mongoosejs.com/docs/typescript.html) +- Built-ins: + - [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) + + ## Constructor ```ts new QuickMongo(quickMongoClient: QuickMongoClient, databaseOptions?: IDatabaseConfiguration) @@ -52,7 +73,7 @@ new QuickMongo(quickMongoClient: QuickMongoClient, databaseOptions?: IData - `_model` (`Model>`): Internal Mongoose model to work with. -# Events +## Events *none* @@ -121,7 +142,7 @@ Determines if the data is stored in database. Writes the specified value into database under the specified key. - **Type Parameters:** - - `TObjectReturnValue` (`any`, defaults to `any`): Type the return type fallbacks to if `TVa\lue` is an object. + - `TObjectReturnValue` (`any`, defaults to `any`): Type the return type fallbacks to if `TValue` is an object. - **Parameters:** - `key` (`string`): The key to write in the target. @@ -238,6 +259,67 @@ Determines whether the specified target is a number. ``` +## `find(queryFunction: QueryFunction): Maybe` +This method is the same as `Array.find()`. + +Iterates over root database values, finds the element in database values array by specified condition in the callback function and returns the result. + +- **Parameters:** + - `queryFunction` (`QueryFunction`): A function that accepts up to three arguments. The `find` method calls the `queryFunction` once for each element in database object values array. + +- **Returns:** `Maybe` + +## `map(queryFunction: QueryFunction): TReturnType[]` +This method is the same as `Array.map()`. + +Calls a defined callback function on each element of an array, and returns an array that contains the results. + +- **Parameters:** + - `queryFunction` (`QueryFunction`): A function that accepts up to three arguments. The `map` method calls the `queryFunction` once for each element in database object values array. + +- **Returns:** `TReturnType[]` + +## `findIndex(queryFunction: QueryFunction): number` +This method is the same as `Array.findIndex()`. + +Iterates over root database values, finds the index of the element in database values array by specified condition in the callback function and returns the result. + +- **Parameters:** + - `queryFunction` (`QueryFunction`): A function that accepts up to three arguments. The `findIndex` method calls the `queryFunction` once for each element in database object values array. + +- **Returns:** `number` + +## `filter(queryFunction: QueryFunction): V[]` +This method is the same as `Array.filter()`. + +Iterates over root database values, finds all the element that match the specified condition in the callback function and returns the result. + +- **Parameters:** + - `queryFunction` (`QueryFunction`): A function that accepts up to three arguments. The `filter` method calls the `queryFunction` once for each element in database object values array. + +- **Returns:** `V[]` + +## `some(queryFunction: QueryFunction): boolean` +This method is the same as `Array.some()`. + +Iterates over root database values and checks if the specified condition in the callback function returns `true` for **any** of the elements of the database object values array. + +- **Parameters:** + - `queryFunction` (`QueryFunction`): A function that accepts up to three arguments. The `some` method calls the `queryFunction` once for each element in database object values array. + +- **Returns:** `boolean` + +## `every(queryFunction: QueryFunction): boolean` +This method is the same as `Array.every()`. + +Iterates over root database values and checks if the specified condition in the callback function returns `true` for **all** of the elements of the database object values array. + +- **Parameters:** + - `queryFunction` (`QueryFunction`): A function that accepts up to three arguments. The `every` method calls the `queryFunction` once for each element in database object values array. + +- **Returns:** `boolean + + ## `push(key: K, ...values: RestOrArray>): Promise[]>` Pushes the specified value(s) into the target array in the database. @@ -289,11 +371,11 @@ Removes the specified element(s) from the target array in the database. console.log(membersPopResult); // -> ['John', 'Tom'] ``` -## `keys(key?: K): string[]` +## `keys = K[]>(key?: K): TKeys[]` Returns an array of object keys by specified database key. - **Type parameters:** - - `TKeys` (`TupleOrArray`, defaults to `string[]`) - The tuple or array of a type of keys to be returned. + - `TValues` (`TupleOrArray`, defaults to `K[]`) - The tuple or array of a type of keys to be returned. - **Parameters:** - `key` (`K`, **optional**): The key to access the target in database by. If omitted, returns object keys of the database root. @@ -305,6 +387,22 @@ Returns an array of object keys by specified database key. console.log(prop3Keys); // -> ['prop4', 'prop5'] ``` +## `values = V>(key?: K): TValues[]` +Returns an array of object values by specified database key. + +- **Type parameters:** + - `TValues` (`TupleOrArray`, defaults to `V[]`) - The tuple or array of a type of values to be returned. + +- **Parameters:** + - `key` (`K`, **optional**): The key to access the target in database by. If omitted, returns object values of the database root. + +- **Returns:** `V[]` - Database object values array. +- **Example:** +```ts + const prop3Values = quickMongo.va('prop3'); + console.log(prop3Values); // -> [789, { prop6: 111 }] +``` + ## `random(key: K): V` Picks a random element of array in the database and returns the picked array element. @@ -386,8 +484,7 @@ Makes a database request and fetches the raw database content - the data as it i console.log(rawData) // -> [{_id: '6534ee98408514005215ad2d', __KEY: 'something', __VALUE: 'something', __v: 0}, ...] ``` - -## `allFromDatabase = V>(): Promise` +## `allFromDatabase(): Promise>` Makes a direct request to the remote cluster and fetches all its contents. - **Type parameters:** @@ -395,7 +492,7 @@ Makes a direct request to the remote cluster and fetches all its contents. - **Returns:** `Promise` - Fetched database contents. -- **Eaxmple:** +- **Example:** ```ts const allDatabase = quickMongo.allFromDatabase() console.log(allDatabase) // -> { ... (the object of all the data stored in database) } diff --git a/docs/classes/QuickMongoClient.md b/docs/classes/QuickMongoClient.md index 240479c..51b90dd 100644 --- a/docs/classes/QuickMongoClient.md +++ b/docs/classes/QuickMongoClient.md @@ -1,5 +1,10 @@ # **`QuickMongoClient` Class** +## References in this doc +- Classes: + - [`QuickMongo`](./QuickMongo.md) + + ## Constructor ```ts new QuickMongoClient(connectionURI: string, initialDatabaseData?: TInitialDatabaseData) diff --git a/docs/classes/TypedObject.md b/docs/classes/TypedObject.md new file mode 100644 index 0000000..dfa8488 --- /dev/null +++ b/docs/classes/TypedObject.md @@ -0,0 +1,45 @@ +# **`TypedObject` Class** + +# Intro +Utility class for working with objects. + +Provides **static** methods for retrieving keys and values of an object. + +This class enhances type safety by providing better typings for object keys and values. + + +# References in this doc +- Types: + - [`TupleOrArray`](../types/TupleOrArray.md) + + +## Constructor +*none* + +## Notice +This class includes static methods to work with objects. It's **not intended** to create instances from this class. + +## Methods + +## `static keys = string[]>(obj: any): K ` +Returns the names of the enumerable string properties and methods of an object. + +- **Type Parameters:** + - `K` (`TupleOrArray`, defaults to `string[]`): The type of the array containing the names of the enumerable string properties and methods. + +- **Parameters:** + - `obj` (`any`): Object that contains the properties and methods. + +- **Returns:** `K` - Array of names of the enumerable string properties and methods of the specified object. + + +## `static values = any[]>(obj: any): V` +Returns an array of values of the enumerable properties of an object. + +- **Type Parameters:** + - `TObjectReturnValue` (`TupleOrArray`, defaults to `any[]`): The type of the array containing the values of the enumerable properties. + +- **Parameters:** + - `obj` (`any`): Object that contains the properties and methods. + +- **Returns:** `V` - Array of values of the enumerable properties of the specified object. diff --git a/docs/events/QuickMongoClient/connect.md b/docs/events/QuickMongoClient/connect.md index 5f1598b..54fbbc5 100644 --- a/docs/events/QuickMongoClient/connect.md +++ b/docs/events/QuickMongoClient/connect.md @@ -5,7 +5,7 @@ Emits when the MongoDB connection is established successfully. - **Parameters:** - `connectedQuickMongoClient` (`QuickMongoClient`) - Connected `QuickMongoClient` instance. -- **Eaxmple:** +- **Example:** ```ts quickMongo.on('connect', connectedClient => { console.log(`Connected ${connectedClient.databases.length} databases to MongoDB.`) diff --git a/docs/events/QuickMongoClient/disconnect.md b/docs/events/QuickMongoClient/disconnect.md index 9225a9e..a2e78ed 100644 --- a/docs/events/QuickMongoClient/disconnect.md +++ b/docs/events/QuickMongoClient/disconnect.md @@ -2,7 +2,7 @@ Emits when the MongoDB connection was destroyed. -- **Eaxmple:** +- **Example:** ```ts quickMongo.on('disconnect', () => { console.log('Disconnected from MongoDB.') diff --git a/docs/interfaces/IDatabaseConfiguration.md b/docs/interfaces/IDatabaseConfiguration.md index 2bc37ad..05f749f 100644 --- a/docs/interfaces/IDatabaseConfiguration.md +++ b/docs/interfaces/IDatabaseConfiguration.md @@ -2,7 +2,7 @@ Represents the configuration object of the `QuickMongo` database instance. -## Implemenatation +## Implementation ```ts export interface IDatabaseConfiguration { name: string diff --git a/docs/interfaces/IDatabaseInternalStructure.md b/docs/interfaces/IDatabaseInternalStructure.md index 71ddb4e..345a8f6 100644 --- a/docs/interfaces/IDatabaseInternalStructure.md +++ b/docs/interfaces/IDatabaseInternalStructure.md @@ -2,7 +2,7 @@ Represents the object of the way data stored in the internal `[__KEY]-[__VALUE]` storage format that was made to achieve better data accessibility across the module. -## Implemenatation +## Implementation ```ts export interface IDatabaseInternalStructure { __KEY: string @@ -11,7 +11,7 @@ export interface IDatabaseInternalStructure { ``` - **Type Parameters:** - - `T` (`any`) - The type of `__VALUE` property in each raw data object. + - `T` (`any`, defaults to `any`) - The type of `__VALUE` property in each raw data object. - **Properties:** - `__KEY` (`string`): The key to store the data under. diff --git a/docs/interfaces/IDatabaseRequestsLatencyData.md b/docs/interfaces/IDatabaseRequestsLatencyData.md index 01278f7..e49f276 100644 --- a/docs/interfaces/IDatabaseRequestsLatencyData.md +++ b/docs/interfaces/IDatabaseRequestsLatencyData.md @@ -2,7 +2,13 @@ Represents the database operations latency object. -## Implemenatation + +# References in this doc +- Built-ins: + - [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) + + +## Implementation ```ts export type IDatabaseRequestsLatencyData = Record< 'readLatency' | 'writeLatency' | 'deleteLatency', diff --git a/docs/types/ExtractFromArray.md b/docs/types/ExtractFromArray.md index d2cecbf..b85ce4c 100644 --- a/docs/types/ExtractFromArray.md +++ b/docs/types/ExtractFromArray.md @@ -1,8 +1,8 @@ # **`ExtractFromArray` Type** -Extracts the type from the `Array` type. +From the type `A`, extracts the type `T` from the `Array` type, or returns `A` if not array type was specified. -## Implemenatation +## Implementation ```ts export type ExtractFromArray = A extends Array ? T : A ``` diff --git a/docs/types/ExtractFromRestOrArray.md b/docs/types/ExtractFromRestOrArray.md index 96114fc..75995fa 100644 --- a/docs/types/ExtractFromRestOrArray.md +++ b/docs/types/ExtractFromRestOrArray.md @@ -6,7 +6,13 @@ Useful to prevent accidentally creating the `RestOrArray>` instan `T` is being extracted from `RestOrArray>` type and being passed into `RestOrArray` type. -## Implemenatation + +## References in this doc +- Types: + - [`RestOrArray`](./RestOrArray.md) + + +## Implementation ```ts export type ExtractFromRestOrArray = T extends RestOrArray ? RestOrArray diff --git a/docs/types/If.md b/docs/types/If.md index 7dfc1a1..b632d89 100644 --- a/docs/types/If.md +++ b/docs/types/If.md @@ -4,7 +4,8 @@ Conditional type that returns the type based on the condition type result. ## Implementation ```ts -export type If = T extends true ? IfTrue : IfFalse @@ -13,5 +14,5 @@ export type If`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) + + ## Implementation ```ts export type IsObject = T extends null diff --git a/docs/types/Maybe.md b/docs/types/Maybe.md index eb08c40..8569ee0 100644 --- a/docs/types/Maybe.md +++ b/docs/types/Maybe.md @@ -2,7 +2,13 @@ Represents the nullish type (`T` or `null`) and excludes `undefined` from it. -## Implemenatation + +# References in this doc +- Built-ins: + - [`Exclude`](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers) + + +## Implementation ```ts export type Maybe = Exclude ``` diff --git a/docs/types/QueryFunction.md b/docs/types/QueryFunction.md new file mode 100644 index 0000000..529c203 --- /dev/null +++ b/docs/types/QueryFunction.md @@ -0,0 +1,12 @@ +# **`QueryFunction` Type** + +Represents a `predicate` callback function from array methods such as `Array.map()`, `Array.find()`, etc. + +## Implementation +```ts +export type QueryFunction = (item: T, index: number, values: T[]) => R +``` + +- **Type Parameters:** + - `T` (`any`): The type of the item in the array. + - `R` (`any`, defaults to `any`): The return type of the function. diff --git a/docs/types/RestOrArray.md b/docs/types/RestOrArray.md index bdd419b..5fda0d0 100644 --- a/docs/types/RestOrArray.md +++ b/docs/types/RestOrArray.md @@ -2,7 +2,7 @@ Represents a type that works as an array of specified type or ...spread of specified type. -## Implemenatation +## Implementation ```ts export type RestOrArray = T[] | [T[]] ``` diff --git a/docs/types/TupleOrArray.md b/docs/types/TupleOrArray.md index e035533..c8ff630 100644 --- a/docs/types/TupleOrArray.md +++ b/docs/types/TupleOrArray.md @@ -2,7 +2,7 @@ Converts the specified type `T` into the array of `T` or just `T` if it's a valid tuple. -## Implemenatation +## Implementation ```ts export type TupleOrArray = T extends [...infer _Rest] ? T : T[] ``` diff --git a/examples/example.ts b/examples/example.ts index 584b788..87a3e29 100644 --- a/examples/example.ts +++ b/examples/example.ts @@ -50,8 +50,8 @@ const main = async () => { // QuickMongo type parameters: // - // - `K` (string) - The type of The key to access the target in database by. - // - `V` (any) - The type of the values in the database. + // - `K` (`string`) - The type of The key to access the target in database by. + // - `V` (`any`) - The type of the values in the database. // Initialize the database. const quickMongo = new QuickMongo(quickMongoClient, { diff --git a/package-lock.json b/package-lock.json index 2e22e5a..ac8527c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "quick-mongo-super", - "version": "2.1.0", + "version": "2.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "quick-mongo-super", - "version": "2.1.0", + "version": "2.2.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 32d480f..ccfc9a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "quick-mongo-super", - "version": "2.1.0", + "version": "2.2.0", "description": "Quick Mongo Super is a light-weight and easy-to-use Node.js module written in TypeScript to work with MongoDB.", "main": "./dist/src/index.js", "types": "./dist/typings/src/index.d.ts", diff --git a/src/lib/QuickMongo.ts b/src/lib/QuickMongo.ts index ae4f714..2762524 100644 --- a/src/lib/QuickMongo.ts +++ b/src/lib/QuickMongo.ts @@ -21,8 +21,8 @@ import { QuickMongoError } from './utils/QuickMongoError' import { ExtractFromArray, If, - IsObject, Maybe, RestOrArray, - TupleOrArray + IsObject, Maybe, QueryFunction, + RestOrArray, TupleOrArray } from '../types/utils' import { createTypesArray } from '../structures/errors' @@ -35,8 +35,8 @@ import { createTypesArray } from '../structures/errors' * - `K` (`string`) - The type of The key to access the target in database by. * - `V` (`any`) - The type of the values in the database. * - * @template K (string) - The type of The key to access the target in database by. - * @template V (any) - The type of the values in the database. + * @template K (`string`) - The type of The key to access the target in database by. + * @template V (`any`) - The type of the values in the database. * * @example * const { QuickMongoClient, QuickMongo } = require('quick-mongo-super') @@ -104,8 +104,8 @@ export class QuickMongo { * @param {QuickMongoClient} client Quick Mongo client to get attached to. * @param {IDatabaseConfiguration} databaseConfiguration Database configuration object. * - * @template K (string) - The type of The key to access the target in database by. - * @template V (any) - The type of the values in the database. + * @template K (`string`) - The type of The key to access the target in database by. + * @template V (`any`) - The type of the values in the database. * * @example * const { QuickMongoClient, QuickMongo } = require('quick-mongo-super') @@ -252,6 +252,110 @@ export class QuickMongo { } } + /** + * This method is the same as `Array.find()`. + * + * Iterates over root database values, finds the element in database values array + * by specified condition in the callback function and returns the result. + * + * @param {QueryFunction} queryFunction + * A function that accepts up to three arguments. + * The `find` method calls the `queryFunction` once for each element in database object values array. + * + * @returns {Maybe} + */ + public find(queryFunction: QueryFunction): Maybe { + const values = this.values() + return values.find(queryFunction) as Maybe ?? null + } + + /** + * This method is the same as `Array.map()`. + * + * Calls a defined callback function on each element of an array, + * and returns an array that contains the results. + * + * @param {QueryFunction} queryFunction + * A function that accepts up to three arguments. + * The `map` method calls the `queryFunction` once for each element in database object values array. + * + * @returns {TReturnType[]} + */ + public map(queryFunction: QueryFunction): TReturnType[] { + const values = this.values() + return values.map(queryFunction) + } + + /** + * This method is the same as `Array.findIndex()`. + * + * Iterates over root database values, finds the index of the element in database values array + * by specified condition in the callback function and returns the result. + * + * @param {QueryFunction} queryFunction + * A function that accepts up to three arguments. + * The `findIndex` method calls the `queryFunction` once for each element in database object values array. + * + * @returns {number} + */ + public findIndex(queryFunction: QueryFunction): number { + const values = this.values() + return values.findIndex(queryFunction) + } + + /** + * This method is the same as `Array.filter()`. + * + * Iterates over root database values, finds all the element that match the + * specified condition in the callback function and returns the result. + * + * @param {QueryFunction} queryFunction + * A function that accepts up to three arguments. + * The `filter` method calls the `queryFunction` once for each element in database object values array. + * + * @returns {V[]} + */ + public filter(queryFunction: QueryFunction): V[] { + const values = this.values() + return values.filter(queryFunction) + } + + /** + * This method is the same as `Array.some()`. + * + * Iterates over root database values and checks if the + * specified condition in the callback function returns `true` + * for **any** of the elements of the database object values array. + * + * @param {QueryFunction} queryFunction + * A function that accepts up to three arguments. + * The `some` method calls the `queryFunction` once for each element in database object values array. + * + * @returns {boolean} + */ + public some(queryFunction: QueryFunction): boolean { + const values = this.values() + return values.some(queryFunction) + } + + /** + * This method is the same as `Array.every()`. + * + * Iterates over root database values and checks if the + * specified condition in the callback function returns `true` + * for **all** of the elements of the database object values array. + * + * @param {QueryFunction} queryFunction + * A function that accepts up to three arguments. + * The `every` method calls the `queryFunction` once for each element in database object values array. + * + * @returns {boolean} + */ + public every(queryFunction: QueryFunction): boolean { + const values = this.values() + return values.every(queryFunction) + } + /** * Retrieves a value from database by a key. * @@ -813,7 +917,7 @@ export class QuickMongo { * * Type parameters: * - * - `TKeys` (`TupleOrArray`, defaults to `string[]`) - The tuple or array of a type of keys to be returned. + * - `TKeys` (`TupleOrArray`, defaults to `K[]`) - The tuple or array of a type of keys to be returned. * * @param {K} [key] The key to access the target in database by. * @returns {string[]} Database object keys array. @@ -826,7 +930,8 @@ export class QuickMongo { * console.log(prop5Keys) // -> ['prop6'] * * const prop6Keys = quickMongo.keys('prop3.prop5.prop6') - * console.log(prop6Keys) // -> [] (empty since the value in `prop6`, 111 a primitive value and not an actual object) + * console.log(prop6Keys) + * // ^ -> [] (empty since the value in `prop6`, 111, is a primitive value and not an actual object) * * const databaseKeys = quickMongo.keys() * // in this example, `key` parameter is omitted - object keys of database object are being returned @@ -843,9 +948,10 @@ export class QuickMongo { * // prop3: { prop4: 789, prop5: { prop6: 111 } } * // } */ - public keys = string[]>(key?: K): TKeys { + public keys = K[]>(key?: K): TKeys { if (!key) { - return TypedObject.keys(this.all()) + const allDatabase = this.all() + return TypedObject.keys(allDatabase) } const data = this.get(key) @@ -854,6 +960,56 @@ export class QuickMongo { return keys.filter(key => data[key] !== undefined && data[key] !== null) as TKeys } + /** + * Returns an array of object values by specified database key. + * + * If `key` parameter is omitted, then an array of object values of database root object will be returned. + * + * Type parameters: + * + * - `TValues` (`TupleOrArray`, defaults to `V[]`) - The tuple or array of a type of values to be returned. + * + * @param {K} [key] The key to access the target in database by. + * @returns {TValues[]} Database object values array. + * + * @example + * const prop3Values = quickMongo.values('prop3') + * console.log(prop3Values) // -> [789, { prop6: 111 }] + * + * const prop5Values = quickMongo.values('prop3.prop5') + * console.log(prop5Values) // -> [] + * + * const prop6Values = quickMongo.values('prop3.prop5.prop6') + * console.log(prop6Values) + * // ^ -> [] (empty since the value in `prop6`, 111, is a primitive value and not an actual object) + * + * const databaseValues = quickMongo.values() + * // in this example, `key` parameter is omitted - object values of database object are being returned + * + * console.log(databaseValues) // -> [123, 456, { prop4: 789, prop5: { prop6: 111 } }] + * + * const unexistentValues = quickMongo.values('somethingElse') + * console.log(unexistentValues) // -> [] (empty since the key `somethingElse` does not exist in database) + * + * // ^ Assuming that the initial database object for this example is: + * // { + * // prop1: 123, + * // prop2: 456, + * // prop3: { prop4: 789, prop5: { prop6: 111 } } + * // } + */ + public values = V>(key?: K): TValues[] { + if (!key) { + const allDatabase = this.all() + return TypedObject.values(allDatabase) + } + + const data = this.get(key) + const values = TypedObject.values(data) + + return values + } + /** * Picks a random element of array in database and returns the picked array element. * @@ -876,7 +1032,7 @@ export class QuickMongo { throw new QuickMongoError('INVALID_TARGET', 'array', typeOf(array)) } - return array[Math.floor(Math.random() * array.length)] || null + return array[Math.floor(Math.random() * array.length)] ?? null } /** diff --git a/src/lib/managers/CacheManager.ts b/src/lib/managers/CacheManager.ts index a62e736..d8b273d 100644 --- a/src/lib/managers/CacheManager.ts +++ b/src/lib/managers/CacheManager.ts @@ -150,22 +150,6 @@ export class CacheManager { } const keys = key.split('.') - - /* for (let i = 0; i < keys.length; i++) { - if (keys.length > 1) { - if (!isObject(data[keys[i]])) { - data[keys[i]] = {} - } - - data[keys[i]] = { - ...data?.[keys[i]], - [keys.at(-1)]: value - } - } else { - data[keys[0]] = value - } - }*/ - let currentObj = data for (let i = 0; i < keys.length; i++) { @@ -179,8 +163,7 @@ export class CacheManager { } } - - this._cache.set(keys[0] as K, data?.[keys[0]] || null) + this._cache.set(keys[0] as K, data?.[keys[0]] ?? null) return data } diff --git a/src/lib/utils/TypedObject.ts b/src/lib/utils/TypedObject.ts index 16fb853..19c1e25 100644 --- a/src/lib/utils/TypedObject.ts +++ b/src/lib/utils/TypedObject.ts @@ -1,11 +1,39 @@ import { TupleOrArray } from '../../types/utils' +/** + * Utility class for working with objects. + * + * Provides **static** methods for retrieving keys and values of an object. + * + * This class enhances type safety by providing better typings for object keys and values. + */ export class TypedObject { + + /** + * Returns the names of the enumerable string properties and methods of an object. + * + * Type parameters: + * - `K` (`TupleOrArray`, defaults to `string[]`) - + * The type of the array containing the names of the enumerable string properties and methods. + * + * @param {any} obj Object that contains the properties and methods. + * @returns {K} Array of names of the enumerable string properties and methods of the specified object. + */ public static keys = string[]>(obj: any): K { return Object.keys(obj || {}) as K } - public static values(obj: any): TupleOrArray { - return Object.values(obj || {}) as TupleOrArray + /** + * Returns an array of values of the enumerable properties of an object. + * + * Type parameters: + * - `V` (`TupleOrArray`, defaults to `any[]`) - + * The type of the array containing the values of the enumerable properties. + * + * @param {any} obj Object that contains the properties and methods. + * @returns {V} Array of values of the enumerable properties of the specified object. + */ + public static values = any[]>(obj: any): V { + return Object.values(obj || {}) as V } } diff --git a/src/types/Database.ts b/src/types/Database.ts index cd75420..0cf67b6 100644 --- a/src/types/Database.ts +++ b/src/types/Database.ts @@ -15,11 +15,20 @@ export interface IDatabaseConfiguration { collectionName?: string } +/** + * Represents the object of the way data stored in the internal + * `[__KEY]-[__VALUE]` storage format that was made to achieve better data accessibility across the module. + * + * @template T The type of `__VALUE` property in each raw data object. + */ export interface IDatabaseInternalStructure { __KEY: string __VALUE: T } +/** + * Represents the database operations latency object. + */ export type IDatabaseRequestsLatencyData = Record< 'readLatency' | 'writeLatency' | 'deleteLatency', number diff --git a/src/types/utils.ts b/src/types/utils.ts index 1f90e68..1e1f593 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -10,7 +10,8 @@ export type Maybe = Exclude * @template IfTrue The type to be returned if the condition type `T` is `true`. * @template IfFalse The type to be returned if the condition type `T` is `false`. */ -export type If = T extends true ? IfTrue : IfFalse @@ -47,7 +48,7 @@ export type ExtractFromRestOrArray = T extends RestOrArray : RestOrArray /** - * Extracts the type from the `Array` type. + * From the type `A`, extracts the type `T` from the `Array` type, or returns `A` if not array type was specified. * @template T The array type to extract the type from. */ export type ExtractFromArray = A extends Array ? T : A @@ -57,3 +58,10 @@ export type ExtractFromArray = A extends Array ? T : A * @template T The type to convert. */ export type TupleOrArray = T extends [...infer _Rest] ? T : T[] + +/** + * Represents a `predicate` callback function from array methods such as `Array.map()`, `Array.find()`, etc. + * @template T The type of the item in the array. + * @template R The return type of the function. + */ +export type QueryFunction = (item: T, index: number, values: T[]) => R diff --git a/tests/errors.test.ts b/tests/errors.test.ts index 89f1f63..5c8e91e 100644 --- a/tests/errors.test.ts +++ b/tests/errors.test.ts @@ -1,17 +1,21 @@ /* eslint-disable no-return-await */ -import { promisify } from 'util' - import { describe, expect, test, afterAll, beforeAll } from '@jest/globals' import { QuickMongo, QuickMongoClient } from '../src' -const sleep = promisify(setTimeout) - let quickMongoClient = new QuickMongoClient('mongodb://127.0.0.1:27018') +let database: QuickMongo beforeAll(async () => { const client = await quickMongoClient.connect() quickMongoClient = client + + database = new QuickMongo(quickMongoClient, { + name: 'test_database', + collectionName: 'test_database_collection' + }) + + await database.deleteAll() }) const resolvePromise = (promise: Promise): Promise => { @@ -35,11 +39,6 @@ const resolveFunction = (fn: (...args: any[]) => any): boolean => { // getting data describe('errors throwing: fetch()', () => { - const database = new QuickMongo(quickMongoClient, { - name: 'test_database', - collectionName: 'test_database_collection' - }) - test.concurrent('\'key\' parameter is missing', async () => { // @ts-expect-error const errorTest = (): any => database.fetch() @@ -53,9 +52,6 @@ describe('errors throwing: fetch()', () => { }) test.concurrent('success case', async () => { - await database.loadCache() - await sleep(1000) - const successTest = (): any => database.fetch('test') return expect(resolveFunction(successTest)).toBeTruthy() }) @@ -80,9 +76,6 @@ describe('errors throwing: has()', () => { }) test.concurrent('success case', async () => { - await database.loadCache() - await sleep(1000) - const successCase = (): any => database.has('test') return expect(resolveFunction(successCase)).toBeTruthy() }) @@ -101,17 +94,11 @@ describe('errors throwing: keys()', () => { }) test.concurrent('success case: \'key\' parameter is missing (return database root keys)', async () => { - await database.loadCache() - await sleep(1000) - const successCase = (): any => database.keys() return expect(resolveFunction(successCase)).toBeTruthy() }) test.concurrent('success case', async () => { - await database.loadCache() - await sleep(1000) - const successCase = (): any => database.keys('test') return expect(resolveFunction(successCase)).toBeTruthy() }) @@ -145,9 +132,6 @@ describe('errors throwing: set()', () => { }) test.concurrent('success case', async () => { - await database.loadCache() - await sleep(1000) - const successCase = async (): Promise => await database.set('test', 123) return expect(resolvePromise(successCase())).resolves.toBeTruthy() }) @@ -172,9 +156,6 @@ describe('errors throwing: delete()', () => { }) test.concurrent('success case', async () => { - await database.loadCache() - await sleep(1000) - const successCase = async (): Promise => await database.delete('test') return expect(resolvePromise(successCase())).resolves.toBeTruthy() }) @@ -224,9 +205,6 @@ describe('errors throwing: add()', () => { }) test.concurrent('success case', async () => { - await database.loadCache() - await sleep(1000) - await database.set('arr', []) const successTest = async (): Promise => await database.add('test', 123) @@ -278,9 +256,6 @@ describe('errors throwing: subtract()', () => { }) test.concurrent('success case', async () => { - await database.loadCache() - await sleep(1000) - const successTest = async (): Promise => await database.subtract('test', 123) return expect(resolvePromise(successTest())).resolves.toBeTruthy() }) @@ -288,9 +263,11 @@ describe('errors throwing: subtract()', () => { // array methods +// soon... + // post-testing cleanup + afterAll(async () => { - await Promise.all(quickMongoClient.databases.map(database => database.deleteAll())) await quickMongoClient.disconnect() }) diff --git a/tests/init.test.ts b/tests/init.test.ts index 5c73cbd..f280707 100644 --- a/tests/init.test.ts +++ b/tests/init.test.ts @@ -23,8 +23,9 @@ describe('initialize the connection and database', () => { }) }) + // post-testing cleanup + afterAll(async () => { - await Promise.all(quickMongoClient.databases.map(database => database.deleteAll())) await quickMongoClient.disconnect() }) diff --git a/tests/math.test.ts b/tests/math.test.ts index 4d62430..c4583b5 100644 --- a/tests/math.test.ts +++ b/tests/math.test.ts @@ -14,100 +14,74 @@ beforeAll(async () => { }) await database.deleteAll() -}) -describe('addition operation', () => { - // setting initial values - - test.concurrent('set number', async () => { - const setResult = await database.set('number', 1) - return expect(setResult).toEqual(1) - }) - - test.concurrent('set number in object', async () => { - const setResult = await database.set('numbers.number', 5) - return expect(setResult).toEqual(5) - }) + await database.set('number', 1) + await database.set('numbers.number', 2) + await database.set('number_2', 6) + await database.set('numbers.number_2', 7) +}) +describe('addition operation', () => { // QuickMongo.add() test.concurrent('add +5', async () => { - const addResult = await database.add('number', 5) - return expect(addResult).toEqual(6) - }) + // setting initial values + await database.set('number', 1) + await database.set('numbers.number', 2) - test.concurrent('add +5 to number in object', async () => { - const addResult = await database.add('numbers.number', 5) - return expect(addResult).toBeDefined() - }) + await database.add('number', 5) // adding 5 to initial value, 1 - test.concurrent('add +5 to unexistent number', async () => { - const addResult = await database.add('unexistentNumber', 5) - return expect(addResult).toEqual(5) + const additionResult = database.get('number') + return expect(additionResult).toEqual(6) }) + test.concurrent('add +5 to number in object', async () => { + await database.set('numbers.number', 2) // setting initial value + await database.add('numbers.number', 5) // adding 5 to initial value, 2 - // getting addition results - - test.concurrent('get addition results', async () => { - const additionResults = [ - database.get('number'), - database.get('numbers.number'), - database.get('unexistentNumber') - ] + const additionResult = database.get('numbers.number') + return expect(additionResult).toEqual(7) + }) - // previous tests cleanup - database.delete('unexistentNumber') + test.concurrent('add +5 to unexistent number', async () => { + await database.add('unexistentNumber', 5) // adding 5 to initial value, 0 - return expect(additionResults).toEqual([6, 10, 5]) + const additionResult = database.get('unexistentNumber') + return expect(additionResult).toEqual(5) }) }) describe('subtraction operation', () => { - // setting initial values - - test.concurrent('set number', async () => { - const setResult = await database.set('number', 1) - return expect(setResult).toEqual(1) - }) - - test.concurrent('set number in object', async () => { - const setResult = await database.set('numbers.number', 5) - return expect(setResult).toEqual(5) - }) - - // QuickMongo.subtract() test.concurrent('subtract -5', async () => { - const subtractionResult = await database.subtract('number', 5) - return expect(subtractionResult).toEqual(-4) - }) + // setting initial values + await database.set('number_2', 6) + await database.set('numbers.number_2', 7) - test.concurrent('subtract -5 from number in object', async () => { - const subtractionResult = await database.subtract('numbers.number', 5) - return expect(subtractionResult).toBeDefined() - }) + await database.subtract('number_2', 5) // subtracting 5 from initial value, 6 - test.concurrent('subtract -5 from unexistent number', async () => { - const subtractResult = await database.subtract('unexistentNumber', 5) - return expect(subtractResult).toEqual(-5) + const subtractionResult = database.get('number_2') + return expect(subtractionResult).toEqual(1) }) + test.concurrent('subtract -5 from number in object', async () => { + await database.set('numbers.number_2', 7) + await database.subtract('numbers.number_2', 5) // subtracting 5 from initial value, 7 - // getting subtraction results + const subtractionResult = database.get('numbers.number_2') + return expect(subtractionResult).toEqual(2) + }) - test.concurrent('get subtraction results', async () => { - const subtractionResults = [ - database.get('number'), - database.get('numbers.number'), - database.get('unexistentNumber') - ] + test.concurrent('subtract -5 from unexistent number', async () => { + await database.subtract('unexistentNumber123', 5) // subtracting 5 from initial value, 0 - return expect(subtractionResults).toEqual([-4, 0, -5]) + const subtractionResult = database.get('unexistentNumber123') + return expect(subtractionResult).toEqual(-5) }) + // QuickMongo.isTargetNumber() test.concurrent('isNumber checks', async () => { @@ -126,7 +100,8 @@ describe('subtraction operation', () => { // post-testing cleanup + afterAll(async () => { - await Promise.all(quickMongoClient.databases.map(database => database.deleteAll())) + await database.deleteAll() await quickMongoClient.disconnect() }) diff --git a/tests/operations.test.ts b/tests/operations.test.ts index 9205536..166e223 100644 --- a/tests/operations.test.ts +++ b/tests/operations.test.ts @@ -1,14 +1,8 @@ -// TODO: remove artificial delays // TODO: perform operations and check database in the same tests -// TODO: uncomment 'attempt to get deleted data' test - -import { promisify } from 'util' import { describe, expect, test, afterAll, beforeAll } from '@jest/globals' import { QuickMongo, QuickMongoClient } from '../src' -const _sleep = promisify(setTimeout) - let quickMongoClient = new QuickMongoClient('mongodb://127.0.0.1:27018') let database: QuickMongo @@ -25,57 +19,33 @@ beforeAll(async () => { }) describe('get, set, delete operations', () => { - // QuickMongo.set() + // QuickMongo.set() & QuickMongo.get() test.concurrent('set data', async () => { - const setResults = [ - await database.set('someString', 'hello'), - await database.set('someString123', 'hello123') - ] - - return expect(setResults).toEqual(['hello', 'hello123']) - }) - - test.concurrent('set objects data', async () => { - const setResults = [ - await database.set('someObject.someProperty.hello', 'hi'), - await database.set('someObject.someProperty.hi', 'hello') - ] - - return expect(setResults).toEqual(['hi', 'hello']) - }) - + await database.set('someString', 'hello') + await database.set('someString123', 'hello123') - // QuickMongo.get() - - test.concurrent('get data', async () => { - await database.loadCache() - await _sleep(1000) - - const getResults = [ + const operationResults = [ database.get('someString'), database.get('someString123') ] - return expect(getResults).toEqual(['hello', 'hello123']) + return expect(operationResults).toEqual(['hello', 'hello123']) }) - test.concurrent('get objects data', async () => { - await database.loadCache() - await _sleep(1000) + test.concurrent('set objects data', async () => { + await database.set('someObject.someProperty.hello', 'hi') + await database.set('someObject.someProperty.hi', 'hello') - const getResults = [ + const operationResults = [ database.get('someObject.someProperty.hello'), database.get('someObject.someProperty.hi') ] - return expect(getResults).toEqual(['hi', 'hello']) + return expect(operationResults).toEqual(['hi', 'hello']) }) test.concurrent('get unexistent value', async () => { - await database.loadCache() - await _sleep(1000) - const getResult = database.get('somethingElse') return expect(getResult).toBeNull() }) @@ -84,8 +54,8 @@ describe('get, set, delete operations', () => { // QuickMongo.has() test.concurrent('has data', async () => { - await database.loadCache() - await _sleep(1000) + await database.set('someString', 'hello') + await database.set('someString123', 'hello123') const hasResults = [ database.has('someString'), @@ -96,8 +66,8 @@ describe('get, set, delete operations', () => { }) test.concurrent('has objects data', async () => { - await database.loadCache() - await _sleep(1000) + await database.set('someObject.someProperty.hello', 'hi') + await database.set('someObject.someProperty.hi', 'hello') const hasResults = [ database.has('someObject.someProperty.hello'), @@ -116,33 +86,18 @@ describe('get, set, delete operations', () => { // QuickMongo.delete() test.concurrent('delete data', async () => { - await database.loadCache() - await _sleep(1000) + await database.delete('someString123') + await database.delete('someObject.someProperty.hi') - const deleteResults = [ - await database.delete('someString123'), - await database.delete('someObject.someProperty.hi') + const deletedItems = [ + database.get('someString123'), + database.get('someObject.someProperty.hi') ] - return expect(deleteResults).toEqual([true, true]) + return expect(deletedItems).toEqual([null, null]) }) - // test.concurrent('attempt to get deleted data', async () => { - // await database.loadCache() - // await _sleep(1000) - - // const deletedGetResults = [ - // database.get('someString123'), - // database.get('someObject.someProperty.hi') - // ] - - // return expect(deletedGetResults).toEqual([null, null]) - // }) - test.concurrent('delete unexistent data', async () => { - await database.loadCache() - await _sleep(1000) - const unexistentDeleteResults = [ await database.delete('justSomething'), await database.delete('someRandomObject.someRandomProperty.randomProp') @@ -154,7 +109,8 @@ describe('get, set, delete operations', () => { // post-testing cleanup + afterAll(async () => { - await Promise.all(quickMongoClient.databases.map(database => database.deleteAll())) + await database.deleteAll() await quickMongoClient.disconnect() }) diff --git a/tests/props.test.ts b/tests/props.test.ts index 0aa0cfd..9c13454 100644 --- a/tests/props.test.ts +++ b/tests/props.test.ts @@ -1,10 +1,6 @@ -import { promisify } from 'util' - import { describe, expect, test, afterAll, beforeAll } from '@jest/globals' import { QuickMongo, QuickMongoClient } from '../src' -const sleep = promisify(setTimeout) - const initialDatabaseObject = { test: 123, hello: 'world' @@ -29,27 +25,34 @@ beforeAll(async () => { }) describe('check for properties of QuickMongoClient and QuickMongo database instances to be correct', () => { - // QuickMongo.set() - test.concurrent('QuickMongo: database name', async () => { - await sleep(1000) return expect(database.name).toEqual(databaseName) }) test.concurrent('QuickMongo: collection name', async () => { - await sleep(1000) return expect(database.collectionName).toEqual(collectionName) }) + test.concurrent('QuickMongo: empty database size', async () => { + return expect(database.size).toEqual(0) + }) + + test.concurrent('QuickMongo: database size with 2 keys', async () => { + await database.set('test1', 1) + await database.set('test2', 2) + + return expect(database.size).toEqual(2) + }) + test.concurrent('QuickMongoClient: initial database data', async () => { - await sleep(1000) return expect(quickMongoClient.initialDatabaseData).toEqual(initialDatabaseObject) }) }) // post-testing cleanup + afterAll(async () => { - await Promise.all(quickMongoClient.databases.map(database => database.deleteAll())) + await database.deleteAll() await quickMongoClient.disconnect() })