From da06da0f11b664e3bac08bae967684f5d6e25335 Mon Sep 17 00:00:00 2001 From: huuyafwww Date: Tue, 24 Dec 2024 23:23:18 +0900 Subject: [PATCH] feat: added support for comparator of null values with equals and notEquals --- src/comparators/date.ts | 4 ++-- src/query/compileQuery.ts | 20 ++++++++++++++------ src/query/queryTypes.ts | 12 ++++++------ test/query/date.test.ts | 32 ++++++++++++++++++++++++++++++++ test/query/number.test.ts | 32 ++++++++++++++++++++++++++++++++ test/query/string.test.ts | 30 ++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 14 deletions(-) diff --git a/src/comparators/date.ts b/src/comparators/date.ts index bdebe2ae..bb04def9 100644 --- a/src/comparators/date.ts +++ b/src/comparators/date.ts @@ -3,10 +3,10 @@ import { DateQuery, QueryToComparator } from '../query/queryTypes' export const dateComparators: QueryToComparator = { equals(expected, actual) { - return compareDates(expected, actual) === 0 + return compareDates(expected as unknown as Date, actual as unknown as Date) === 0 }, notEquals(expected, actual) { - return compareDates(expected, actual) !== 0 + return compareDates(expected as unknown as Date, actual as unknown as Date) !== 0 }, gt(expected, actual) { return compareDates(actual, expected) === 1 diff --git a/src/query/compileQuery.ts b/src/query/compileQuery.ts index 5f6d3acd..f87499a9 100644 --- a/src/query/compileQuery.ts +++ b/src/query/compileQuery.ts @@ -32,18 +32,26 @@ export function compileQuery>( return true } - // If an entity doesn't have any value for the property - // is being queried for, treat it as non-matching. - if (actualValue == null) { - return false - } - return Object.entries(queryChunk).reduce( (acc, [comparatorName, expectedValue]) => { if (!acc) { return acc } + if (comparatorName === "equals" && expectedValue === null) { + return actualValue === null + } + + if (comparatorName === "notEquals" && expectedValue === null) { + return actualValue !== null + } + + // If an entity doesn't have any value for the property + // is being queried for, treat it as non-matching. + if (actualValue == null) { + return false + } + if (Array.isArray(actualValue)) { log( 'actual value is array, checking if at least one item matches...', diff --git a/src/query/queryTypes.ts b/src/query/queryTypes.ts index ad48403a..fc6a0db2 100644 --- a/src/query/queryTypes.ts +++ b/src/query/queryTypes.ts @@ -106,8 +106,8 @@ export type GetQueryFor = ValueType extends string : never export interface StringQuery { - equals: string - notEquals: string + equals: string | null + notEquals: string | null contains: string notContains: string gt: string @@ -119,8 +119,8 @@ export interface StringQuery { } export interface NumberQuery { - equals: number - notEquals: number + equals: number | null + notEquals: number | null between: [number, number] notBetween: [number, number] gt: number @@ -137,8 +137,8 @@ export interface BooleanQuery { } export interface DateQuery { - equals: Date - notEquals: Date + equals: Date | null + notEquals: Date | null gt: Date gte: Date lt: Date diff --git a/test/query/date.test.ts b/test/query/date.test.ts index c4acbda0..4d0d8e38 100644 --- a/test/query/date.test.ts +++ b/test/query/date.test.ts @@ -140,3 +140,35 @@ test('ignores entities with missing values when querying using date', () => { expect(updatedUserNames).toHaveLength(2) expect(updatedUserNames).not.toContain('Sedrick') }) + +test('queries entities that are null', () => { + const db = setup() + + const userResults = db.user.findMany({ + where: { + updatedAt: { + equals: null, + }, + }, + }) + expect(userResults).toHaveLength(1) + + const userNames = userResults.map((user) => user.firstName) + expect(userNames).toEqual(['Sedrik']) +}) + +test('queries entities that are not null', () => { + const db = setup() + + const userResults = db.user.findMany({ + where: { + updatedAt: { + notEquals: null, + }, + }, + }) + expect(userResults).toHaveLength(2) + + const userNames = userResults.map((user) => user.firstName) + expect(userNames).toEqual(['John', 'Kate']) +}) diff --git a/test/query/number.test.ts b/test/query/number.test.ts index ea06e733..0f777967 100644 --- a/test/query/number.test.ts +++ b/test/query/number.test.ts @@ -207,3 +207,35 @@ test('ignores entities with missing values when querying using number', () => { expect(userNames).toHaveLength(2) expect(userNames).toEqual(['Alice', 'John']) }) + +test('queries entities that are null', () => { + const db = setup() + + const users = db.user.findMany({ + where: { + height: { + equals: null, + }, + }, + }) + expect(users).toHaveLength(1) + + const names = users.map((user) => user.firstName) + expect(names).toEqual(['Kate']) +}) + +test('queries entities that are not null', () => { + const db = setup() + + const users = db.user.findMany({ + where: { + height: { + notEquals: null, + }, + }, + }) + expect(users).toHaveLength(2) + + const names = users.map((user) => user.firstName) + expect(names).toEqual(['John', 'Alice']) +}) diff --git a/test/query/string.test.ts b/test/query/string.test.ts index 4a2e714e..3fe585eb 100644 --- a/test/query/string.test.ts +++ b/test/query/string.test.ts @@ -144,3 +144,33 @@ test('ignores entities with missing values when querying using strings', () => { expect(pizzaOrCakeRecipeTitles).toHaveLength(3) expect(pizzaOrCakeRecipeTitles).not.toContain('Pizza Cake') }) + +test('queries entities where property is null', () => { + const db = setup() + + const pizzaCake = db.recipe.findMany({ + where: { + category: { + equals: null, + }, + }, + }) + + const titles = pizzaCake.map((recipe) => recipe.title) + expect(titles).toEqual(['Pizza Cake']) +}) + +test('queries entities where property is not null', () => { + const db = setup() + + const pizzaCake = db.recipe.findMany({ + where: { + category: { + notEquals: null, + }, + }, + }) + + const titles = pizzaCake.map((recipe) => recipe.title) + expect(titles).toEqual(['New York Pizza', 'Chocolate Cake', 'Pizza Mozzarrela']) +})