diff --git a/README.md b/README.md index efb1e61..3365056 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Minimalistic JSON database. [![Build Status](https://github.com/yusufshakeel/minivium/actions/workflows/ci.yml/badge.svg)](https://github.com/yusufshakeel/minivium/actions/workflows/ci.yml) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/yusufshakeel/minivium) -[![npm version](https://img.shields.io/badge/npm-0.2.4-blue.svg)](https://www.npmjs.com/package/minivium) +[![npm version](https://img.shields.io/badge/npm-0.3.0-blue.svg)](https://www.npmjs.com/package/minivium) [![npm Downloads](https://img.shields.io/npm/dm/minivium.svg)](https://www.npmjs.com/package/minivium) ![img.webp](assets/img.webp) @@ -484,6 +484,8 @@ select * from users where email in ['yusuf@example.com']; ``` +The `Op.in` operator will work for column with `string`, `string[]`, `number`, `number[]` values. + ### Not in `notIn` ```js @@ -504,6 +506,8 @@ select * from users where email not in ['yusuf@example.com']; ``` +The `Op.notIn` operator will work for column with `string`, `string[]`, `number`, `number[]` values. + ### Greater than `gt` ```js diff --git a/package-lock.json b/package-lock.json index 8e633a2..e6cad65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "minivium", - "version": "0.2.4", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "minivium", - "version": "0.2.4", + "version": "0.3.0", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.14", diff --git a/package.json b/package.json index 985e640..8ec01b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minivium", - "version": "0.2.4", + "version": "0.3.0", "description": "Minimalistic JSON database.", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/src/helpers/filter.ts b/src/helpers/filter.ts index 85c9f5d..3cb5b76 100644 --- a/src/helpers/filter.ts +++ b/src/helpers/filter.ts @@ -2,16 +2,28 @@ import { Op } from '../core/Operators'; import { Filter, Condition } from '../types/where'; const actions: any = { - [Op.eq]: (itemValue: any, value: any) => itemValue === value, - [Op.notEq]: (itemValue: any, value: any) => itemValue !== value, - [Op.in]: (itemValue: any, value: any) => value.includes(itemValue), - [Op.notIn]: (itemValue: any, value: any) => !value.includes(itemValue), - [Op.gt]: (itemValue: any, value: any) => itemValue > value, - [Op.gte]: (itemValue: any, value: any) => itemValue >= value, - [Op.lt]: (itemValue: any, value: any) => itemValue < value, - [Op.lte]: (itemValue: any, value: any) => itemValue <= value, - [Op.between]: (itemValue: any, value: any) => - itemValue >= value[0] && itemValue <= value[1] + [Op.eq]: (fieldValue: any, searchValue: any) => fieldValue === searchValue, + [Op.notEq]: (fieldValue: any, searchValue: any) => fieldValue !== searchValue, + [Op.in]: (needle: any | any[], haystack: any[]) => { + if (Array.isArray(needle)) { + return haystack.some(h => needle.includes(h)); + } else { + return haystack.includes(needle); + } + }, + [Op.notIn]: (needle: any, haystack: any[]) => { + if (Array.isArray(needle)) { + return haystack.every(h => !needle.includes(h)); + } else { + return !haystack.includes(needle); + } + }, + [Op.gt]: (fieldValue: any, searchValue: any) => fieldValue > searchValue, + [Op.gte]: (fieldValue: any, searchValue: any) => fieldValue >= searchValue, + [Op.lt]: (fieldValue: any, searchValue: any) => fieldValue < searchValue, + [Op.lte]: (fieldValue: any, searchValue: any) => fieldValue <= searchValue, + [Op.between]: (fieldValue: any, betweenValues: any) => + fieldValue >= betweenValues[0] && fieldValue <= betweenValues[1] }; const evaluateCondition = diff --git a/test/testdata/users.ts b/test/testdata/users.ts index 43a7855..72d6052 100644 --- a/test/testdata/users.ts +++ b/test/testdata/users.ts @@ -11,4 +11,10 @@ export const usersForSorting = [ { id: 2, firstName: 'Charlie', lastName: 'Wise', createdAt: '2025-01-01', updatedAt: '2025-01-02' }, { id: 3, firstName: 'Alice', lastName: 'Anderson', createdAt: '2025-01-02', updatedAt: '2025-01-04' }, { id: 4, firstName: 'Bob', lastName: 'Builder', createdAt: '2025-01-02', updatedAt: '2025-01-02' } +]; + +export const userMembershipForColumnWithArrayOfValues = [ + { id: 1, membership: ['red', 'green'] }, + { id: 2, membership: ['red', 'green', 'blue'] }, + { id: 3, membership: ['yellow'] } ]; \ No newline at end of file diff --git a/test/unit/helpers/filter.unit.test.ts b/test/unit/helpers/filter.unit.test.ts index b1c0df9..3008504 100644 --- a/test/unit/helpers/filter.unit.test.ts +++ b/test/unit/helpers/filter.unit.test.ts @@ -1,6 +1,9 @@ import { filter } from '../../../src/helpers/filter'; import { Op } from '../../../src/core/Operators'; -import { users as data } from '../../testdata/users'; +import { + users as data, + userMembershipForColumnWithArrayOfValues as dataColumnsWithArrayOfValues +} from '../../testdata/users'; describe('filter', () => { it('should throw error for invalid operator', () => { @@ -43,23 +46,79 @@ describe('filter', () => { ]); }); - it('should return matching entries by in', () => { - const result = - filter(data, { score: { [Op.in]: [30, 40] } }); - expect(result).toStrictEqual([ - { id: 2, name: 'jane', score: 30, isOnline: true, status: 'active', createdAt: '2024-12-01' }, - { id: 3, name: 'tom', score: 40, isOnline: false, status: 'inactive', createdAt: '2024-12-02' }, - { id: 4, name: 'jerry', score: 30, isOnline: true, status: 'active', createdAt: '2024-12-03' } - ]); + describe('in', () => { + describe('column with non-array value', () => { + it('should return matching entries by in', () => { + const result = + filter(data, { score: { [Op.in]: [30, 40] } }); + expect(result).toStrictEqual([ + { id: 2, name: 'jane', score: 30, isOnline: true, status: 'active', createdAt: '2024-12-01' }, + { id: 3, name: 'tom', score: 40, isOnline: false, status: 'inactive', createdAt: '2024-12-02' }, + { id: 4, name: 'jerry', score: 30, isOnline: true, status: 'active', createdAt: '2024-12-03' } + ]); + }); + + it('should return empty array when there is no match for the in', () => { + const result = + filter(data, { score: { [Op.in]: [100, 200] } }); + expect(result).toStrictEqual([]); + }); + }); + + describe('column with array of values', () => { + it('should return matching entries by in', () => { + const result = + filter(dataColumnsWithArrayOfValues, { membership: { [Op.in]: ['red'] } }); + expect(result).toStrictEqual([ + { id: 1, membership: ['red', 'green'] }, + { id: 2, membership: ['red', 'green', 'blue'] } + ]); + }); + + it('should return empty array when there is no match for the in', () => { + const result = + filter(dataColumnsWithArrayOfValues, { membership: { [Op.in]: ['purple'] } }); + expect(result).toStrictEqual([]); + }); + }); }); - it('should return matching entries by notIn', () => { - const result = - filter(data, { score: { [Op.notIn]: [30, 40] } }); - expect(result).toStrictEqual([ - { id: 1, name: 'john', score: 20, isOnline: false, status: 'active', createdAt: '2024-12-01' }, - { id: 5, name: 'bruce', score: 50, isOnline: true, status: 'active', createdAt: '2024-12-05' } - ]); + describe('notIn', () => { + describe('column with non-array value', () => { + it('should return matching entries by notIn', () => { + const result = + filter(data, { score: { [Op.notIn]: [30, 40] } }); + expect(result).toStrictEqual([ + { id: 1, name: 'john', score: 20, isOnline: false, status: 'active', createdAt: '2024-12-01' }, + { id: 5, name: 'bruce', score: 50, isOnline: true, status: 'active', createdAt: '2024-12-05' } + ]); + }); + + it('should return empty array when there is no match for the notIn', () => { + const result = + filter(data, { score: { [Op.notIn]: [20, 30] } }); + expect(result).toStrictEqual([ + { id: 3, name: 'tom', score: 40, isOnline: false, status: 'inactive', createdAt: '2024-12-02' }, + { id: 5, name: 'bruce', score: 50, isOnline: true, status: 'active', createdAt: '2024-12-05' } + ]); + }); + }); + + describe('column with array of values', () => { + it('should return matching entries by notIn', () => { + const result = + filter(dataColumnsWithArrayOfValues, { membership: { [Op.notIn]: ['red'] } }); + expect(result).toStrictEqual([ + { id: 3, membership: ['yellow'] } + ]); + }); + + it('should return empty array when there is no match for the notIn', () => { + const result = + filter(dataColumnsWithArrayOfValues, { membership: { [Op.notIn]: ['red', 'yellow'] } }); + expect(result).toStrictEqual([]); + }); + }); }); it('should return matching entries by gt', () => {