graphile-pg-aggregates 1.1.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.
Files changed (63) hide show
  1. package/AddAggregateTypesPlugin.d.ts +38 -0
  2. package/AddAggregateTypesPlugin.js +268 -0
  3. package/AddConnectionAggregatesPlugin.d.ts +2 -0
  4. package/AddConnectionAggregatesPlugin.js +56 -0
  5. package/AddConnectionGroupedAggregatesPlugin.d.ts +10 -0
  6. package/AddConnectionGroupedAggregatesPlugin.js +118 -0
  7. package/AddGroupByAggregateEnumValuesForAttributesPlugin.d.ts +10 -0
  8. package/AddGroupByAggregateEnumValuesForAttributesPlugin.js +116 -0
  9. package/AddGroupByAggregateEnumsPlugin.d.ts +2 -0
  10. package/AddGroupByAggregateEnumsPlugin.js +51 -0
  11. package/AddHavingAggregateTypesPlugin.d.ts +22 -0
  12. package/AddHavingAggregateTypesPlugin.js +449 -0
  13. package/AggregateSpecsPlugin.d.ts +1 -0
  14. package/AggregateSpecsPlugin.js +206 -0
  15. package/AggregatesSmartTagsPlugin.d.ts +8 -0
  16. package/AggregatesSmartTagsPlugin.js +65 -0
  17. package/EXPORTABLE.d.ts +1 -0
  18. package/EXPORTABLE.js +15 -0
  19. package/FilterRelationalAggregatesPlugin.d.ts +74 -0
  20. package/FilterRelationalAggregatesPlugin.js +443 -0
  21. package/InflectionPlugin.d.ts +67 -0
  22. package/InflectionPlugin.js +71 -0
  23. package/LICENSE +23 -0
  24. package/OrderByAggregatesPlugin.d.ts +31 -0
  25. package/OrderByAggregatesPlugin.js +205 -0
  26. package/README.md +128 -0
  27. package/esm/AddAggregateTypesPlugin.d.ts +38 -0
  28. package/esm/AddAggregateTypesPlugin.js +265 -0
  29. package/esm/AddConnectionAggregatesPlugin.d.ts +2 -0
  30. package/esm/AddConnectionAggregatesPlugin.js +53 -0
  31. package/esm/AddConnectionGroupedAggregatesPlugin.d.ts +10 -0
  32. package/esm/AddConnectionGroupedAggregatesPlugin.js +115 -0
  33. package/esm/AddGroupByAggregateEnumValuesForAttributesPlugin.d.ts +10 -0
  34. package/esm/AddGroupByAggregateEnumValuesForAttributesPlugin.js +113 -0
  35. package/esm/AddGroupByAggregateEnumsPlugin.d.ts +2 -0
  36. package/esm/AddGroupByAggregateEnumsPlugin.js +48 -0
  37. package/esm/AddHavingAggregateTypesPlugin.d.ts +22 -0
  38. package/esm/AddHavingAggregateTypesPlugin.js +446 -0
  39. package/esm/AggregateSpecsPlugin.d.ts +1 -0
  40. package/esm/AggregateSpecsPlugin.js +203 -0
  41. package/esm/AggregatesSmartTagsPlugin.d.ts +8 -0
  42. package/esm/AggregatesSmartTagsPlugin.js +62 -0
  43. package/esm/EXPORTABLE.d.ts +1 -0
  44. package/esm/EXPORTABLE.js +12 -0
  45. package/esm/FilterRelationalAggregatesPlugin.d.ts +74 -0
  46. package/esm/FilterRelationalAggregatesPlugin.js +440 -0
  47. package/esm/InflectionPlugin.d.ts +67 -0
  48. package/esm/InflectionPlugin.js +68 -0
  49. package/esm/OrderByAggregatesPlugin.d.ts +31 -0
  50. package/esm/OrderByAggregatesPlugin.js +202 -0
  51. package/esm/index.d.ts +42 -0
  52. package/esm/index.js +37 -0
  53. package/esm/interfaces.d.ts +145 -0
  54. package/esm/interfaces.js +44 -0
  55. package/esm/utils.d.ts +23 -0
  56. package/esm/utils.js +22 -0
  57. package/index.d.ts +42 -0
  58. package/index.js +40 -0
  59. package/interfaces.d.ts +145 -0
  60. package/interfaces.js +47 -0
  61. package/package.json +61 -0
  62. package/utils.d.ts +23 -0
  63. package/utils.js +25 -0
@@ -0,0 +1,53 @@
1
+ import { EXPORTABLE } from './EXPORTABLE';
2
+ const version = '1.0.0';
3
+ const pgAggregatesCloneSubplanWithoutPaginationSingle = EXPORTABLE(() => function plan($connection) {
4
+ return $connection.cloneSubplanWithoutPagination('aggregate').single();
5
+ }, [], 'pgAggregatesCloneSubplanWithoutPaginationSingle');
6
+ const Plugin = {
7
+ name: 'PgAggregatesAddConnectionAggregatesPlugin',
8
+ description: 'Adds the `aggregates` field to connections.',
9
+ version,
10
+ provides: ['aggregates'],
11
+ schema: {
12
+ hooks: {
13
+ // Hook all connections to add the 'aggregates' field
14
+ GraphQLObjectType_fields(fields, build, context) {
15
+ const { inflection } = build;
16
+ const { fieldWithHooks, scope: { pgCodec, pgTypeResource, isConnectionType, isPgConnectionRelated } } = context;
17
+ const table = pgTypeResource ??
18
+ Object.values(build.input.pgRegistry.pgResources).find((s) => s.codec === pgCodec && !s.parameters);
19
+ // If it's not a table connection, abort
20
+ if (!isConnectionType ||
21
+ !isPgConnectionRelated ||
22
+ !table ||
23
+ table.parameters ||
24
+ !table.codec.attributes) {
25
+ return fields;
26
+ }
27
+ if (!build.behavior.pgResourceMatches(table, `resource:aggregates`)) {
28
+ return fields;
29
+ }
30
+ const AggregateContainerType = build.getTypeByName(inflection.aggregateContainerType({ resource: table }));
31
+ if (!AggregateContainerType ||
32
+ Object.keys(AggregateContainerType.getFields()).length === 0) {
33
+ // No aggregates for this connection, abort
34
+ return fields;
35
+ }
36
+ const fieldName = inflection.aggregatesContainerField({
37
+ resource: table
38
+ });
39
+ return {
40
+ ...fields,
41
+ [fieldName]: fieldWithHooks({ fieldName }, () => {
42
+ return {
43
+ description: `Aggregates across the matching connection (ignoring before/after/first/last/offset)`,
44
+ type: AggregateContainerType,
45
+ plan: pgAggregatesCloneSubplanWithoutPaginationSingle
46
+ };
47
+ })
48
+ };
49
+ }
50
+ }
51
+ }
52
+ };
53
+ export { Plugin as PgAggregatesAddConnectionAggregatesPlugin };
@@ -0,0 +1,10 @@
1
+ declare global {
2
+ namespace GraphileBuild {
3
+ interface BehaviorStrings {
4
+ 'resource:groupedAggregates': true;
5
+ groupedAggregates: true;
6
+ }
7
+ }
8
+ }
9
+ declare const Plugin: GraphileConfig.Plugin;
10
+ export { Plugin as PgAggregatesAddConnectionGroupedAggregatesPlugin };
@@ -0,0 +1,115 @@
1
+ import { EXPORTABLE } from './EXPORTABLE';
2
+ const version = '1.0.0';
3
+ function isValidEnum(build, enumType) {
4
+ try {
5
+ if (!enumType) {
6
+ return false;
7
+ }
8
+ if (!(enumType instanceof build.graphql.GraphQLEnumType)) {
9
+ return false;
10
+ }
11
+ if (Object.keys(enumType.getValues()).length === 0) {
12
+ return false;
13
+ }
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ return true;
19
+ }
20
+ const pgAggregateCloneSubplanWithoutPaginationAsAggregate = EXPORTABLE(() => function plan($connection) {
21
+ return $connection.cloneSubplanWithoutPagination('aggregate');
22
+ }, [], 'pgAggregateCloneSubplanWithoutPaginationAsAggregate');
23
+ const pgAggregatesApplyGroupedAggregate = EXPORTABLE(() => function (_$parent, $pgSelect, input) {
24
+ return input.apply($pgSelect);
25
+ }, [], 'pgAggregatesApplyGroupedAggregate');
26
+ const pgAggregatesApplyConditionsToGroupedAggregates = EXPORTABLE(() => function applyPlan(_$parent, $pgSelect, input) {
27
+ return input.apply($pgSelect, (queryBuilder) => queryBuilder.havingBuilder());
28
+ }, [], 'pgAggregatesApplyConditionsToGroupedAggregates');
29
+ const Plugin = {
30
+ name: 'PgAggregatesAddConnectionGroupedAggregatesPlugin',
31
+ description: 'Adds the groupedAggregates field to connections.',
32
+ version,
33
+ provides: ['aggregates'],
34
+ schema: {
35
+ behaviorRegistry: {
36
+ add: {
37
+ 'resource:groupedAggregates': {
38
+ description: 'Should we enable grouped aggregates on this resource?',
39
+ entities: ['pgResource']
40
+ },
41
+ groupedAggregates: {
42
+ description: 'Should we enable grouped aggregates on this resource?',
43
+ entities: ['pgResource']
44
+ }
45
+ }
46
+ },
47
+ entityBehavior: {
48
+ pgResource: [
49
+ 'resource:groupedAggregates',
50
+ 'resource:groupedAggregates:having'
51
+ ]
52
+ },
53
+ hooks: {
54
+ GraphQLObjectType_fields(fields, build, context) {
55
+ const { graphql: { GraphQLList, GraphQLNonNull }, inflection } = build;
56
+ const { fieldWithHooks, scope: { pgCodec, pgTypeResource, isConnectionType, isPgConnectionRelated } } = context;
57
+ const table = pgTypeResource ??
58
+ Object.values(build.input.pgRegistry.pgResources).find((s) => s.codec === pgCodec && !s.parameters);
59
+ // If it's not a table connection, abort
60
+ if (!isConnectionType ||
61
+ !isPgConnectionRelated ||
62
+ !table ||
63
+ table.parameters ||
64
+ !table.codec.attributes) {
65
+ return fields;
66
+ }
67
+ if (!build.behavior.pgResourceMatches(table, `resource:groupedAggregates`)) {
68
+ return fields;
69
+ }
70
+ const AggregateContainerType = build.getTypeByName(inflection.aggregateContainerType({ resource: table }));
71
+ if (!AggregateContainerType ||
72
+ Object.keys(AggregateContainerType.getFields()).length === 0) {
73
+ // No aggregates for this connection, abort
74
+ return fields;
75
+ }
76
+ const fieldName = inflection.groupedAggregatesContainerField({
77
+ resource: table
78
+ });
79
+ const TableGroupByType = build.getTypeByName(inflection.aggregateGroupByType({ resource: table }));
80
+ const TableHavingInputType = build.getTypeByName(inflection.aggregateHavingInputType({ resource: table }));
81
+ const tableTypeName = inflection.tableType(table.codec);
82
+ if (!TableGroupByType || !isValidEnum(build, TableGroupByType)) {
83
+ return fields;
84
+ }
85
+ return {
86
+ ...fields,
87
+ [fieldName]: fieldWithHooks({ fieldName }, () => {
88
+ return {
89
+ description: `Grouped aggregates across the matching connection (ignoring before/after/first/last/offset)`,
90
+ type: new GraphQLList(new GraphQLNonNull(AggregateContainerType)),
91
+ args: {
92
+ groupBy: {
93
+ type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(TableGroupByType))),
94
+ description: build.wrapDescription(`The method to use when grouping \`${tableTypeName}\` for these aggregates.`, 'arg'),
95
+ applyPlan: pgAggregatesApplyGroupedAggregate
96
+ },
97
+ ...(TableHavingInputType
98
+ ? {
99
+ having: {
100
+ type: TableHavingInputType,
101
+ description: build.wrapDescription(`Conditions on the grouped aggregates.`, 'arg'),
102
+ applyPlan: pgAggregatesApplyConditionsToGroupedAggregates
103
+ }
104
+ }
105
+ : null)
106
+ },
107
+ plan: pgAggregateCloneSubplanWithoutPaginationAsAggregate
108
+ };
109
+ })
110
+ };
111
+ }
112
+ }
113
+ }
114
+ };
115
+ export { Plugin as PgAggregatesAddConnectionGroupedAggregatesPlugin };
@@ -0,0 +1,10 @@
1
+ declare global {
2
+ namespace GraphileBuild {
3
+ interface BehaviorStrings {
4
+ 'attribute:groupBy': true;
5
+ groupBy: true;
6
+ }
7
+ }
8
+ }
9
+ declare const Plugin: GraphileConfig.Plugin;
10
+ export { Plugin as PgAggregatesAddGroupByAggregateEnumValuesForAttributesPlugin };
@@ -0,0 +1,113 @@
1
+ import { EXPORTABLE } from './EXPORTABLE';
2
+ const version = '1.0.0';
3
+ const applyGroupByAggregateSpec = EXPORTABLE(() => (sql, aggregateGroupBySpec, attributeName, attrCodec, qb) => {
4
+ qb.groupBy({
5
+ fragment: aggregateGroupBySpec.sqlWrap(sql `${qb.alias}.${sql.identifier(attributeName)}`),
6
+ codec: aggregateGroupBySpec.sqlWrapCodec(attrCodec)
7
+ });
8
+ }, [], 'applyGroupByAggregateSpec');
9
+ const applyGroupByAttribute = EXPORTABLE(() => (sql, attributeName, attrCodec, qb) => {
10
+ qb.groupBy({
11
+ fragment: sql.fragment `${qb.alias}.${sql.identifier(attributeName)}`,
12
+ codec: attrCodec
13
+ });
14
+ }, [], 'applyGroupByAttribute');
15
+ const Plugin = {
16
+ name: 'PgAggregatesAddGroupByAggregateEnumValuesForAttributesPlugin',
17
+ description: 'Adds values representing table attributes to the enum that defines the groupedAggregates groupings.',
18
+ version,
19
+ provides: ['aggregates'],
20
+ // Now add group by attributes
21
+ schema: {
22
+ behaviorRegistry: {
23
+ add: {
24
+ 'attribute:groupBy': {
25
+ description: 'Can we group by this attribute when aggregating?',
26
+ entities: ['pgCodecAttribute']
27
+ },
28
+ groupBy: {
29
+ description: 'Can we group by this attribute when aggregating?',
30
+ entities: ['pgCodecAttribute']
31
+ }
32
+ }
33
+ },
34
+ entityBehavior: {
35
+ pgCodecAttribute: ['attribute:groupBy']
36
+ },
37
+ hooks: {
38
+ GraphQLEnumType_values(values, build, context) {
39
+ const { extend, inflection, sql, pgAggregateGroupBySpecs, EXPORTABLE } = build;
40
+ const { scope: { isPgAggregateGroupEnum, pgTypeResource: table } } = context;
41
+ if (!isPgAggregateGroupEnum ||
42
+ !table ||
43
+ table.parameters ||
44
+ !table.codec.attributes) {
45
+ return values;
46
+ }
47
+ return extend(values, Object.entries(table.codec.attributes).reduce((memo, [attributeName, attribute]) => {
48
+ // Grouping requires ordering.
49
+ if (!build.behavior.pgCodecAttributeMatches([table.codec, attributeName], 'orderBy')) {
50
+ return memo;
51
+ }
52
+ if (!build.behavior.pgCodecAttributeMatches([table.codec, attributeName], `attribute:groupBy`)) {
53
+ return memo;
54
+ }
55
+ const unique = !!table.uniques.find((u) => u.attributes.length === 1 && u.attributes[0] === attributeName);
56
+ if (unique)
57
+ return memo; // No point grouping by something that's unique.
58
+ const fieldName = inflection.aggregateGroupByAttributeEnum({
59
+ resource: table,
60
+ attributeName
61
+ });
62
+ const attrCodec = attribute.codec;
63
+ memo = extend(memo, {
64
+ [fieldName]: {
65
+ extensions: {
66
+ grafast: {
67
+ apply: EXPORTABLE((applyGroupByAttribute, attrCodec, attributeName, sql) => function ($pgSelect) {
68
+ applyGroupByAttribute(sql, attributeName, attrCodec, $pgSelect);
69
+ }, [applyGroupByAttribute, attrCodec, attributeName, sql])
70
+ }
71
+ }
72
+ }
73
+ }, `Adding groupBy enum value for ${table.name}.${attributeName}.`);
74
+ // Derivatives of this attribute
75
+ pgAggregateGroupBySpecs.forEach((aggregateGroupBySpec) => {
76
+ if ((!aggregateGroupBySpec.shouldApplyToEntity ||
77
+ aggregateGroupBySpec.shouldApplyToEntity({
78
+ type: 'attribute',
79
+ codec: table.codec,
80
+ attributeName
81
+ })) &&
82
+ aggregateGroupBySpec.isSuitableType(attribute.codec)) {
83
+ const fieldName = inflection.aggregateGroupByAttributeDerivativeEnum({
84
+ resource: table,
85
+ attributeName,
86
+ aggregateGroupBySpec
87
+ });
88
+ memo = extend(memo, {
89
+ [fieldName]: {
90
+ extensions: {
91
+ grafast: {
92
+ apply: EXPORTABLE((aggregateGroupBySpec, applyGroupByAggregateSpec, attrCodec, attributeName, sql) => function (qb) {
93
+ applyGroupByAggregateSpec(sql, aggregateGroupBySpec, attributeName, attrCodec, qb);
94
+ }, [
95
+ aggregateGroupBySpec,
96
+ applyGroupByAggregateSpec,
97
+ attrCodec,
98
+ attributeName,
99
+ sql
100
+ ])
101
+ }
102
+ }
103
+ }
104
+ }, `Adding groupBy enum value for '${aggregateGroupBySpec.id}' derivative of ${table.name}.${attributeName}.`);
105
+ }
106
+ });
107
+ return memo;
108
+ }, Object.create(null)), `Adding group by values for attributes from table '${table.name}'`);
109
+ }
110
+ }
111
+ }
112
+ };
113
+ export { Plugin as PgAggregatesAddGroupByAggregateEnumValuesForAttributesPlugin };
@@ -0,0 +1,2 @@
1
+ declare const Plugin: GraphileConfig.Plugin;
2
+ export { Plugin as PgAggregatesAddGroupByAggregateEnumsPlugin };
@@ -0,0 +1,48 @@
1
+ const version = '1.0.0';
2
+ const Plugin = {
3
+ name: 'PgAggregatesAddGroupByAggregateEnumsPlugin',
4
+ description: 'Creates the enum types used for grouping in groupedAggregates.',
5
+ version,
6
+ provides: ['aggregates'],
7
+ schema: {
8
+ entityBehavior: {
9
+ pgResource: ['select', 'order', 'groupedAggregates']
10
+ },
11
+ hooks: {
12
+ // Create the group by enums for each table
13
+ init(_, build) {
14
+ const { inflection } = build;
15
+ for (const resource of Object.values(build.input.pgRegistry.pgResources)) {
16
+ if (resource.parameters ||
17
+ !resource.codec.attributes ||
18
+ resource.isUnique) {
19
+ continue;
20
+ }
21
+ if (!build.behavior.pgResourceMatches(resource, 'select')) {
22
+ continue;
23
+ }
24
+ if (!build.behavior.pgResourceMatches(resource, 'order')) {
25
+ continue;
26
+ }
27
+ if (!build.behavior.pgResourceMatches(resource, 'resource:groupedAggregates')) {
28
+ continue;
29
+ }
30
+ const tableTypeName = inflection.tableType(resource.codec);
31
+ /* const TableGroupByType = */
32
+ build.registerEnumType(inflection.aggregateGroupByType({ resource }), {
33
+ pgTypeResource: resource,
34
+ isPgAggregateGroupEnum: true
35
+ }, () => ({
36
+ name: inflection.aggregateGroupByType({ resource }),
37
+ description: build.wrapDescription(`Grouping methods for \`${tableTypeName}\` for usage during aggregation.`, 'type'),
38
+ values: {
39
+ /* no default values, these will be added via hooks */
40
+ }
41
+ }), `Adding connection "groupBy" enum type for ${resource.name}.`);
42
+ }
43
+ return _;
44
+ }
45
+ }
46
+ }
47
+ };
48
+ export { Plugin as PgAggregatesAddGroupByAggregateEnumsPlugin };
@@ -0,0 +1,22 @@
1
+ declare global {
2
+ namespace GraphileBuild {
3
+ interface BehaviorStrings {
4
+ 'resource:groupedAggregates:having': true;
5
+ having: true;
6
+ 'resource:havingBy': true;
7
+ 'attribute:havingBy': true;
8
+ havingBy: true;
9
+ 'sum:resource:groupedAggregates:having': true;
10
+ 'distinctCount:resource:groupedAggregates:having': true;
11
+ 'min:resource:groupedAggregates:having': true;
12
+ 'max:resource:groupedAggregates:having': true;
13
+ 'average:resource:groupedAggregates:having': true;
14
+ 'stddevSample:resource:groupedAggregates:having': true;
15
+ 'stddevPopulation:resource:groupedAggregates:having': true;
16
+ 'varianceSample:resource:groupedAggregates:having': true;
17
+ 'variancePopulation:resource:groupedAggregates:having': true;
18
+ }
19
+ }
20
+ }
21
+ declare const Plugin: GraphileConfig.Plugin;
22
+ export { Plugin as PgAggregatesAddHavingAggregateTypesPlugin };