@simtlix/simfinity-js 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,7 @@ A powerful Node.js framework that automatically generates GraphQL schemas from y
8
8
  - **MongoDB Integration**: Seamless translation between GraphQL and MongoDB
9
9
  - **Powerful Querying**: Any query that can be executed in MongoDB can be executed in GraphQL
10
10
  - **Auto-Generated Resolvers**: Automatically generates resolve methods for relationship fields
11
+ - **Automatic Index Creation**: Automatically creates MongoDB indexes for all ObjectId fields, including nested embedded objects and relationship fields
11
12
  - **Business Logic**: Implement business logic and domain validations declaratively
12
13
  - **State Machines**: Built-in support for declarative state machine workflows
13
14
  - **Lifecycle Hooks**: Controller methods for granular control over operations
@@ -0,0 +1,252 @@
1
+ # Automatic ObjectId Index Creation
2
+
3
+ This feature automatically creates MongoDB indexes for all ObjectId fields when models are generated, including nested embedded objects.
4
+
5
+ ## How it works
6
+
7
+ When you define GraphQL types with ObjectId fields (GraphQLID), the system will automatically:
8
+
9
+ 1. Analyze the schema definition to find all ObjectId fields
10
+ 2. Create indexes for each ObjectId field found
11
+ 3. Handle nested embedded objects recursively
12
+ 4. Create indexes for arrays of embedded objects
13
+ 5. Create indexes for relationship fields (foreign keys between types)
14
+
15
+ ## Examples
16
+
17
+ ### Simple ObjectId fields
18
+
19
+ ```javascript
20
+ import { GraphQLObjectType, GraphQLString, GraphQLID } from 'graphql';
21
+ import * as simfinity from '@simtlix/simfinity-js';
22
+
23
+ const UserType = new GraphQLObjectType({
24
+ name: 'User',
25
+ fields: () => ({
26
+ id: { type: GraphQLID },
27
+ name: { type: GraphQLString },
28
+ email: { type: GraphQLString },
29
+ managerId: { type: GraphQLID }, // This will get an index
30
+ }),
31
+ });
32
+
33
+ simfinity.connect(null, UserType, 'user', 'users');
34
+ simfinity.createSchema();
35
+ ```
36
+
37
+ This will create indexes for:
38
+ - `id` field
39
+ - `managerId` field
40
+
41
+ ### Relationship fields (non-embedded)
42
+
43
+ ```javascript
44
+ const DepartmentType = new GraphQLObjectType({
45
+ name: 'Department',
46
+ fields: () => ({
47
+ id: { type: GraphQLID },
48
+ name: { type: GraphQLString },
49
+ }),
50
+ });
51
+
52
+ const UserType = new GraphQLObjectType({
53
+ name: 'User',
54
+ fields: () => ({
55
+ id: { type: GraphQLID },
56
+ name: { type: GraphQLString },
57
+ email: { type: GraphQLString },
58
+ department: {
59
+ type: DepartmentType,
60
+ extensions: {
61
+ relation: { embedded: false } // or omit this line (default is false)
62
+ }
63
+ },
64
+ }),
65
+ });
66
+
67
+ // Register both types
68
+ simfinity.connect(null, DepartmentType, 'department', 'departments');
69
+ simfinity.connect(null, UserType, 'user', 'users');
70
+ simfinity.createSchema();
71
+ ```
72
+
73
+ This will create indexes for:
74
+ - `id` field (in both User and Department)
75
+ - `department` field (the foreign key field that references Department)
76
+
77
+ ### One-to-Many relationships
78
+
79
+ ```javascript
80
+ const OrderType = new GraphQLObjectType({
81
+ name: 'Order',
82
+ fields: () => ({
83
+ id: { type: GraphQLID },
84
+ orderNumber: { type: GraphQLString },
85
+ customerId: { type: GraphQLID }, // This will get an index
86
+ items: {
87
+ type: new GraphQLList(OrderItemType),
88
+ extensions: {
89
+ relation: { embedded: false }
90
+ }
91
+ },
92
+ }),
93
+ });
94
+
95
+ const OrderItemType = new GraphQLObjectType({
96
+ name: 'OrderItem',
97
+ fields: () => ({
98
+ id: { type: GraphQLID },
99
+ productId: { type: GraphQLID }, // This will get an index
100
+ quantity: { type: GraphQLString },
101
+ orderId: { type: GraphQLID }, // This will get an index (foreign key)
102
+ }),
103
+ });
104
+
105
+ // Register both types
106
+ simfinity.connect(null, OrderItemType, 'orderItem', 'orderItems');
107
+ simfinity.connect(null, OrderType, 'order', 'orders');
108
+ simfinity.createSchema();
109
+ ```
110
+
111
+ This will create indexes for:
112
+ - `id` field (in both Order and OrderItem)
113
+ - `customerId` field (in Order)
114
+ - `productId` field (in OrderItem)
115
+ - `orderId` field (in OrderItem - the foreign key that references Order)
116
+
117
+ ### Embedded objects with ObjectId
118
+
119
+ ```javascript
120
+ const AddressType = new GraphQLObjectType({
121
+ name: 'Address',
122
+ fields: () => ({
123
+ street: { type: GraphQLString },
124
+ city: { type: GraphQLString },
125
+ countryId: { type: GraphQLID }, // This will get an index
126
+ }),
127
+ });
128
+
129
+ const UserType = new GraphQLObjectType({
130
+ name: 'User',
131
+ fields: () => ({
132
+ id: { type: GraphQLID },
133
+ name: { type: GraphQLString },
134
+ address: {
135
+ type: AddressType,
136
+ extensions: {
137
+ relation: { embedded: true }
138
+ }
139
+ },
140
+ }),
141
+ });
142
+
143
+ // Register the embedded type
144
+ simfinity.addNoEndpointType(AddressType);
145
+ simfinity.connect(null, UserType, 'user', 'users');
146
+ simfinity.createSchema();
147
+ ```
148
+
149
+ This will create indexes for:
150
+ - `id` field
151
+ - `countryId` field (from AddressType)
152
+ - `address.countryId` field (embedded path)
153
+
154
+ ### Nested embedded objects
155
+
156
+ ```javascript
157
+ const CityType = new GraphQLObjectType({
158
+ name: 'City',
159
+ fields: () => ({
160
+ name: { type: GraphQLString },
161
+ stateId: { type: GraphQLID }, // This will get an index
162
+ }),
163
+ });
164
+
165
+ const AddressType = new GraphQLObjectType({
166
+ name: 'Address',
167
+ fields: () => ({
168
+ street: { type: GraphQLString },
169
+ city: {
170
+ type: CityType,
171
+ extensions: {
172
+ relation: { embedded: true }
173
+ }
174
+ },
175
+ }),
176
+ });
177
+
178
+ const UserType = new GraphQLObjectType({
179
+ name: 'User',
180
+ fields: () => ({
181
+ id: { type: GraphQLID },
182
+ name: { type: GraphQLString },
183
+ address: {
184
+ type: AddressType,
185
+ extensions: {
186
+ relation: { embedded: true }
187
+ }
188
+ },
189
+ }),
190
+ });
191
+
192
+ // Register the embedded types
193
+ simfinity.addNoEndpointType(CityType);
194
+ simfinity.addNoEndpointType(AddressType);
195
+ simfinity.connect(null, UserType, 'user', 'users');
196
+ simfinity.createSchema();
197
+ ```
198
+
199
+ This will create indexes for:
200
+ - `id` field
201
+ - `stateId` field (from CityType)
202
+ - `city.stateId` field (from AddressType)
203
+ - `address.city.stateId` field (nested embedded path)
204
+
205
+ ### Arrays of embedded objects
206
+
207
+ ```javascript
208
+ const OrderItemType = new GraphQLObjectType({
209
+ name: 'OrderItem',
210
+ fields: () => ({
211
+ productId: { type: GraphQLID }, // This will get an index
212
+ quantity: { type: GraphQLString },
213
+ }),
214
+ });
215
+
216
+ const OrderType = new GraphQLObjectType({
217
+ name: 'Order',
218
+ fields: () => ({
219
+ id: { type: GraphQLID },
220
+ customerId: { type: GraphQLID },
221
+ items: {
222
+ type: new GraphQLList(OrderItemType),
223
+ extensions: {
224
+ relation: { embedded: true }
225
+ }
226
+ },
227
+ }),
228
+ });
229
+
230
+ // Register the embedded type
231
+ simfinity.addNoEndpointType(OrderItemType);
232
+ simfinity.connect(null, OrderType, 'order', 'orders');
233
+ simfinity.createSchema();
234
+ ```
235
+
236
+ This will create indexes for:
237
+ - `id` field
238
+ - `customerId` field
239
+ - `productId` field (from OrderItemType)
240
+ - `items.productId` field (array embedded path)
241
+
242
+ ## Benefits
243
+
244
+ 1. **Automatic Performance Optimization**: ObjectId fields are automatically indexed for better query performance
245
+ 2. **Relationship Optimization**: Foreign key fields are automatically indexed for efficient joins and lookups
246
+ 3. **No Manual Configuration**: No need to manually specify which fields should be indexed
247
+ 4. **Handles Complex Schemas**: Works with nested embedded objects and arrays
248
+ 5. **Consistent**: All ObjectId fields get the same treatment regardless of their location in the schema
249
+
250
+ ## Technical Details
251
+
252
+ The system uses the `mongoose.Schema` constructor to create schemas and then calls `schema.index()` for each ObjectId field found. The index creation happens during model generation, so it's transparent to the application code.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simtlix/simfinity-js",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -1003,8 +1003,56 @@ const generateSchemaDefinition = (gqlType) => {
1003
1003
  return schemaArg;
1004
1004
  };
1005
1005
 
1006
+ const findObjectIdFields = (schemaDefinition, parentPath = '') => {
1007
+ const objectIdFields = [];
1008
+
1009
+ for (const [fieldName, fieldDefinition] of Object.entries(schemaDefinition)) {
1010
+ const currentPath = parentPath ? `${parentPath}.${fieldName}` : fieldName;
1011
+
1012
+ if (fieldDefinition === mongoose.Schema.Types.ObjectId) {
1013
+ // Direct ObjectId field
1014
+ objectIdFields.push(currentPath);
1015
+ } else if (typeof fieldDefinition === 'object' && fieldDefinition !== null) {
1016
+ if (Array.isArray(fieldDefinition)) {
1017
+ // Array field - check if it's an array of objects
1018
+ const arrayElement = fieldDefinition[0];
1019
+ if (typeof arrayElement === 'object' && arrayElement !== null) {
1020
+ // Array of embedded objects - recursively check for ObjectId fields
1021
+ const nestedObjectIdFields = findObjectIdFields(arrayElement, currentPath);
1022
+ objectIdFields.push(...nestedObjectIdFields);
1023
+ }
1024
+ } else if (fieldDefinition.type === mongoose.Schema.Types.ObjectId) {
1025
+ // Object with ObjectId type
1026
+ objectIdFields.push(currentPath);
1027
+ } else if (typeof fieldDefinition === 'object' && !fieldDefinition.type) {
1028
+ // Embedded object - recursively check for ObjectId fields
1029
+ const nestedObjectIdFields = findObjectIdFields(fieldDefinition, currentPath);
1030
+ objectIdFields.push(...nestedObjectIdFields);
1031
+ }
1032
+ }
1033
+ }
1034
+
1035
+ return objectIdFields;
1036
+ };
1037
+
1038
+ const createSchemaWithIndexes = (schemaDefinition) => {
1039
+ const schema = new mongoose.Schema(schemaDefinition);
1040
+
1041
+ // Find all ObjectId fields in the schema
1042
+ const objectIdFields = findObjectIdFields(schemaDefinition);
1043
+
1044
+ // Create indexes for all ObjectId fields
1045
+ objectIdFields.forEach(fieldPath => {
1046
+ schema.index({ [fieldPath]: 1 });
1047
+ });
1048
+
1049
+ return schema;
1050
+ };
1051
+
1006
1052
  const generateModel = (gqlType, onModelCreated) => {
1007
- const model = mongoose.model(gqlType.name, generateSchemaDefinition(gqlType), gqlType.name);
1053
+ const schemaDefinition = generateSchemaDefinition(gqlType);
1054
+ const schema = createSchemaWithIndexes(schemaDefinition);
1055
+ const model = mongoose.model(gqlType.name, schema, gqlType.name);
1008
1056
  if (onModelCreated) {
1009
1057
  onModelCreated(model);
1010
1058
  }
@@ -1015,7 +1063,9 @@ const generateModel = (gqlType, onModelCreated) => {
1015
1063
  };
1016
1064
 
1017
1065
  const generateModelWithoutCollection = (gqlType, onModelCreated) => {
1018
- const model = mongoose.model(gqlType.name, generateSchemaDefinition(gqlType), gqlType.name);
1066
+ const schemaDefinition = generateSchemaDefinition(gqlType);
1067
+ const schema = createSchemaWithIndexes(schemaDefinition);
1068
+ const model = mongoose.model(gqlType.name, schema, gqlType.name);
1019
1069
  if (onModelCreated) {
1020
1070
  onModelCreated(model);
1021
1071
  }
@@ -1537,6 +1587,26 @@ we will allow users to use when they are making request. */
1537
1587
  export const createSchema = (includedQueryTypes,
1538
1588
  includedMutationTypes, includedCustomMutations) => {
1539
1589
 
1590
+ // Generate models for all registered types now that all types are available
1591
+ Object.values(typesDict.types).forEach(typeInfo => {
1592
+ if (typeInfo.gqltype && !typeInfo.model) {
1593
+ if (typeInfo.endpoint) {
1594
+ // Generate model with collection for endpoint types (types registered with connect)
1595
+ typeInfo.model = generateModel(typeInfo.gqltype, typeInfo.onModelCreated);
1596
+ } else if (typeInfo.needsModel) {
1597
+ // Generate model without collection for no-endpoint types that need models (addNoEndpointType)
1598
+ typeInfo.model = generateModelWithoutCollection(typeInfo.gqltype, null);
1599
+ }
1600
+ }
1601
+ });
1602
+
1603
+ // Also update the typesDictForUpdate with the generated models
1604
+ Object.keys(typesDict.types).forEach(typeName => {
1605
+ if (typesDictForUpdate.types[typeName]) {
1606
+ typesDictForUpdate.types[typeName].model = typesDict.types[typeName].model;
1607
+ }
1608
+ });
1609
+
1540
1610
  // Auto-generate resolvers for all registered types now that all types are available
1541
1611
  Object.values(typesDict.types).forEach(typeInfo => {
1542
1612
  if (typeInfo.gqltype) {
@@ -1628,13 +1698,14 @@ export const connect = (model, gqltype, simpleEntityEndpointName,
1628
1698
  gqltype,
1629
1699
  };
1630
1700
  typesDict.types[gqltype.name] = {
1631
- model: model || generateModel(gqltype, onModelCreated),
1701
+ model: model, // Will be generated later in createSchema if not provided
1632
1702
  gqltype,
1633
1703
  simpleEntityEndpointName,
1634
1704
  listEntitiesEndpointName,
1635
1705
  endpoint: true,
1636
1706
  controller,
1637
1707
  stateMachine,
1708
+ onModelCreated, // Store the callback for later use
1638
1709
  };
1639
1710
 
1640
1711
  typesDictForUpdate.types[gqltype.name] = { ...typesDict.types[gqltype.name] };
@@ -1661,8 +1732,9 @@ export const addNoEndpointType = (gqltype) => {
1661
1732
  typesDict.types[gqltype.name] = {
1662
1733
  gqltype,
1663
1734
  endpoint: false,
1664
- // Generate model if needed for relationships, but don't create collection
1665
- model: needsModel ? generateModelWithoutCollection(gqltype, null) : null,
1735
+ // Model will be generated later in createSchema if needed
1736
+ model: null,
1737
+ needsModel, // Store whether this type needs a model
1666
1738
  };
1667
1739
 
1668
1740
  typesDictForUpdate.types[gqltype.name] = { ...typesDict.types[gqltype.name] };
@@ -0,0 +1,215 @@
1
+ import {
2
+ describe, test, expect, beforeEach, afterEach, vi,
3
+ } from 'vitest';
4
+ import mongoose from 'mongoose';
5
+ import { GraphQLObjectType, GraphQLString, GraphQLID, GraphQLList } from 'graphql';
6
+ import * as simfinity from '../src/index.js';
7
+
8
+ describe('ObjectId Index Creation', () => {
9
+ let indexSpy;
10
+
11
+ beforeEach(() => {
12
+ // Spy on the index method of mongoose Schema
13
+ indexSpy = vi.spyOn(mongoose.Schema.prototype, 'index').mockImplementation(() => {});
14
+ // Prevent collection creation to avoid database connection issues
15
+ simfinity.preventCreatingCollection(true);
16
+ });
17
+
18
+ afterEach(() => {
19
+ // Restore the original implementation
20
+ indexSpy.mockRestore();
21
+ });
22
+
23
+ test('should create index for direct ObjectId field', () => {
24
+ const TestType = new GraphQLObjectType({
25
+ name: 'TestTypeWithObjectId',
26
+ fields: () => ({
27
+ id: { type: GraphQLID },
28
+ name: { type: GraphQLString },
29
+ userId: { type: GraphQLID },
30
+ }),
31
+ });
32
+
33
+ simfinity.connect(null, TestType, 'testTypeWithObjectId', 'testTypesWithObjectId');
34
+ simfinity.createSchema();
35
+
36
+ // Should create indexes for both id and userId fields
37
+ expect(indexSpy).toHaveBeenCalledWith({ id: 1 });
38
+ expect(indexSpy).toHaveBeenCalledWith({ userId: 1 });
39
+ expect(indexSpy).toHaveBeenCalledTimes(2);
40
+ });
41
+
42
+ test('should create index for embedded ObjectId field', () => {
43
+ const EmbeddedType = new GraphQLObjectType({
44
+ name: 'EmbeddedType',
45
+ fields: () => ({
46
+ embeddedId: { type: GraphQLID },
47
+ embeddedName: { type: GraphQLString },
48
+ }),
49
+ });
50
+
51
+ const TestType = new GraphQLObjectType({
52
+ name: 'TestTypeWithEmbedded',
53
+ fields: () => ({
54
+ id: { type: GraphQLID },
55
+ name: { type: GraphQLString },
56
+ embedded: {
57
+ type: EmbeddedType,
58
+ extensions: {
59
+ relation: { embedded: true },
60
+ },
61
+ },
62
+ }),
63
+ });
64
+
65
+ // Register the embedded type first
66
+ simfinity.addNoEndpointType(EmbeddedType);
67
+ simfinity.connect(null, TestType, 'testTypeWithEmbedded', 'testTypesWithEmbedded');
68
+ simfinity.createSchema();
69
+
70
+ // Should create indexes for id and embedded.embeddedId fields
71
+ expect(indexSpy).toHaveBeenCalledWith({ id: 1 });
72
+ expect(indexSpy).toHaveBeenCalledWith({ 'embedded.embeddedId': 1 });
73
+ expect(indexSpy).toHaveBeenCalledTimes(2);
74
+ });
75
+
76
+ test('should create index for nested embedded ObjectId field', () => {
77
+ const DeepEmbeddedType = new GraphQLObjectType({
78
+ name: 'DeepEmbeddedType',
79
+ fields: () => ({
80
+ deepId: { type: GraphQLID },
81
+ deepName: { type: GraphQLString },
82
+ }),
83
+ });
84
+
85
+ const EmbeddedType = new GraphQLObjectType({
86
+ name: 'EmbeddedTypeWithDeep',
87
+ fields: () => ({
88
+ embeddedId: { type: GraphQLID },
89
+ embeddedName: { type: GraphQLString },
90
+ deep: {
91
+ type: DeepEmbeddedType,
92
+ extensions: {
93
+ relation: { embedded: true },
94
+ },
95
+ },
96
+ }),
97
+ });
98
+
99
+ const TestType = new GraphQLObjectType({
100
+ name: 'TestTypeWithNestedEmbedded',
101
+ fields: () => ({
102
+ id: { type: GraphQLID },
103
+ name: { type: GraphQLString },
104
+ embedded: {
105
+ type: EmbeddedType,
106
+ extensions: {
107
+ relation: { embedded: true },
108
+ },
109
+ },
110
+ }),
111
+ });
112
+
113
+ // Register the embedded types first
114
+ simfinity.addNoEndpointType(DeepEmbeddedType);
115
+ simfinity.addNoEndpointType(EmbeddedType);
116
+ simfinity.connect(null, TestType, 'testTypeWithNestedEmbedded', 'testTypesWithNestedEmbedded');
117
+ simfinity.createSchema();
118
+
119
+ // Should create indexes for all ObjectId fields at all levels
120
+ expect(indexSpy).toHaveBeenCalledWith({ embeddedId: 1 }); // EmbeddedType
121
+ expect(indexSpy).toHaveBeenCalledWith({ 'deep.deepId': 1 }); // DeepEmbeddedType
122
+ expect(indexSpy).toHaveBeenCalledWith({ id: 1 }); // TestType
123
+ expect(indexSpy).toHaveBeenCalledWith({ 'embedded.embeddedId': 1 }); // embedded field in TestType
124
+ expect(indexSpy).toHaveBeenCalledWith({ 'embedded.deep.deepId': 1 }); // nested embedded field in TestType
125
+ expect(indexSpy).toHaveBeenCalledTimes(5);
126
+ });
127
+
128
+ test('should create index for array of embedded objects with ObjectId', () => {
129
+ const EmbeddedType = new GraphQLObjectType({
130
+ name: 'EmbeddedTypeForArray',
131
+ fields: () => ({
132
+ embeddedId: { type: GraphQLID },
133
+ embeddedName: { type: GraphQLString },
134
+ }),
135
+ });
136
+
137
+ const TestType = new GraphQLObjectType({
138
+ name: 'TestTypeWithEmbeddedArray',
139
+ fields: () => ({
140
+ id: { type: GraphQLID },
141
+ name: { type: GraphQLString },
142
+ embeddedArray: {
143
+ type: new GraphQLList(EmbeddedType),
144
+ extensions: {
145
+ relation: { embedded: true },
146
+ },
147
+ },
148
+ }),
149
+ });
150
+
151
+ // Register the embedded type first
152
+ simfinity.addNoEndpointType(EmbeddedType);
153
+ simfinity.connect(null, TestType, 'testTypeWithEmbeddedArray', 'testTypesWithEmbeddedArray');
154
+ simfinity.createSchema();
155
+
156
+ // Should create indexes for id and embeddedArray.embeddedId fields
157
+ expect(indexSpy).toHaveBeenCalledWith({ id: 1 });
158
+ expect(indexSpy).toHaveBeenCalledWith({ 'embeddedArray.embeddedId': 1 });
159
+ expect(indexSpy).toHaveBeenCalledTimes(2);
160
+ });
161
+
162
+ test('should not create index for non-ObjectId fields', () => {
163
+ const TestType = new GraphQLObjectType({
164
+ name: 'TestTypeWithoutObjectId',
165
+ fields: () => ({
166
+ id: { type: GraphQLID },
167
+ name: { type: GraphQLString },
168
+ age: { type: GraphQLString },
169
+ active: { type: GraphQLString },
170
+ }),
171
+ });
172
+
173
+ simfinity.connect(null, TestType, 'testTypeWithoutObjectId', 'testTypesWithoutObjectId');
174
+ simfinity.createSchema();
175
+
176
+ // Should only create index for id field (ObjectId), not for other fields
177
+ expect(indexSpy).toHaveBeenCalledWith({ id: 1 });
178
+ expect(indexSpy).toHaveBeenCalledTimes(1);
179
+ });
180
+
181
+ test('should create index for relationship fields (non-embedded)', () => {
182
+ const DepartmentType = new GraphQLObjectType({
183
+ name: 'DepartmentType',
184
+ fields: () => ({
185
+ id: { type: GraphQLID },
186
+ name: { type: GraphQLString },
187
+ }),
188
+ });
189
+
190
+ const UserType = new GraphQLObjectType({
191
+ name: 'TestTypeWithRelationship',
192
+ fields: () => ({
193
+ id: { type: GraphQLID },
194
+ name: { type: GraphQLString },
195
+ department: {
196
+ type: DepartmentType,
197
+ extensions: {
198
+ relation: { embedded: false },
199
+ },
200
+ },
201
+ }),
202
+ });
203
+
204
+ // Register both types
205
+ simfinity.connect(null, DepartmentType, 'department', 'departments');
206
+ simfinity.connect(null, UserType, 'testTypeWithRelationship', 'testTypesWithRelationship');
207
+ simfinity.createSchema();
208
+
209
+ // Should create indexes for id fields in both types and the relationship field
210
+ expect(indexSpy).toHaveBeenCalledWith({ id: 1 }); // DepartmentType
211
+ expect(indexSpy).toHaveBeenCalledWith({ id: 1 }); // UserType
212
+ expect(indexSpy).toHaveBeenCalledWith({ department: 1 }); // relationship field
213
+ expect(indexSpy).toHaveBeenCalledTimes(3);
214
+ });
215
+ });
@@ -28,6 +28,7 @@ describe('preventCreatingCollection option', () => {
28
28
  });
29
29
 
30
30
  simfinity.connect(null, TestType, 'testTypeDefault', 'testTypesDefault');
31
+ simfinity.createSchema(); // Models are now generated during schema creation
31
32
  expect(createCollectionSpy).toHaveBeenCalledTimes(1);
32
33
  });
33
34
 
@@ -43,6 +44,7 @@ describe('preventCreatingCollection option', () => {
43
44
  });
44
45
 
45
46
  simfinity.connect(null, TestType, 'testTypePrevent', 'testTypesPrevent');
47
+ simfinity.createSchema(); // Models are now generated during schema creation
46
48
  expect(createCollectionSpy).not.toHaveBeenCalled();
47
49
  });
48
50
 
@@ -59,6 +61,7 @@ describe('preventCreatingCollection option', () => {
59
61
  });
60
62
 
61
63
  simfinity.connect(null, TestType, 'testTypeAllow', 'testTypesAllow');
64
+ simfinity.createSchema(); // Models are now generated during schema creation
62
65
  expect(createCollectionSpy).toHaveBeenCalledTimes(1);
63
66
  });
64
67
  });
@@ -117,6 +117,7 @@ describe('Custom Validated Scalar Types', () => {
117
117
 
118
118
  beforeAll(() => {
119
119
  simfinity.connect(null, UserType, 'user', 'users');
120
+ simfinity.createSchema(); // Models are now generated during schema creation
120
121
  UserModel = simfinity.getModel(UserType);
121
122
  });
122
123
 
@@ -150,6 +151,7 @@ describe('Custom Validated Scalar Types', () => {
150
151
  }),
151
152
  });
152
153
  simfinity.connect(null, UserWithUniqueType, 'userWithUnique', 'usersWithUnique');
154
+ simfinity.createSchema(); // Models are now generated during schema creation
153
155
  const UserWithUniqueModel = simfinity.getModel(UserWithUniqueType);
154
156
  const schema = UserWithUniqueModel.schema.obj;
155
157