@simtlix/simfinity-js 1.9.1 → 2.0.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 +116 -0
- package/package.json +1 -1
- package/simtlix-simfinity-js-1.9.1.tgz +0 -0
- package/src/index.js +74 -36
package/README.md
CHANGED
|
@@ -204,6 +204,122 @@ query {
|
|
|
204
204
|
- `NIN` - Not in array
|
|
205
205
|
- `BTW` - Between two values
|
|
206
206
|
|
|
207
|
+
### Collection Field Filtering
|
|
208
|
+
|
|
209
|
+
Simfinity.js now supports filtering collection fields (one-to-many relationships) using the same powerful query format. This allows you to filter related objects directly within your GraphQL queries.
|
|
210
|
+
|
|
211
|
+
#### Basic Collection Filtering
|
|
212
|
+
|
|
213
|
+
Filter collection fields using the same operators and format as main queries:
|
|
214
|
+
|
|
215
|
+
```graphql
|
|
216
|
+
query {
|
|
217
|
+
series {
|
|
218
|
+
seasons(number: { operator: EQ, value: 1 }) {
|
|
219
|
+
number
|
|
220
|
+
id
|
|
221
|
+
year
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### Advanced Collection Filtering
|
|
228
|
+
|
|
229
|
+
You can use complex filtering with nested object properties:
|
|
230
|
+
|
|
231
|
+
```graphql
|
|
232
|
+
query {
|
|
233
|
+
series {
|
|
234
|
+
seasons(
|
|
235
|
+
year: { operator: GTE, value: 2020 }
|
|
236
|
+
episodes: {
|
|
237
|
+
terms: [
|
|
238
|
+
{
|
|
239
|
+
path: "name",
|
|
240
|
+
operator: LIKE,
|
|
241
|
+
value: "Pilot"
|
|
242
|
+
}
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
) {
|
|
246
|
+
number
|
|
247
|
+
year
|
|
248
|
+
episodes {
|
|
249
|
+
name
|
|
250
|
+
date
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
#### Collection Filtering with Multiple Conditions
|
|
258
|
+
|
|
259
|
+
Combine multiple filter conditions for collection fields:
|
|
260
|
+
|
|
261
|
+
```graphql
|
|
262
|
+
query {
|
|
263
|
+
series {
|
|
264
|
+
seasons(
|
|
265
|
+
number: { operator: GT, value: 1 }
|
|
266
|
+
year: { operator: BTW, value: [2015, 2023] }
|
|
267
|
+
) {
|
|
268
|
+
number
|
|
269
|
+
year
|
|
270
|
+
state
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### Nested Collection Filtering
|
|
277
|
+
|
|
278
|
+
Filter deeply nested collections using dot notation:
|
|
279
|
+
|
|
280
|
+
```graphql
|
|
281
|
+
query {
|
|
282
|
+
series {
|
|
283
|
+
seasons(
|
|
284
|
+
episodes: {
|
|
285
|
+
terms: [
|
|
286
|
+
{
|
|
287
|
+
path: "name",
|
|
288
|
+
operator: LIKE,
|
|
289
|
+
value: "Final"
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
) {
|
|
294
|
+
number
|
|
295
|
+
episodes {
|
|
296
|
+
name
|
|
297
|
+
date
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### Collection Filtering with Array Operations
|
|
305
|
+
|
|
306
|
+
Use array operations for collection fields:
|
|
307
|
+
|
|
308
|
+
```graphql
|
|
309
|
+
query {
|
|
310
|
+
series {
|
|
311
|
+
seasons(
|
|
312
|
+
categories: { operator: IN, value: ["Drama", "Crime"] }
|
|
313
|
+
) {
|
|
314
|
+
number
|
|
315
|
+
categories
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Note**: Collection field filtering uses the exact same format as main query filtering, ensuring consistency across your GraphQL API. All available operators (`EQ`, `NE`, `GT`, `LT`, `GTE`, `LTE`, `LIKE`, `IN`, `NIN`, `BTW`) work with collection fields.
|
|
322
|
+
|
|
207
323
|
## 🔧 Middlewares
|
|
208
324
|
|
|
209
325
|
Middlewares provide a powerful way to intercept and process all GraphQL operations before they execute. Use them for cross-cutting concerns like authentication, logging, validation, and performance monitoring.
|
package/package.json
CHANGED
|
Binary file
|
package/src/index.js
CHANGED
|
@@ -1516,37 +1516,7 @@ const buildRootQuery = (name, includedTypes) => {
|
|
|
1516
1516
|
|
|
1517
1517
|
const argTypes = type.gqltype.getFields();
|
|
1518
1518
|
|
|
1519
|
-
const argsObject =
|
|
1520
|
-
|
|
1521
|
-
for (const [fieldEntryName, fieldEntry] of Object.entries(argTypes)) {
|
|
1522
|
-
argsObject[fieldEntryName] = {};
|
|
1523
|
-
|
|
1524
|
-
if (fieldEntry.type instanceof GraphQLScalarType
|
|
1525
|
-
|| isNonNullOfType(fieldEntry.type, GraphQLScalarType)
|
|
1526
|
-
|| fieldEntry.type instanceof GraphQLEnumType
|
|
1527
|
-
|| isNonNullOfType(fieldEntry.type, GraphQLEnumType)) {
|
|
1528
|
-
argsObject[fieldEntryName].type = QLFilter;
|
|
1529
|
-
} else if (fieldEntry.type instanceof GraphQLObjectType
|
|
1530
|
-
|| isNonNullOfType(fieldEntry.type, GraphQLObjectType)) {
|
|
1531
|
-
argsObject[fieldEntryName].type = QLTypeFilterExpression;
|
|
1532
|
-
} else if (fieldEntry.type instanceof GraphQLList) {
|
|
1533
|
-
const listOfType = fieldEntry.type.ofType;
|
|
1534
|
-
if (listOfType instanceof GraphQLScalarType
|
|
1535
|
-
|| isNonNullOfType(listOfType, GraphQLScalarType)
|
|
1536
|
-
|| listOfType instanceof GraphQLEnumType
|
|
1537
|
-
|| isNonNullOfType(listOfType, GraphQLEnumType)) {
|
|
1538
|
-
argsObject[fieldEntryName].type = QLFilter;
|
|
1539
|
-
} else {
|
|
1540
|
-
argsObject[fieldEntryName].type = QLTypeFilterExpression;
|
|
1541
|
-
}
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
argsObject.pagination = {};
|
|
1546
|
-
argsObject.pagination.type = QLPagination;
|
|
1547
|
-
|
|
1548
|
-
argsObject.sort = {};
|
|
1549
|
-
argsObject.sort.type = QLSortExpression;
|
|
1519
|
+
const argsObject = createArgsForQuery(argTypes);
|
|
1550
1520
|
|
|
1551
1521
|
rootQueryArgs.fields[type.listEntitiesEndpointName] = {
|
|
1552
1522
|
type: new GraphQLList(type.gqltype),
|
|
@@ -1656,18 +1626,39 @@ const autoGenerateResolvers = (gqltype) => {
|
|
|
1656
1626
|
if (!relation.embedded) {
|
|
1657
1627
|
if (fieldEntry.type instanceof GraphQLList) {
|
|
1658
1628
|
// Collection field - generate resolve for one-to-many relationship
|
|
1629
|
+
//This is a one-to-many resolver that will return a list of related objects. Also this one allows to filter the related objects as is in the find endpoint.
|
|
1659
1630
|
const relatedType = fieldEntry.type.ofType;
|
|
1660
1631
|
const connectionField = relation.connectionField || fieldName;
|
|
1632
|
+
const relatedTypeInfo = typesDict.types[relatedType.name];
|
|
1633
|
+
const argsObject = createArgsForQuery(relatedTypeInfo.gqltype.getFields());
|
|
1634
|
+
|
|
1635
|
+
delete argsObject[connectionField];
|
|
1636
|
+
const argsArray = Object.entries(argsObject);
|
|
1637
|
+
|
|
1661
1638
|
|
|
1662
|
-
|
|
1639
|
+
const graphqlArgs = formatArgs(argsArray);
|
|
1640
|
+
|
|
1641
|
+
fieldEntry.args = graphqlArgs;
|
|
1642
|
+
|
|
1643
|
+
fieldEntry.resolve = async (parent, args) => {
|
|
1663
1644
|
// Lazy lookup of the related model
|
|
1664
|
-
|
|
1645
|
+
|
|
1665
1646
|
if (!relatedTypeInfo || !relatedTypeInfo.model) {
|
|
1666
1647
|
throw new Error(`Related type ${relatedType.name} not found or not connected. Make sure it's connected with simfinity.connect() or simfinity.addNoEndpointType().`);
|
|
1667
1648
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1649
|
+
|
|
1650
|
+
args[connectionField] = {
|
|
1651
|
+
terms: [{
|
|
1652
|
+
path: 'id',
|
|
1653
|
+
operator: 'EQ',
|
|
1654
|
+
value: parent.id || parent._id,
|
|
1655
|
+
}],
|
|
1656
|
+
};
|
|
1657
|
+
|
|
1658
|
+
|
|
1659
|
+
const aggregateClauses = await buildQuery(args, relatedTypeInfo.gqltype);
|
|
1660
|
+
|
|
1661
|
+
return await relatedTypeInfo.model.aggregate(aggregateClauses);
|
|
1671
1662
|
};
|
|
1672
1663
|
} else if (fieldEntry.type instanceof GraphQLObjectType
|
|
1673
1664
|
|| (fieldEntry.type instanceof GraphQLNonNull && fieldEntry.type.ofType instanceof GraphQLObjectType)) {
|
|
@@ -1741,3 +1732,50 @@ export const addNoEndpointType = (gqltype) => {
|
|
|
1741
1732
|
};
|
|
1742
1733
|
|
|
1743
1734
|
export { createValidatedScalar };
|
|
1735
|
+
|
|
1736
|
+
const createArgsForQuery = (argTypes) => {
|
|
1737
|
+
const argsObject = {};
|
|
1738
|
+
|
|
1739
|
+
for (const [fieldEntryName, fieldEntry] of Object.entries(argTypes)) {
|
|
1740
|
+
argsObject[fieldEntryName] = {};
|
|
1741
|
+
|
|
1742
|
+
if (fieldEntry.type instanceof GraphQLScalarType
|
|
1743
|
+
|| isNonNullOfType(fieldEntry.type, GraphQLScalarType)
|
|
1744
|
+
|| fieldEntry.type instanceof GraphQLEnumType
|
|
1745
|
+
|| isNonNullOfType(fieldEntry.type, GraphQLEnumType)) {
|
|
1746
|
+
argsObject[fieldEntryName].type = QLFilter;
|
|
1747
|
+
} else if (fieldEntry.type instanceof GraphQLObjectType
|
|
1748
|
+
|| isNonNullOfType(fieldEntry.type, GraphQLObjectType)) {
|
|
1749
|
+
argsObject[fieldEntryName].type = QLTypeFilterExpression;
|
|
1750
|
+
} else if (fieldEntry.type instanceof GraphQLList) {
|
|
1751
|
+
const listOfType = fieldEntry.type.ofType;
|
|
1752
|
+
if (listOfType instanceof GraphQLScalarType
|
|
1753
|
+
|| isNonNullOfType(listOfType, GraphQLScalarType)
|
|
1754
|
+
|| listOfType instanceof GraphQLEnumType
|
|
1755
|
+
|| isNonNullOfType(listOfType, GraphQLEnumType)) {
|
|
1756
|
+
argsObject[fieldEntryName].type = QLFilter;
|
|
1757
|
+
} else {
|
|
1758
|
+
argsObject[fieldEntryName].type = QLTypeFilterExpression;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
argsObject.pagination = {};
|
|
1764
|
+
argsObject.pagination.type = QLPagination;
|
|
1765
|
+
|
|
1766
|
+
argsObject.sort = {};
|
|
1767
|
+
argsObject.sort.type = QLSortExpression;
|
|
1768
|
+
return argsObject;
|
|
1769
|
+
};
|
|
1770
|
+
|
|
1771
|
+
function formatArgs(argsArray) {
|
|
1772
|
+
const graphqlArgs = [];
|
|
1773
|
+
for (const [key, value] of argsArray) {
|
|
1774
|
+
const item = {
|
|
1775
|
+
name: key,
|
|
1776
|
+
type: value.type,
|
|
1777
|
+
};
|
|
1778
|
+
graphqlArgs.push(item);
|
|
1779
|
+
}
|
|
1780
|
+
return graphqlArgs;
|
|
1781
|
+
}
|