7
7
} from '../types' ;
8
8
import { buildAnd as and } from '../builder' ;
9
9
import { parseInstruction } from './defaultInstructionParsers' ;
10
- import { identity , hasOperators } from '../utils' ;
10
+ import { identity , hasOperators , object } from '../utils' ;
11
11
12
12
export type FieldQueryOperators < T extends { } > = {
13
13
[ K in keyof T ] : T [ K ] extends { } ? T [ K ] : never
@@ -18,46 +18,57 @@ type ParsingInstructions = Record<string, NamedInstruction>;
18
18
export interface QueryOptions {
19
19
operatorToConditionName ?( name : string ) : string
20
20
defaultOperatorName ?: string
21
+ fieldContext ?: Record < string , unknown >
22
+ documentContext ?: Record < string , unknown >
21
23
}
22
24
23
25
export type ObjectQueryFieldParsingContext = ParsingContext < FieldParsingContext & {
24
- query : unknown ,
26
+ query : { } ,
25
27
hasOperators < T > ( value : unknown ) : value is T
26
28
} > ;
27
29
28
30
export class ObjectQueryParser <
29
31
T extends Record < any , any > ,
30
32
U extends FieldQueryOperators < T > = FieldQueryOperators < T >
31
33
> {
32
- protected readonly _instructions : ParsingInstructions ;
33
- protected _fieldInstructionContext : ObjectQueryFieldParsingContext ;
34
- protected readonly _options : Required < QueryOptions > ;
35
-
36
- constructor ( instructions : Record < string , ParsingInstruction > , options ?: QueryOptions ) {
34
+ private readonly _instructions : ParsingInstructions ;
35
+ private _fieldInstructionContext : ObjectQueryFieldParsingContext ;
36
+ private _documentInstructionContext : ParsingContext < { query : { } } > ;
37
+ private readonly _options : Required <
38
+ Pick < QueryOptions , 'operatorToConditionName' | 'defaultOperatorName' >
39
+ > ;
40
+
41
+ constructor ( instructions : Record < string , ParsingInstruction > , options : QueryOptions = object ( ) ) {
37
42
this . parse = this . parse . bind ( this ) ;
38
43
this . _options = {
39
- operatorToConditionName : identity ,
40
- defaultOperatorName : 'eq' ,
41
- ...options
44
+ operatorToConditionName : options . operatorToConditionName || identity ,
45
+ defaultOperatorName : options . defaultOperatorName || 'eq' ,
42
46
} ;
43
47
this . _instructions = Object . keys ( instructions ) . reduce ( ( all , name ) => {
44
48
all [ name ] = { name : this . _options . operatorToConditionName ( name ) , ...instructions [ name ] } ;
45
49
return all ;
46
50
} , { } as ParsingInstructions ) ;
47
51
this . _fieldInstructionContext = {
52
+ ...options . fieldContext ,
48
53
field : '' ,
49
54
query : { } ,
50
55
parse : this . parse ,
51
56
hasOperators : < T > ( value : unknown ) : value is T => hasOperators ( value , this . _instructions ) ,
52
57
} ;
58
+ this . _documentInstructionContext = {
59
+ ...options . documentContext ,
60
+ parse : this . parse ,
61
+ query : { }
62
+ } ;
53
63
}
54
64
55
65
setParse ( parse : this[ 'parse' ] ) {
56
66
this . parse = parse ;
57
67
this . _fieldInstructionContext . parse = parse ;
68
+ this . _documentInstructionContext . parse = parse ;
58
69
}
59
70
60
- protected parseField ( field : string , operator : string , value : unknown , parentQuery : unknown ) {
71
+ protected parseField ( field : string , operator : string , value : unknown , parentQuery : { } ) {
61
72
const instruction = this . _instructions [ operator ] ;
62
73
63
74
if ( ! instruction ) {
@@ -96,11 +107,12 @@ export class ObjectQueryParser<
96
107
return conditions ;
97
108
}
98
109
99
- parse < Q extends T , FQ extends U = U > ( query : Q ) : Condition {
100
- const defaultContext = { query, parse : this . parse } ;
110
+ parse < Q extends T > ( query : Q ) : Condition {
101
111
const conditions = [ ] ;
102
112
const keys = Object . keys ( query ) ;
103
113
114
+ this . _documentInstructionContext . query = query ;
115
+
104
116
for ( let i = 0 , length = keys . length ; i < length ; i ++ ) {
105
117
const key = keys [ i ] ;
106
118
const value = query [ key ] ;
@@ -111,8 +123,8 @@ export class ObjectQueryParser<
111
123
throw new Error ( `Cannot use parsing instruction for operator "${ key } " in "document" context as it is supposed to be used in "${ instruction . type } " context` ) ;
112
124
}
113
125
114
- conditions . push ( parseInstruction ( instruction , value , defaultContext ) ) ;
115
- } else if ( hasOperators < FQ > ( value , this . _instructions ) ) {
126
+ conditions . push ( parseInstruction ( instruction , value , this . _documentInstructionContext ) ) ;
127
+ } else if ( hasOperators < U > ( value , this . _instructions ) ) {
116
128
conditions . push ( ...this . parseFieldOperators ( key , value ) ) ;
117
129
} else {
118
130
conditions . push ( this . parseField ( key , this . _options . defaultOperatorName , value , query ) ) ;
0 commit comments