1
- import { BSONRegExp , Decimal128 , ObjectId } from 'bson' ;
1
+ import {
2
+ Binary ,
3
+ BSONRegExp ,
4
+ BSONSymbol ,
5
+ Code ,
6
+ DBRef ,
7
+ Decimal128 ,
8
+ Long ,
9
+ MaxKey ,
10
+ MinKey ,
11
+ ObjectId
12
+ } from 'bson' ;
2
13
import { expectAssignable , expectError , expectNotType , expectType } from 'tsd' ;
3
14
4
15
import { Collection , Filter , MongoClient , WithId } from '../../../../src' ;
@@ -16,6 +27,11 @@ const db = client.db('test');
16
27
* Test the generic Filter using collection.find<T>() method
17
28
*/
18
29
30
+ interface HumanModel {
31
+ _id : ObjectId ;
32
+ name : string ;
33
+ }
34
+
19
35
// a collection model for all possible MongoDB BSON types and TypeScript types
20
36
interface PetModel {
21
37
_id : ObjectId ; // ObjectId field
@@ -24,14 +40,42 @@ interface PetModel {
24
40
age : number ; // number field
25
41
type : 'dog' | 'cat' | 'fish' ; // union field
26
42
isCute : boolean ; // boolean field
27
- bestFriend ?: PetModel ; // object field (Embedded/Nested Documents)
43
+ bestFriend ?: HumanModel ; // object field (Embedded/Nested Documents)
28
44
createdAt : Date ; // date field
45
+ numOfPats : Long ; // long field
29
46
treats : string [ ] ; // array of string
30
47
playTimePercent : Decimal128 ; // bson Decimal128 type
31
- readonly friends ?: ReadonlyArray < PetModel > ; // readonly array of objects
32
- playmates ?: PetModel [ ] ; // writable array of objects
48
+ readonly friends ?: ReadonlyArray < HumanModel > ; // readonly array of objects
49
+ playmates ?: HumanModel [ ] ; // writable array of objects
50
+ laps ?: Map < string , number > ; // map field
51
+ // Object with multiple nested levels
52
+ meta ?: {
53
+ updatedAt ?: Date ;
54
+ deep ?: {
55
+ nestedArray : number [ ] ;
56
+ nested ?: {
57
+ level ?: number ;
58
+ } ;
59
+ } ;
60
+ } ;
61
+
62
+ binary : Binary ;
63
+ code : Code ;
64
+ minKey : MinKey ;
65
+ maxKey : MaxKey ;
66
+ dBRef : DBRef ;
67
+ bSONSymbol : BSONSymbol ;
68
+
69
+ regex : RegExp ;
70
+
71
+ fn : ( ...args : any [ ] ) => any ;
33
72
}
34
73
74
+ const john = {
75
+ _id : new ObjectId ( '577fa2d90c4cc47e31cf4b6a' ) ,
76
+ name : 'John'
77
+ } ;
78
+
35
79
const spot = {
36
80
_id : new ObjectId ( '577fa2d90c4cc47e31cf4b6f' ) ,
37
81
name : 'Spot' ,
@@ -40,16 +84,30 @@ const spot = {
40
84
type : 'dog' as const ,
41
85
isCute : true ,
42
86
createdAt : new Date ( ) ,
87
+ numOfPats : Long . fromBigInt ( 100000000n ) ,
43
88
treats : [ 'kibble' , 'bone' ] ,
44
- playTimePercent : new Decimal128 ( '0.999999' )
89
+ playTimePercent : new Decimal128 ( '0.999999' ) ,
90
+
91
+ binary : new Binary ( '' , 2 ) ,
92
+ code : new Code ( ( ) => true ) ,
93
+ minKey : new MinKey ( ) ,
94
+ maxKey : new MaxKey ( ) ,
95
+ dBRef : new DBRef ( 'collection' , new ObjectId ( ) ) ,
96
+ bSONSymbol : new BSONSymbol ( 'hi' ) ,
97
+
98
+ regex : / a / ,
99
+
100
+ fn ( ) {
101
+ return 'hi' ;
102
+ }
45
103
} ;
46
104
47
105
expectAssignable < PetModel > ( spot ) ;
48
106
49
107
const collectionT = db . collection < PetModel > ( 'test.filterQuery' ) ;
50
108
51
109
// Assert that collection.find uses the Filter helper like so:
52
- const filter : Filter < PetModel > = { } ;
110
+ const filter : Filter < PetModel > = { } as Filter < PetModel > ;
53
111
collectionT . find ( filter ) ;
54
112
collectionT . find ( spot ) ; // a whole model definition is also a valid filter
55
113
// Now tests below can directly test the Filter helper, and are implicitly checking collection.find
@@ -73,6 +131,10 @@ expectNotType<Filter<PetModel>>({ name: 23 });
73
131
expectNotType < Filter < PetModel > > ( { name : { suffix : 'Jr' } } ) ;
74
132
expectNotType < Filter < PetModel > > ( { name : [ 'Spot' ] } ) ;
75
133
134
+ // it should not accept wrong types for function fields
135
+ expectNotType < Filter < PetModel > > ( { fn : 3 } ) ;
136
+ expectAssignable < WithId < PetModel > [ ] > ( await collectionT . find ( { fn : ( ) => true } ) . toArray ( ) ) ;
137
+
76
138
/// it should query __number__ fields
77
139
await collectionT . find ( { age : 12 } ) . toArray ( ) ;
78
140
/// it should not accept wrong types for number fields
@@ -83,14 +145,67 @@ expectNotType<Filter<PetModel>>({ age: [23, 43] });
83
145
84
146
/// it should query __nested document__ fields only by exact match
85
147
// TODO: we currently cannot enforce field order but field order is important for mongo
86
- await collectionT . find ( { bestFriend : spot } ) . toArray ( ) ;
148
+ await collectionT . find ( { bestFriend : john } ) . toArray ( ) ;
87
149
/// nested documents query should contain all required fields
88
- expectNotType < Filter < PetModel > > ( { bestFriend : { family : 'Andersons' } } ) ;
150
+ expectNotType < Filter < PetModel > > ( { bestFriend : { name : 'Andersons' } } ) ;
89
151
/// it should not accept wrong types for nested document fields
90
152
expectNotType < Filter < PetModel > > ( { bestFriend : 21 } ) ;
91
153
expectNotType < Filter < PetModel > > ( { bestFriend : 'Andersons' } ) ;
92
154
expectNotType < Filter < PetModel > > ( { bestFriend : [ spot ] } ) ;
93
- expectNotType < Filter < PetModel > > ( { bestFriend : [ { family : 'Andersons' } ] } ) ;
155
+ expectNotType < Filter < PetModel > > ( { bestFriend : [ { name : 'Andersons' } ] } ) ;
156
+
157
+ // it should permit all our BSON types as query values
158
+ expectAssignable < Filter < PetModel > > ( { binary : new Binary ( '' , 2 ) } ) ;
159
+ expectAssignable < Filter < PetModel > > ( { code : new Code ( ( ) => true ) } ) ;
160
+ expectAssignable < Filter < PetModel > > ( { minKey : new MinKey ( ) } ) ;
161
+ expectAssignable < Filter < PetModel > > ( { maxKey : new MaxKey ( ) } ) ;
162
+ expectAssignable < Filter < PetModel > > ( { dBRef : new DBRef ( 'collection' , new ObjectId ( ) ) } ) ;
163
+ expectAssignable < Filter < PetModel > > ( { bSONSymbol : new BSONSymbol ( 'hi' ) } ) ;
164
+
165
+ // None of the bson types should be broken up into their nested keys
166
+ expectNotType < Filter < PetModel > > ( { 'binary.sub_type' : 2 } ) ;
167
+ expectNotType < Filter < PetModel > > ( { 'code.code' : 'string' } ) ;
168
+ expectNotType < Filter < PetModel > > ( { 'minKey._bsontype' : 'MinKey' } ) ;
169
+ expectNotType < Filter < PetModel > > ( { 'maxKey._bsontype' : 'MaxKey' } ) ;
170
+ expectNotType < Filter < PetModel > > ( { 'dBRef.collection' : 'collection' } ) ;
171
+ expectNotType < Filter < PetModel > > ( { 'bSONSymbol.value' : 'hi' } ) ;
172
+ expectNotType < Filter < PetModel > > ( { 'numOfPats.__isLong__' : true } ) ;
173
+ expectNotType < Filter < PetModel > > ( { 'playTimePercent.bytes.BYTES_PER_ELEMENT' : 1 } ) ;
174
+ expectNotType < Filter < PetModel > > ( { 'binary.sub_type' : 'blah' } ) ;
175
+ expectNotType < Filter < PetModel > > ( { 'regex.dotAll' : true } ) ;
176
+
177
+ /// it should query __nested document__ fields using dot-notation
178
+ collectionT . find ( { 'meta.updatedAt' : new Date ( ) } ) ;
179
+ collectionT . find ( { 'meta.deep.nested.level' : 123 } ) ;
180
+ collectionT . find ( { meta : { deep : { nested : { level : 123 } } } } ) ; // no impact on actual nesting
181
+ collectionT . find ( { 'friends.0.name' : 'John' } ) ;
182
+ collectionT . find ( { 'playmates.0.name' : 'John' } ) ;
183
+ // supports arrays with primitive types
184
+ collectionT . find ( { 'treats.0' : 'bone' } ) ;
185
+ collectionT . find ( { 'laps.foo' : 123 } ) ;
186
+
187
+ // Handle special BSON types
188
+ collectionT . find ( { numOfPats : Long . fromBigInt ( 2n ) } ) ;
189
+ collectionT . find ( { playTimePercent : new Decimal128 ( '123.2' ) } ) ;
190
+
191
+ // works with some extreme indexes
192
+ collectionT . find ( { 'friends.4294967295.name' : 'John' } ) ;
193
+ collectionT . find ( { 'friends.999999999999999999999999999999999999.name' : 'John' } ) ;
194
+
195
+ /// it should not accept wrong types for nested document fields
196
+ expectNotType < Filter < PetModel > > ( { 'meta.updatedAt' : 123 } ) ;
197
+ expectNotType < Filter < PetModel > > ( { 'meta.updatedAt' : true } ) ;
198
+ expectNotType < Filter < PetModel > > ( { 'meta.updatedAt' : 'now' } ) ;
199
+ expectNotType < Filter < PetModel > > ( { 'meta.deep.nested.level' : 'string' } ) ;
200
+ expectNotType < Filter < PetModel > > ( { 'meta.deep.nested.level' : true } ) ;
201
+ expectNotType < Filter < PetModel > > ( { 'meta.deep.nested.level' : new Date ( ) } ) ;
202
+ expectNotType < Filter < PetModel > > ( { 'friends.0.name' : 123 } ) ;
203
+ expectNotType < Filter < PetModel > > ( { 'playmates.0.name' : 123 } ) ;
204
+ expectNotType < Filter < PetModel > > ( { 'laps.foo' : 'string' } ) ;
205
+ expectNotType < Filter < PetModel > > ( { 'treats.0' : 123 } ) ;
206
+
207
+ // Nested arrays aren't checked
208
+ expectNotType < Filter < PetModel > > ( { 'meta.deep.nestedArray.0' : 'not a number' } ) ;
94
209
95
210
/// it should query __array__ fields by exact match
96
211
await collectionT . find ( { treats : [ 'kibble' , 'bone' ] } ) . toArray ( ) ;
@@ -233,10 +348,6 @@ expectNotType<Filter<PetModel>>({ name: { $all: ['world', 'world'] } });
233
348
expectNotType < Filter < PetModel > > ( { age : { $elemMatch : [ 1 , 2 ] } } ) ;
234
349
expectNotType < Filter < PetModel > > ( { type : { $size : 2 } } ) ;
235
350
236
- // dot key case that shows it is assignable even when the referenced key is the wrong type
237
- expectAssignable < Filter < PetModel > > ( { 'bestFriend.name' : 23 } ) ; // using dot notation permits any type for the key
238
- expectNotType < Filter < PetModel > > ( { bestFriend : { name : 23 } } ) ;
239
-
240
351
// ObjectId are not allowed to be used as a query predicate (issue described here: NODE-3758)
241
352
// this only applies to schemas where the _id is not of type ObjectId.
242
353
declare const nonObjectIdCollection : Collection < { _id : number ; otherField : string } > ;
0 commit comments