Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added support for comparator of null values with equals and notEquals #310

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/comparators/date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { DateQuery, QueryToComparator } from '../query/queryTypes'

export const dateComparators: QueryToComparator<DateQuery> = {
equals(expected, actual) {
return compareDates(expected, actual) === 0
return compareDates(expected as unknown as Date, actual as unknown as Date) === 0
Copy link
Author

@huuyafwww huuyafwww Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added null to equals in DateQuery.
This would cause people to guess that a null value is coming here.
However, since null values cannot be retrieved by getComparatorsForValue, the function for comparison cannot be used.
The null value is now handled before the call.
So the null value will not appear here,
The unknown type is used to overwrite the Date to suppress type errors.

},
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
Expand Down
20 changes: 14 additions & 6 deletions src/query/compileQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,26 @@ export function compileQuery<Data extends Record<string, any>>(
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<boolean>(
(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
}
Comment on lines +41 to +53
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed control when actualValue is null.
Addes comparison if comparatorName is equals or notEquals and expectedValue is null.
This control prevents null values from entering getComparatorsForValue.


if (Array.isArray(actualValue)) {
log(
'actual value is array, checking if at least one item matches...',
Expand Down
12 changes: 6 additions & 6 deletions src/query/queryTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ export type GetQueryFor<ValueType extends any> = ValueType extends string
: never

export interface StringQuery {
equals: string
notEquals: string
equals: string | null
notEquals: string | null
contains: string
notContains: string
gt: string
Expand All @@ -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
Expand All @@ -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
Expand Down
32 changes: 32 additions & 0 deletions test/query/date.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
})
32 changes: 32 additions & 0 deletions test/query/number.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
})
30 changes: 30 additions & 0 deletions test/query/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
})