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' ;
213import { expectAssignable , expectError , expectNotType , expectType } from 'tsd' ;
314
415import { Collection , Filter , MongoClient , WithId } from '../../../../src' ;
@@ -16,6 +27,11 @@ const db = client.db('test');
1627 * Test the generic Filter using collection.find<T>() method
1728 */
1829
30+ interface HumanModel {
31+ _id : ObjectId ;
32+ name : string ;
33+ }
34+
1935// a collection model for all possible MongoDB BSON types and TypeScript types
2036interface PetModel {
2137 _id : ObjectId ; // ObjectId field
@@ -24,14 +40,42 @@ interface PetModel {
2440 age : number ; // number field
2541 type : 'dog' | 'cat' | 'fish' ; // union field
2642 isCute : boolean ; // boolean field
27- bestFriend ?: PetModel ; // object field (Embedded/Nested Documents)
43+ bestFriend ?: HumanModel ; // object field (Embedded/Nested Documents)
2844 createdAt : Date ; // date field
45+ numOfPats : Long ; // long field
2946 treats : string [ ] ; // array of string
3047 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 ;
3372}
3473
74+ const john = {
75+ _id : new ObjectId ( '577fa2d90c4cc47e31cf4b6a' ) ,
76+ name : 'John'
77+ } ;
78+
3579const spot = {
3680 _id : new ObjectId ( '577fa2d90c4cc47e31cf4b6f' ) ,
3781 name : 'Spot' ,
@@ -40,16 +84,30 @@ const spot = {
4084 type : 'dog' as const ,
4185 isCute : true ,
4286 createdAt : new Date ( ) ,
87+ numOfPats : Long . fromBigInt ( 100000000n ) ,
4388 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+ }
45103} ;
46104
47105expectAssignable < PetModel > ( spot ) ;
48106
49107const collectionT = db . collection < PetModel > ( 'test.filterQuery' ) ;
50108
51109// Assert that collection.find uses the Filter helper like so:
52- const filter : Filter < PetModel > = { } ;
110+ const filter : Filter < PetModel > = { } as Filter < PetModel > ;
53111collectionT . find ( filter ) ;
54112collectionT . find ( spot ) ; // a whole model definition is also a valid filter
55113// Now tests below can directly test the Filter helper, and are implicitly checking collection.find
@@ -73,6 +131,10 @@ expectNotType<Filter<PetModel>>({ name: 23 });
73131expectNotType < Filter < PetModel > > ( { name : { suffix : 'Jr' } } ) ;
74132expectNotType < Filter < PetModel > > ( { name : [ 'Spot' ] } ) ;
75133
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+
76138/// it should query __number__ fields
77139await collectionT . find ( { age : 12 } ) . toArray ( ) ;
78140/// it should not accept wrong types for number fields
@@ -83,14 +145,67 @@ expectNotType<Filter<PetModel>>({ age: [23, 43] });
83145
84146/// it should query __nested document__ fields only by exact match
85147// 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 ( ) ;
87149/// nested documents query should contain all required fields
88- expectNotType < Filter < PetModel > > ( { bestFriend : { family : 'Andersons' } } ) ;
150+ expectNotType < Filter < PetModel > > ( { bestFriend : { name : 'Andersons' } } ) ;
89151/// it should not accept wrong types for nested document fields
90152expectNotType < Filter < PetModel > > ( { bestFriend : 21 } ) ;
91153expectNotType < Filter < PetModel > > ( { bestFriend : 'Andersons' } ) ;
92154expectNotType < 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' } ) ;
94209
95210/// it should query __array__ fields by exact match
96211await collectionT . find ( { treats : [ 'kibble' , 'bone' ] } ) . toArray ( ) ;
@@ -233,10 +348,6 @@ expectNotType<Filter<PetModel>>({ name: { $all: ['world', 'world'] } });
233348expectNotType < Filter < PetModel > > ( { age : { $elemMatch : [ 1 , 2 ] } } ) ;
234349expectNotType < Filter < PetModel > > ( { type : { $size : 2 } } ) ;
235350
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-
240351// ObjectId are not allowed to be used as a query predicate (issue described here: NODE-3758)
241352// this only applies to schemas where the _id is not of type ObjectId.
242353declare const nonObjectIdCollection : Collection < { _id : number ; otherField : string } > ;
0 commit comments