Skip to content

Commit d41435a

Browse files
committed
feat(query): add primary closure support for the where clause
1 parent 108ab47 commit d41435a

File tree

7 files changed

+59
-20
lines changed

7 files changed

+59
-20
lines changed

src/model/attributes/relations/BelongsTo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class BelongsTo extends Relation {
6262
* Set the constraints for an eager load of the relation.
6363
*/
6464
addEagerConstraints(query: Query, models: Collection): void {
65-
query.whereIn(this.ownerKey, this.getEagerModelKeys(models))
65+
query.whereIn(this.ownerKey as any, this.getEagerModelKeys(models))
6666
}
6767

6868
/**

src/model/attributes/relations/HasMany.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class HasMany extends Relation {
5858
* Set the constraints for an eager load of the relation.
5959
*/
6060
addEagerConstraints(query: Query, models: Collection): void {
61-
query.whereIn(this.foreignKey, this.getKeys(models, this.localKey))
61+
query.whereIn(this.foreignKey as any, this.getKeys(models, this.localKey))
6262
}
6363

6464
/**

src/model/attributes/relations/HasOne.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class HasOne extends Relation {
5252
* Set the constraints for an eager load of the relation.
5353
*/
5454
addEagerConstraints(query: Query, models: Collection): void {
55-
query.whereIn(this.foreignKey, this.getKeys(models, this.localKey))
55+
query.whereIn(this.foreignKey as any, this.getKeys(models, this.localKey))
5656
}
5757

5858
/**

src/query/Options.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import { Model } from '../model/Model'
22
import { Query } from './Query'
33

4-
export type WhereSecondaryClosure<M extends Model, K extends keyof M> = (
5-
value: M[K]
6-
) => boolean
7-
84
export interface Where<M extends Model, T extends keyof M> {
9-
field: T
5+
field: WherePrimaryClosure<M> | T
106
value: WhereSecondaryClosure<M, T> | M[T] | M[T][]
117
boolean: 'and' | 'or'
128
}
139

10+
export type WherePrimaryClosure<M extends Model> = (model: M) => boolean
11+
12+
export type WhereSecondaryClosure<M extends Model, K extends keyof M> = (
13+
value: M[K]
14+
) => boolean
15+
1416
export interface WhereGroup<M extends Model, T extends keyof M> {
1517
and?: Where<M, T>[]
1618
or?: Where<M, T>[]

src/query/Query.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Model } from '../model/Model'
1212
import { Connection } from '../connection/Connection'
1313
import {
1414
Where,
15+
WherePrimaryClosure,
1516
WhereSecondaryClosure,
1617
Order,
1718
OrderDirection,
@@ -80,9 +81,10 @@ export class Query<M extends Model = Model> {
8081
/**
8182
* Add a basic where clause to the query.
8283
*/
83-
where<T extends keyof M>(field: T, value: WhereSecondaryClosure<M, T>): this
84-
where<T extends keyof M>(field: T, value: M[T] | M[T][]): this
85-
where(field: any, value: any): any {
84+
where<T extends keyof M>(
85+
field: WherePrimaryClosure<M> | T,
86+
value?: WhereSecondaryClosure<M, T> | M[T] | M[T][]
87+
): this {
8688
this.wheres.push({ field, value, boolean: 'and' })
8789

8890
return this
@@ -107,9 +109,10 @@ export class Query<M extends Model = Model> {
107109
/**
108110
* Add an "or where" clause to the query.
109111
*/
110-
orWhere<T extends keyof M>(field: T, value: WhereSecondaryClosure<M, T>): this
111-
orWhere<T extends keyof M>(field: T, value: M[T] | M[T][]): this
112-
orWhere(field: any, value: any): any {
112+
orWhere<T extends keyof M>(
113+
field: WherePrimaryClosure<M> | T,
114+
value?: WhereSecondaryClosure<M, T> | M[T] | M[T][]
115+
): this {
113116
this.wheres.push({ field, value, boolean: 'or' })
114117

115118
return this
@@ -273,6 +276,10 @@ export class Query<M extends Model = Model> {
273276
* The function to compare where clause to the given model.
274277
*/
275278
protected whereComparator(model: M, where: Where<M, any>): boolean {
279+
if (isFunction(where.field)) {
280+
return where.field(model)
281+
}
282+
276283
if (isArray(where.value)) {
277284
return where.value.includes(model[where.field])
278285
}

src/repository/Repository.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import {
1111
import { Model } from '../model/Model'
1212
import { Interpreter } from '../interpreter/Interpreter'
1313
import { Query } from '../query/Query'
14-
import { WhereSecondaryClosure, OrderDirection } from '../query/Options'
14+
import {
15+
WherePrimaryClosure,
16+
WhereSecondaryClosure,
17+
OrderDirection
18+
} from '../query/Options'
1519

1620
export type PersistMethod = 'insert' | 'merge'
1721

@@ -67,11 +71,9 @@ export class Repository<M extends Model> {
6771
* Add a basic where clause to the query.
6872
*/
6973
where<T extends keyof M>(
70-
field: T,
71-
value: WhereSecondaryClosure<M, T>
72-
): Query<M>
73-
where<T extends keyof M>(field: T, value: M[T] | M[T][]): Query<M>
74-
where(field: any, value: any): any {
74+
field: WherePrimaryClosure<M> | T,
75+
value?: WhereSecondaryClosure<M, T> | M[T] | M[T][]
76+
): Query<M> {
7577
return this.query().where(field, value)
7678
}
7779

@@ -82,7 +84,9 @@ export class Repository<M extends Model> {
8284
field: T,
8385
value: WhereSecondaryClosure<M, T>
8486
): Query<M>
87+
8588
orWhere<T extends keyof M>(field: T, value: M[T] | M[T][]): Query<M>
89+
8690
orWhere(field: any, value: any): any {
8791
return this.query().orWhere(field, value)
8892
}

test/feature/repository/retrieves_where.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,30 @@ describe('feature/repository/retrieves_where', () => {
8787
assertInstanceOf(users, User)
8888
assertModels(users, expected)
8989
})
90+
91+
it('can filter records by passing closure as a field', () => {
92+
const store = createStore([User])
93+
94+
fillState(store, {
95+
users: {
96+
1: { id: 1, name: 'John Doe', age: 30 },
97+
2: { id: 2, name: 'Jane Doe', age: 30 },
98+
3: { id: 3, name: 'Johnny Doe', age: 20 }
99+
}
100+
})
101+
102+
const users = store
103+
.$repo(User)
104+
.where((user) => user.age === 30)
105+
.get()
106+
107+
const expected = [
108+
{ id: 1, name: 'John Doe', age: 30 },
109+
{ id: 2, name: 'Jane Doe', age: 30 }
110+
]
111+
112+
expect(users.length).toBe(2)
113+
assertInstanceOf(users, User)
114+
assertModels(users, expected)
115+
})
90116
})

0 commit comments

Comments
 (0)