@@ -41,7 +41,7 @@ import {
4141 type PostgresClientReference
4242} from '..'
4343import { genMinModel } from './minmodel'
44- import { createTaskModel , type Task , type TaskComment , taskPlugin } from './tasks'
44+ import { createTaskModel , TaskReproduce , TaskStatus , type Task , type TaskComment , taskPlugin } from './tasks'
4545
4646const txes = genMinModel ( )
4747createTaskModel ( txes )
@@ -923,6 +923,142 @@ describe('PostgreSQL Integration Tests (Real Database)', () => {
923923 } )
924924 } )
925925
926+ describe ( '$ne predicate with missing fields' , ( ) => {
927+ let taskWithRate100 : Ref < Task >
928+ let taskWithRate50 : Ref < Task >
929+ let taskWithRateNull : Ref < Task >
930+ let taskWithoutRate : Ref < Task >
931+
932+ beforeEach ( async ( ) => {
933+ // Create tasks with different rate scenarios
934+ taskWithRate100 = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
935+ name : 'Task with rate 100' ,
936+ description : 'Should not match $ne: 100' ,
937+ rate : 100
938+ } )
939+
940+ taskWithRate50 = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
941+ name : 'Task with rate 50' ,
942+ description : 'Should match $ne: 100' ,
943+ rate : 50
944+ } )
945+
946+ taskWithRateNull = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
947+ name : 'Task with rate null' ,
948+ description : 'Should match $ne: 100 (null != 100)' ,
949+ rate : null
950+ } )
951+
952+ // Create task without rate field (missing field)
953+ taskWithoutRate = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
954+ name : 'Task without rate field' ,
955+ description : 'Should match $ne: 100 (missing field should match)'
956+ // rate field is not provided - it will be missing/undefined in the document
957+ } )
958+ } )
959+
960+ it ( 'should match documents with missing fields when using $ne' , async ( ) => {
961+ const tasks = await client . findAll < Task > ( taskPlugin . class . Task , { rate : { $ne : 100 } } )
962+
963+ // Should match:
964+ // - Task with rate 50 (different value)
965+ // - Task with rate null (null != 100)
966+ // - Task without rate field (missing field should match)
967+ // Should NOT match:
968+ // - Task with rate 100
969+
970+ expect ( tasks ) . toHaveLength ( 3 )
971+
972+ const taskIds = tasks . map ( ( t ) => t . _id )
973+ expect ( taskIds ) . not . toContain ( taskWithRate100 )
974+ expect ( taskIds ) . toContain ( taskWithRate50 )
975+ expect ( taskIds ) . toContain ( taskWithRateNull )
976+ expect ( taskIds ) . toContain ( taskWithoutRate )
977+
978+ // Verify that the task without rate field has undefined or null rate
979+ const taskWithoutRateDoc = tasks . find ( ( t ) => t . _id === taskWithoutRate )
980+ expect ( taskWithoutRateDoc ) . toBeDefined ( )
981+ expect ( taskWithoutRateDoc ?. rate === undefined || taskWithoutRateDoc ?. rate === null ) . toBe ( true )
982+ } )
983+
984+ it ( 'should match documents with missing fields when using $ne with boolean true' , async ( ) => {
985+ // Test with a boolean field scenario
986+ // Create tasks with status field (which is optional)
987+ const taskWithStatusOpen = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
988+ name : 'Task with status Open' ,
989+ description : 'Has status' ,
990+ status : TaskStatus . Open
991+ } )
992+
993+ const taskWithStatusClose = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
994+ name : 'Task with status Close' ,
995+ description : 'Has different status' ,
996+ status : TaskStatus . Close
997+ } )
998+
999+ const taskWithoutStatus = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
1000+ name : 'Task without status' ,
1001+ description : 'Missing status field'
1002+ // status field is not provided
1003+ } )
1004+
1005+ // Query for tasks where status is not Open
1006+ const tasks = await client . findAll < Task > ( taskPlugin . class . Task , { status : { $ne : TaskStatus . Open } } )
1007+
1008+ // Should match:
1009+ // - Task with status Close (different value)
1010+ // - Task without status field (missing field should match)
1011+ // Should NOT match:
1012+ // - Task with status Open
1013+
1014+ expect ( tasks . length ) . toBeGreaterThanOrEqual ( 2 )
1015+
1016+ const taskIds = tasks . map ( ( t ) => t . _id )
1017+ expect ( taskIds ) . not . toContain ( taskWithStatusOpen )
1018+ expect ( taskIds ) . toContain ( taskWithStatusClose )
1019+ expect ( taskIds ) . toContain ( taskWithoutStatus )
1020+ } )
1021+
1022+ it ( 'should handle $ne with string value and missing fields' , async ( ) => {
1023+ // Test with a string field that can be missing
1024+ const taskWithReproduceAlways = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
1025+ name : 'Task with reproduce Always' ,
1026+ description : 'Has reproduce field' ,
1027+ reproduce : TaskReproduce . Always
1028+ } )
1029+
1030+ const taskWithReproduceRare = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
1031+ name : 'Task with reproduce Rare' ,
1032+ description : 'Has different reproduce value' ,
1033+ reproduce : TaskReproduce . Rare
1034+ } )
1035+
1036+ const taskWithoutReproduce = await operations . createDoc ( taskPlugin . class . Task , '' as Ref < Space > , {
1037+ name : 'Task without reproduce field' ,
1038+ description : 'Missing reproduce field'
1039+ // reproduce field is not provided
1040+ } )
1041+
1042+ // Query for tasks where reproduce is not Always
1043+ const tasks = await client . findAll < Task > ( taskPlugin . class . Task , {
1044+ reproduce : { $ne : TaskReproduce . Always }
1045+ } )
1046+
1047+ // Should match:
1048+ // - Task with reproduce Rare (different value)
1049+ // - Task without reproduce field (missing field should match)
1050+ // Should NOT match:
1051+ // - Task with reproduce Always
1052+
1053+ expect ( tasks . length ) . toBeGreaterThanOrEqual ( 2 )
1054+
1055+ const taskIds = tasks . map ( ( t ) => t . _id )
1056+ expect ( taskIds ) . not . toContain ( taskWithReproduceAlways )
1057+ expect ( taskIds ) . toContain ( taskWithReproduceRare )
1058+ expect ( taskIds ) . toContain ( taskWithoutReproduce )
1059+ } )
1060+ } )
1061+
9261062 describe ( 'Projection with Lookups Combined' , ( ) => {
9271063 let taskId : Ref < Task >
9281064
0 commit comments