graphile-postgis 2.5.2 → 2.6.1
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/esm/index.d.ts +1 -0
- package/esm/index.js +2 -0
- package/esm/plugins/connection-filter-operators.d.ts +15 -0
- package/esm/plugins/connection-filter-operators.js +271 -0
- package/esm/preset.d.ts +7 -0
- package/esm/preset.js +14 -1
- package/index.d.ts +1 -0
- package/index.js +4 -1
- package/package.json +18 -12
- package/plugins/connection-filter-operators.d.ts +15 -0
- package/plugins/connection-filter-operators.js +277 -0
- package/preset.d.ts +7 -0
- package/preset.js +14 -1
package/esm/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export { PostgisInflectionPlugin } from './plugins/inflection';
|
|
|
18
18
|
export { PostgisExtensionDetectionPlugin } from './plugins/detect-extension';
|
|
19
19
|
export { PostgisRegisterTypesPlugin } from './plugins/register-types';
|
|
20
20
|
export { PostgisGeometryFieldsPlugin } from './plugins/geometry-fields';
|
|
21
|
+
export { createPostgisOperatorFactory } from './plugins/connection-filter-operators';
|
|
21
22
|
export { GisSubtype, SUBTYPE_STRING_BY_SUBTYPE, GIS_SUBTYPE_NAME, CONCRETE_SUBTYPES } from './constants';
|
|
22
23
|
export { getGISTypeDetails, getGISTypeModifier, getGISTypeName } from './utils';
|
|
23
24
|
export type { GisTypeDetails, GisFieldValue } from './types';
|
package/esm/index.js
CHANGED
|
@@ -20,6 +20,8 @@ export { PostgisInflectionPlugin } from './plugins/inflection';
|
|
|
20
20
|
export { PostgisExtensionDetectionPlugin } from './plugins/detect-extension';
|
|
21
21
|
export { PostgisRegisterTypesPlugin } from './plugins/register-types';
|
|
22
22
|
export { PostgisGeometryFieldsPlugin } from './plugins/geometry-fields';
|
|
23
|
+
// Connection filter operator factory (spatial operators for graphile-connection-filter)
|
|
24
|
+
export { createPostgisOperatorFactory } from './plugins/connection-filter-operators';
|
|
23
25
|
// Constants and utilities
|
|
24
26
|
export { GisSubtype, SUBTYPE_STRING_BY_SUBTYPE, GIS_SUBTYPE_NAME, CONCRETE_SUBTYPES } from './constants';
|
|
25
27
|
export { getGISTypeDetails, getGISTypeModifier, getGISTypeName } from './utils';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import 'graphile-build';
|
|
2
|
+
import 'graphile-connection-filter';
|
|
3
|
+
import type { ConnectionFilterOperatorFactory } from 'graphile-connection-filter';
|
|
4
|
+
/**
|
|
5
|
+
* Creates the PostGIS spatial filter operator factory.
|
|
6
|
+
*
|
|
7
|
+
* This factory dynamically generates operator registrations based on the
|
|
8
|
+
* PostGIS extension info discovered during the build phase. It discovers
|
|
9
|
+
* all geometry/geography GQL type names and creates ST_ function-based
|
|
10
|
+
* and SQL operator-based filter operators for each.
|
|
11
|
+
*
|
|
12
|
+
* Registered via the declarative `connectionFilterOperatorFactories` API
|
|
13
|
+
* in the GraphilePostgisPreset.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPostgisOperatorFactory(): ConnectionFilterOperatorFactory;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import 'graphile-build';
|
|
2
|
+
import 'graphile-connection-filter';
|
|
3
|
+
import sql from 'pg-sql2';
|
|
4
|
+
import { CONCRETE_SUBTYPES } from '../constants';
|
|
5
|
+
const ALLOWED_SQL_OPERATORS = new Set([
|
|
6
|
+
'=',
|
|
7
|
+
'&&',
|
|
8
|
+
'&&&',
|
|
9
|
+
'&<',
|
|
10
|
+
'&<|',
|
|
11
|
+
'&>',
|
|
12
|
+
'|&>',
|
|
13
|
+
'<<',
|
|
14
|
+
'<<|',
|
|
15
|
+
'>>',
|
|
16
|
+
'|>>',
|
|
17
|
+
'~',
|
|
18
|
+
'~=',
|
|
19
|
+
]);
|
|
20
|
+
// PostGIS function-based operators
|
|
21
|
+
const FUNCTION_SPECS = [
|
|
22
|
+
[
|
|
23
|
+
'ST_3DIntersects',
|
|
24
|
+
['geometry'],
|
|
25
|
+
'intersects3D',
|
|
26
|
+
'They share any portion of space in 3D.'
|
|
27
|
+
],
|
|
28
|
+
[
|
|
29
|
+
'ST_Contains',
|
|
30
|
+
['geometry'],
|
|
31
|
+
'contains',
|
|
32
|
+
'No points of the specified geometry lie in the exterior, and at least one point of the interior of the specified geometry lies in the interior.'
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
'ST_ContainsProperly',
|
|
36
|
+
['geometry'],
|
|
37
|
+
'containsProperly',
|
|
38
|
+
'The specified geometry intersects the interior but not the boundary (or exterior).'
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
'ST_CoveredBy',
|
|
42
|
+
['geometry', 'geography'],
|
|
43
|
+
'coveredBy',
|
|
44
|
+
'No point is outside the specified geometry.'
|
|
45
|
+
],
|
|
46
|
+
[
|
|
47
|
+
'ST_Covers',
|
|
48
|
+
['geometry', 'geography'],
|
|
49
|
+
'covers',
|
|
50
|
+
'No point in the specified geometry is outside.'
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
'ST_Crosses',
|
|
54
|
+
['geometry'],
|
|
55
|
+
'crosses',
|
|
56
|
+
'They have some, but not all, interior points in common.'
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
'ST_Disjoint',
|
|
60
|
+
['geometry'],
|
|
61
|
+
'disjoint',
|
|
62
|
+
'They do not share any space together.'
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
'ST_Equals',
|
|
66
|
+
['geometry'],
|
|
67
|
+
'equals',
|
|
68
|
+
'They represent the same geometry. Directionality is ignored.'
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
'ST_Intersects',
|
|
72
|
+
['geometry', 'geography'],
|
|
73
|
+
'intersects',
|
|
74
|
+
'They share any portion of space in 2D.'
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
'ST_OrderingEquals',
|
|
78
|
+
['geometry'],
|
|
79
|
+
'orderingEquals',
|
|
80
|
+
'They represent the same geometry and points are in the same directional order.'
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
'ST_Overlaps',
|
|
84
|
+
['geometry'],
|
|
85
|
+
'overlaps',
|
|
86
|
+
'They share space, are of the same dimension, but are not completely contained by each other.'
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
'ST_Touches',
|
|
90
|
+
['geometry'],
|
|
91
|
+
'touches',
|
|
92
|
+
'They have at least one point in common, but their interiors do not intersect.'
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
'ST_Within',
|
|
96
|
+
['geometry'],
|
|
97
|
+
'within',
|
|
98
|
+
'Completely inside the specified geometry.'
|
|
99
|
+
]
|
|
100
|
+
];
|
|
101
|
+
// SQL operator-based operators
|
|
102
|
+
const OPERATOR_SPECS = [
|
|
103
|
+
[
|
|
104
|
+
'=',
|
|
105
|
+
['geometry', 'geography'],
|
|
106
|
+
'exactlyEquals',
|
|
107
|
+
'Coordinates and coordinate order are the same as specified geometry.'
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
'&&',
|
|
111
|
+
['geometry', 'geography'],
|
|
112
|
+
'bboxIntersects2D',
|
|
113
|
+
"2D bounding box intersects the specified geometry's 2D bounding box."
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
'&&&',
|
|
117
|
+
['geometry'],
|
|
118
|
+
'bboxIntersectsND',
|
|
119
|
+
"n-D bounding box intersects the specified geometry's n-D bounding box."
|
|
120
|
+
],
|
|
121
|
+
[
|
|
122
|
+
'&<',
|
|
123
|
+
['geometry'],
|
|
124
|
+
'bboxOverlapsOrLeftOf',
|
|
125
|
+
"Bounding box overlaps or is to the left of the specified geometry's bounding box."
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
'&<|',
|
|
129
|
+
['geometry'],
|
|
130
|
+
'bboxOverlapsOrBelow',
|
|
131
|
+
"Bounding box overlaps or is below the specified geometry's bounding box."
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
'&>',
|
|
135
|
+
['geometry'],
|
|
136
|
+
'bboxOverlapsOrRightOf',
|
|
137
|
+
"Bounding box overlaps or is to the right of the specified geometry's bounding box."
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
'|&>',
|
|
141
|
+
['geometry'],
|
|
142
|
+
'bboxOverlapsOrAbove',
|
|
143
|
+
"Bounding box overlaps or is above the specified geometry's bounding box."
|
|
144
|
+
],
|
|
145
|
+
[
|
|
146
|
+
'<<',
|
|
147
|
+
['geometry'],
|
|
148
|
+
'bboxLeftOf',
|
|
149
|
+
"Bounding box is strictly to the left of the specified geometry's bounding box."
|
|
150
|
+
],
|
|
151
|
+
[
|
|
152
|
+
'<<|',
|
|
153
|
+
['geometry'],
|
|
154
|
+
'bboxBelow',
|
|
155
|
+
"Bounding box is strictly below the specified geometry's bounding box."
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
'>>',
|
|
159
|
+
['geometry'],
|
|
160
|
+
'bboxRightOf',
|
|
161
|
+
"Bounding box is strictly to the right of the specified geometry's bounding box."
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
'|>>',
|
|
165
|
+
['geometry'],
|
|
166
|
+
'bboxAbove',
|
|
167
|
+
"Bounding box is strictly above the specified geometry's bounding box."
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
'~',
|
|
171
|
+
['geometry'],
|
|
172
|
+
'bboxContains',
|
|
173
|
+
"Bounding box contains the specified geometry's bounding box."
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
'~=',
|
|
177
|
+
['geometry'],
|
|
178
|
+
'bboxEquals',
|
|
179
|
+
"Bounding box is the same as the specified geometry's bounding box."
|
|
180
|
+
]
|
|
181
|
+
];
|
|
182
|
+
/**
|
|
183
|
+
* Creates the PostGIS spatial filter operator factory.
|
|
184
|
+
*
|
|
185
|
+
* This factory dynamically generates operator registrations based on the
|
|
186
|
+
* PostGIS extension info discovered during the build phase. It discovers
|
|
187
|
+
* all geometry/geography GQL type names and creates ST_ function-based
|
|
188
|
+
* and SQL operator-based filter operators for each.
|
|
189
|
+
*
|
|
190
|
+
* Registered via the declarative `connectionFilterOperatorFactories` API
|
|
191
|
+
* in the GraphilePostgisPreset.
|
|
192
|
+
*/
|
|
193
|
+
export function createPostgisOperatorFactory() {
|
|
194
|
+
return (build) => {
|
|
195
|
+
const postgisInfo = build.pgGISExtensionInfo;
|
|
196
|
+
if (!postgisInfo) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
const { inflection } = build;
|
|
200
|
+
const { schemaName, geometryCodec, geographyCodec } = postgisInfo;
|
|
201
|
+
// Collect all GQL type names for geometry and geography
|
|
202
|
+
const gqlTypeNamesByBase = {
|
|
203
|
+
geometry: [],
|
|
204
|
+
geography: []
|
|
205
|
+
};
|
|
206
|
+
const codecPairs = [['geometry', geometryCodec]];
|
|
207
|
+
if (geographyCodec) {
|
|
208
|
+
codecPairs.push(['geography', geographyCodec]);
|
|
209
|
+
}
|
|
210
|
+
for (const [baseKey, codec] of codecPairs) {
|
|
211
|
+
const typeName = codec.name;
|
|
212
|
+
gqlTypeNamesByBase[baseKey].push(inflection.gisInterfaceName(typeName));
|
|
213
|
+
for (const subtype of CONCRETE_SUBTYPES) {
|
|
214
|
+
for (const hasZ of [false, true]) {
|
|
215
|
+
for (const hasM of [false, true]) {
|
|
216
|
+
gqlTypeNamesByBase[baseKey].push(inflection.gisType(typeName, subtype, hasZ, hasM, 0));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const allSpecs = [];
|
|
222
|
+
// Process function-based operators
|
|
223
|
+
for (const [fn, baseTypes, operatorName, description] of FUNCTION_SPECS) {
|
|
224
|
+
for (const baseType of baseTypes) {
|
|
225
|
+
const sqlGisFunction = sql.identifier(schemaName, fn.toLowerCase());
|
|
226
|
+
allSpecs.push({
|
|
227
|
+
typeNames: gqlTypeNamesByBase[baseType],
|
|
228
|
+
operatorName,
|
|
229
|
+
description,
|
|
230
|
+
resolve: (i, v) => sql.fragment `${sqlGisFunction}(${i}, ${v})`
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Process SQL operator-based operators
|
|
235
|
+
for (const [op, baseTypes, operatorName, description] of OPERATOR_SPECS) {
|
|
236
|
+
if (!ALLOWED_SQL_OPERATORS.has(op)) {
|
|
237
|
+
throw new Error(`Unexpected SQL operator: ${op}`);
|
|
238
|
+
}
|
|
239
|
+
for (const baseType of baseTypes) {
|
|
240
|
+
allSpecs.push({
|
|
241
|
+
typeNames: gqlTypeNamesByBase[baseType],
|
|
242
|
+
operatorName,
|
|
243
|
+
description,
|
|
244
|
+
resolve: (i, v) => sql.fragment `${i} ${sql.raw(op)} ${v}`
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Sort by operator name for deterministic schema output
|
|
249
|
+
allSpecs.sort((a, b) => a.operatorName.localeCompare(b.operatorName));
|
|
250
|
+
// Convert to ConnectionFilterOperatorRegistration format.
|
|
251
|
+
// Each InternalSpec may target multiple type names; we expand each
|
|
252
|
+
// into individual registrations keyed by typeName.
|
|
253
|
+
const registrations = [];
|
|
254
|
+
for (const spec of allSpecs) {
|
|
255
|
+
for (const typeName of spec.typeNames) {
|
|
256
|
+
registrations.push({
|
|
257
|
+
typeNames: typeName,
|
|
258
|
+
operatorName: spec.operatorName,
|
|
259
|
+
spec: {
|
|
260
|
+
description: spec.description,
|
|
261
|
+
resolveType: (fieldType) => fieldType,
|
|
262
|
+
resolve(sqlIdentifier, sqlValue, _input, _$where, _details) {
|
|
263
|
+
return spec.resolve(sqlIdentifier, sqlValue);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return registrations;
|
|
270
|
+
};
|
|
271
|
+
}
|
package/esm/preset.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ import type { GraphileConfig } from 'graphile-config';
|
|
|
5
5
|
* A preset that includes all PostGIS plugins for PostGraphile v5.
|
|
6
6
|
* Use this as the recommended way to add PostGIS support.
|
|
7
7
|
*
|
|
8
|
+
* Includes:
|
|
9
|
+
* - Geometry/geography type codecs and GeoJSON scalar
|
|
10
|
+
* - PostGIS extension auto-detection
|
|
11
|
+
* - PostGIS inflection (type names for subtypes, Z/M variants)
|
|
12
|
+
* - Geometry field plugins (coordinates, GeoJSON output)
|
|
13
|
+
* - Connection filter operators (26 spatial operators via declarative factory API)
|
|
14
|
+
*
|
|
8
15
|
* @example
|
|
9
16
|
* ```typescript
|
|
10
17
|
* import { GraphilePostgisPreset } from 'graphile-postgis';
|
package/esm/preset.js
CHANGED
|
@@ -3,12 +3,20 @@ import { PostgisInflectionPlugin } from './plugins/inflection';
|
|
|
3
3
|
import { PostgisExtensionDetectionPlugin } from './plugins/detect-extension';
|
|
4
4
|
import { PostgisRegisterTypesPlugin } from './plugins/register-types';
|
|
5
5
|
import { PostgisGeometryFieldsPlugin } from './plugins/geometry-fields';
|
|
6
|
+
import { createPostgisOperatorFactory } from './plugins/connection-filter-operators';
|
|
6
7
|
/**
|
|
7
8
|
* GraphilePostgisPreset
|
|
8
9
|
*
|
|
9
10
|
* A preset that includes all PostGIS plugins for PostGraphile v5.
|
|
10
11
|
* Use this as the recommended way to add PostGIS support.
|
|
11
12
|
*
|
|
13
|
+
* Includes:
|
|
14
|
+
* - Geometry/geography type codecs and GeoJSON scalar
|
|
15
|
+
* - PostGIS extension auto-detection
|
|
16
|
+
* - PostGIS inflection (type names for subtypes, Z/M variants)
|
|
17
|
+
* - Geometry field plugins (coordinates, GeoJSON output)
|
|
18
|
+
* - Connection filter operators (26 spatial operators via declarative factory API)
|
|
19
|
+
*
|
|
12
20
|
* @example
|
|
13
21
|
* ```typescript
|
|
14
22
|
* import { GraphilePostgisPreset } from 'graphile-postgis';
|
|
@@ -25,6 +33,11 @@ export const GraphilePostgisPreset = {
|
|
|
25
33
|
PostgisExtensionDetectionPlugin,
|
|
26
34
|
PostgisRegisterTypesPlugin,
|
|
27
35
|
PostgisGeometryFieldsPlugin
|
|
28
|
-
]
|
|
36
|
+
],
|
|
37
|
+
schema: {
|
|
38
|
+
connectionFilterOperatorFactories: [
|
|
39
|
+
createPostgisOperatorFactory(),
|
|
40
|
+
],
|
|
41
|
+
},
|
|
29
42
|
};
|
|
30
43
|
export default GraphilePostgisPreset;
|
package/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export { PostgisInflectionPlugin } from './plugins/inflection';
|
|
|
18
18
|
export { PostgisExtensionDetectionPlugin } from './plugins/detect-extension';
|
|
19
19
|
export { PostgisRegisterTypesPlugin } from './plugins/register-types';
|
|
20
20
|
export { PostgisGeometryFieldsPlugin } from './plugins/geometry-fields';
|
|
21
|
+
export { createPostgisOperatorFactory } from './plugins/connection-filter-operators';
|
|
21
22
|
export { GisSubtype, SUBTYPE_STRING_BY_SUBTYPE, GIS_SUBTYPE_NAME, CONCRETE_SUBTYPES } from './constants';
|
|
22
23
|
export { getGISTypeDetails, getGISTypeModifier, getGISTypeName } from './utils';
|
|
23
24
|
export type { GisTypeDetails, GisFieldValue } from './types';
|
package/index.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.getGISTypeName = exports.getGISTypeModifier = exports.getGISTypeDetails = exports.CONCRETE_SUBTYPES = exports.GIS_SUBTYPE_NAME = exports.SUBTYPE_STRING_BY_SUBTYPE = exports.GisSubtype = exports.PostgisGeometryFieldsPlugin = exports.PostgisRegisterTypesPlugin = exports.PostgisExtensionDetectionPlugin = exports.PostgisInflectionPlugin = exports.PostgisCodecPlugin = exports.GraphilePostgisPreset = void 0;
|
|
17
|
+
exports.getGISTypeName = exports.getGISTypeModifier = exports.getGISTypeDetails = exports.CONCRETE_SUBTYPES = exports.GIS_SUBTYPE_NAME = exports.SUBTYPE_STRING_BY_SUBTYPE = exports.GisSubtype = exports.createPostgisOperatorFactory = exports.PostgisGeometryFieldsPlugin = exports.PostgisRegisterTypesPlugin = exports.PostgisExtensionDetectionPlugin = exports.PostgisInflectionPlugin = exports.PostgisCodecPlugin = exports.GraphilePostgisPreset = void 0;
|
|
18
18
|
// Preset (recommended entry point)
|
|
19
19
|
var preset_1 = require("./preset");
|
|
20
20
|
Object.defineProperty(exports, "GraphilePostgisPreset", { enumerable: true, get: function () { return preset_1.GraphilePostgisPreset; } });
|
|
@@ -29,6 +29,9 @@ var register_types_1 = require("./plugins/register-types");
|
|
|
29
29
|
Object.defineProperty(exports, "PostgisRegisterTypesPlugin", { enumerable: true, get: function () { return register_types_1.PostgisRegisterTypesPlugin; } });
|
|
30
30
|
var geometry_fields_1 = require("./plugins/geometry-fields");
|
|
31
31
|
Object.defineProperty(exports, "PostgisGeometryFieldsPlugin", { enumerable: true, get: function () { return geometry_fields_1.PostgisGeometryFieldsPlugin; } });
|
|
32
|
+
// Connection filter operator factory (spatial operators for graphile-connection-filter)
|
|
33
|
+
var connection_filter_operators_1 = require("./plugins/connection-filter-operators");
|
|
34
|
+
Object.defineProperty(exports, "createPostgisOperatorFactory", { enumerable: true, get: function () { return connection_filter_operators_1.createPostgisOperatorFactory; } });
|
|
32
35
|
// Constants and utilities
|
|
33
36
|
var constants_1 = require("./constants");
|
|
34
37
|
Object.defineProperty(exports, "GisSubtype", { enumerable: true, get: function () { return constants_1.GisSubtype; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-postgis",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "PostGIS support for PostGraphile v5",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"homepage": "https://github.com/constructive-io/constructive",
|
|
@@ -41,21 +41,27 @@
|
|
|
41
41
|
"url": "https://github.com/constructive-io/constructive/issues"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@dataplan/pg": "1.0.0-rc.
|
|
45
|
-
"grafast": "1.0.0-rc.
|
|
46
|
-
"graphile-build": "5.0.0-rc.
|
|
47
|
-
"graphile-build-pg": "5.0.0-rc.
|
|
48
|
-
"graphile-config": "1.0.0-rc.
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
44
|
+
"@dataplan/pg": "1.0.0-rc.8",
|
|
45
|
+
"grafast": "1.0.0-rc.9",
|
|
46
|
+
"graphile-build": "5.0.0-rc.6",
|
|
47
|
+
"graphile-build-pg": "5.0.0-rc.8",
|
|
48
|
+
"graphile-config": "1.0.0-rc.6",
|
|
49
|
+
"graphile-connection-filter": "^1.1.1",
|
|
50
|
+
"graphql": "16.13.0",
|
|
51
|
+
"pg-sql2": "5.0.0-rc.5",
|
|
52
|
+
"postgraphile": "5.0.0-rc.10"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"graphile-connection-filter": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
52
58
|
},
|
|
53
59
|
"devDependencies": {
|
|
54
60
|
"@types/geojson": "^7946.0.14",
|
|
55
61
|
"@types/node": "^22.19.11",
|
|
56
|
-
"graphile-test": "^4.5.
|
|
62
|
+
"graphile-test": "^4.5.3",
|
|
57
63
|
"makage": "^0.1.10",
|
|
58
|
-
"pgsql-test": "^4.5.
|
|
64
|
+
"pgsql-test": "^4.5.3"
|
|
59
65
|
},
|
|
60
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "21fd7c2c30663548cf15aa448c1935ab56e5497d"
|
|
61
67
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import 'graphile-build';
|
|
2
|
+
import 'graphile-connection-filter';
|
|
3
|
+
import type { ConnectionFilterOperatorFactory } from 'graphile-connection-filter';
|
|
4
|
+
/**
|
|
5
|
+
* Creates the PostGIS spatial filter operator factory.
|
|
6
|
+
*
|
|
7
|
+
* This factory dynamically generates operator registrations based on the
|
|
8
|
+
* PostGIS extension info discovered during the build phase. It discovers
|
|
9
|
+
* all geometry/geography GQL type names and creates ST_ function-based
|
|
10
|
+
* and SQL operator-based filter operators for each.
|
|
11
|
+
*
|
|
12
|
+
* Registered via the declarative `connectionFilterOperatorFactories` API
|
|
13
|
+
* in the GraphilePostgisPreset.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPostgisOperatorFactory(): ConnectionFilterOperatorFactory;
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createPostgisOperatorFactory = createPostgisOperatorFactory;
|
|
7
|
+
require("graphile-build");
|
|
8
|
+
require("graphile-connection-filter");
|
|
9
|
+
const pg_sql2_1 = __importDefault(require("pg-sql2"));
|
|
10
|
+
const constants_1 = require("../constants");
|
|
11
|
+
const ALLOWED_SQL_OPERATORS = new Set([
|
|
12
|
+
'=',
|
|
13
|
+
'&&',
|
|
14
|
+
'&&&',
|
|
15
|
+
'&<',
|
|
16
|
+
'&<|',
|
|
17
|
+
'&>',
|
|
18
|
+
'|&>',
|
|
19
|
+
'<<',
|
|
20
|
+
'<<|',
|
|
21
|
+
'>>',
|
|
22
|
+
'|>>',
|
|
23
|
+
'~',
|
|
24
|
+
'~=',
|
|
25
|
+
]);
|
|
26
|
+
// PostGIS function-based operators
|
|
27
|
+
const FUNCTION_SPECS = [
|
|
28
|
+
[
|
|
29
|
+
'ST_3DIntersects',
|
|
30
|
+
['geometry'],
|
|
31
|
+
'intersects3D',
|
|
32
|
+
'They share any portion of space in 3D.'
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
'ST_Contains',
|
|
36
|
+
['geometry'],
|
|
37
|
+
'contains',
|
|
38
|
+
'No points of the specified geometry lie in the exterior, and at least one point of the interior of the specified geometry lies in the interior.'
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
'ST_ContainsProperly',
|
|
42
|
+
['geometry'],
|
|
43
|
+
'containsProperly',
|
|
44
|
+
'The specified geometry intersects the interior but not the boundary (or exterior).'
|
|
45
|
+
],
|
|
46
|
+
[
|
|
47
|
+
'ST_CoveredBy',
|
|
48
|
+
['geometry', 'geography'],
|
|
49
|
+
'coveredBy',
|
|
50
|
+
'No point is outside the specified geometry.'
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
'ST_Covers',
|
|
54
|
+
['geometry', 'geography'],
|
|
55
|
+
'covers',
|
|
56
|
+
'No point in the specified geometry is outside.'
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
'ST_Crosses',
|
|
60
|
+
['geometry'],
|
|
61
|
+
'crosses',
|
|
62
|
+
'They have some, but not all, interior points in common.'
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
'ST_Disjoint',
|
|
66
|
+
['geometry'],
|
|
67
|
+
'disjoint',
|
|
68
|
+
'They do not share any space together.'
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
'ST_Equals',
|
|
72
|
+
['geometry'],
|
|
73
|
+
'equals',
|
|
74
|
+
'They represent the same geometry. Directionality is ignored.'
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
'ST_Intersects',
|
|
78
|
+
['geometry', 'geography'],
|
|
79
|
+
'intersects',
|
|
80
|
+
'They share any portion of space in 2D.'
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
'ST_OrderingEquals',
|
|
84
|
+
['geometry'],
|
|
85
|
+
'orderingEquals',
|
|
86
|
+
'They represent the same geometry and points are in the same directional order.'
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
'ST_Overlaps',
|
|
90
|
+
['geometry'],
|
|
91
|
+
'overlaps',
|
|
92
|
+
'They share space, are of the same dimension, but are not completely contained by each other.'
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
'ST_Touches',
|
|
96
|
+
['geometry'],
|
|
97
|
+
'touches',
|
|
98
|
+
'They have at least one point in common, but their interiors do not intersect.'
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
'ST_Within',
|
|
102
|
+
['geometry'],
|
|
103
|
+
'within',
|
|
104
|
+
'Completely inside the specified geometry.'
|
|
105
|
+
]
|
|
106
|
+
];
|
|
107
|
+
// SQL operator-based operators
|
|
108
|
+
const OPERATOR_SPECS = [
|
|
109
|
+
[
|
|
110
|
+
'=',
|
|
111
|
+
['geometry', 'geography'],
|
|
112
|
+
'exactlyEquals',
|
|
113
|
+
'Coordinates and coordinate order are the same as specified geometry.'
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
'&&',
|
|
117
|
+
['geometry', 'geography'],
|
|
118
|
+
'bboxIntersects2D',
|
|
119
|
+
"2D bounding box intersects the specified geometry's 2D bounding box."
|
|
120
|
+
],
|
|
121
|
+
[
|
|
122
|
+
'&&&',
|
|
123
|
+
['geometry'],
|
|
124
|
+
'bboxIntersectsND',
|
|
125
|
+
"n-D bounding box intersects the specified geometry's n-D bounding box."
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
'&<',
|
|
129
|
+
['geometry'],
|
|
130
|
+
'bboxOverlapsOrLeftOf',
|
|
131
|
+
"Bounding box overlaps or is to the left of the specified geometry's bounding box."
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
'&<|',
|
|
135
|
+
['geometry'],
|
|
136
|
+
'bboxOverlapsOrBelow',
|
|
137
|
+
"Bounding box overlaps or is below the specified geometry's bounding box."
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
'&>',
|
|
141
|
+
['geometry'],
|
|
142
|
+
'bboxOverlapsOrRightOf',
|
|
143
|
+
"Bounding box overlaps or is to the right of the specified geometry's bounding box."
|
|
144
|
+
],
|
|
145
|
+
[
|
|
146
|
+
'|&>',
|
|
147
|
+
['geometry'],
|
|
148
|
+
'bboxOverlapsOrAbove',
|
|
149
|
+
"Bounding box overlaps or is above the specified geometry's bounding box."
|
|
150
|
+
],
|
|
151
|
+
[
|
|
152
|
+
'<<',
|
|
153
|
+
['geometry'],
|
|
154
|
+
'bboxLeftOf',
|
|
155
|
+
"Bounding box is strictly to the left of the specified geometry's bounding box."
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
'<<|',
|
|
159
|
+
['geometry'],
|
|
160
|
+
'bboxBelow',
|
|
161
|
+
"Bounding box is strictly below the specified geometry's bounding box."
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
'>>',
|
|
165
|
+
['geometry'],
|
|
166
|
+
'bboxRightOf',
|
|
167
|
+
"Bounding box is strictly to the right of the specified geometry's bounding box."
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
'|>>',
|
|
171
|
+
['geometry'],
|
|
172
|
+
'bboxAbove',
|
|
173
|
+
"Bounding box is strictly above the specified geometry's bounding box."
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
'~',
|
|
177
|
+
['geometry'],
|
|
178
|
+
'bboxContains',
|
|
179
|
+
"Bounding box contains the specified geometry's bounding box."
|
|
180
|
+
],
|
|
181
|
+
[
|
|
182
|
+
'~=',
|
|
183
|
+
['geometry'],
|
|
184
|
+
'bboxEquals',
|
|
185
|
+
"Bounding box is the same as the specified geometry's bounding box."
|
|
186
|
+
]
|
|
187
|
+
];
|
|
188
|
+
/**
|
|
189
|
+
* Creates the PostGIS spatial filter operator factory.
|
|
190
|
+
*
|
|
191
|
+
* This factory dynamically generates operator registrations based on the
|
|
192
|
+
* PostGIS extension info discovered during the build phase. It discovers
|
|
193
|
+
* all geometry/geography GQL type names and creates ST_ function-based
|
|
194
|
+
* and SQL operator-based filter operators for each.
|
|
195
|
+
*
|
|
196
|
+
* Registered via the declarative `connectionFilterOperatorFactories` API
|
|
197
|
+
* in the GraphilePostgisPreset.
|
|
198
|
+
*/
|
|
199
|
+
function createPostgisOperatorFactory() {
|
|
200
|
+
return (build) => {
|
|
201
|
+
const postgisInfo = build.pgGISExtensionInfo;
|
|
202
|
+
if (!postgisInfo) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
const { inflection } = build;
|
|
206
|
+
const { schemaName, geometryCodec, geographyCodec } = postgisInfo;
|
|
207
|
+
// Collect all GQL type names for geometry and geography
|
|
208
|
+
const gqlTypeNamesByBase = {
|
|
209
|
+
geometry: [],
|
|
210
|
+
geography: []
|
|
211
|
+
};
|
|
212
|
+
const codecPairs = [['geometry', geometryCodec]];
|
|
213
|
+
if (geographyCodec) {
|
|
214
|
+
codecPairs.push(['geography', geographyCodec]);
|
|
215
|
+
}
|
|
216
|
+
for (const [baseKey, codec] of codecPairs) {
|
|
217
|
+
const typeName = codec.name;
|
|
218
|
+
gqlTypeNamesByBase[baseKey].push(inflection.gisInterfaceName(typeName));
|
|
219
|
+
for (const subtype of constants_1.CONCRETE_SUBTYPES) {
|
|
220
|
+
for (const hasZ of [false, true]) {
|
|
221
|
+
for (const hasM of [false, true]) {
|
|
222
|
+
gqlTypeNamesByBase[baseKey].push(inflection.gisType(typeName, subtype, hasZ, hasM, 0));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const allSpecs = [];
|
|
228
|
+
// Process function-based operators
|
|
229
|
+
for (const [fn, baseTypes, operatorName, description] of FUNCTION_SPECS) {
|
|
230
|
+
for (const baseType of baseTypes) {
|
|
231
|
+
const sqlGisFunction = pg_sql2_1.default.identifier(schemaName, fn.toLowerCase());
|
|
232
|
+
allSpecs.push({
|
|
233
|
+
typeNames: gqlTypeNamesByBase[baseType],
|
|
234
|
+
operatorName,
|
|
235
|
+
description,
|
|
236
|
+
resolve: (i, v) => pg_sql2_1.default.fragment `${sqlGisFunction}(${i}, ${v})`
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Process SQL operator-based operators
|
|
241
|
+
for (const [op, baseTypes, operatorName, description] of OPERATOR_SPECS) {
|
|
242
|
+
if (!ALLOWED_SQL_OPERATORS.has(op)) {
|
|
243
|
+
throw new Error(`Unexpected SQL operator: ${op}`);
|
|
244
|
+
}
|
|
245
|
+
for (const baseType of baseTypes) {
|
|
246
|
+
allSpecs.push({
|
|
247
|
+
typeNames: gqlTypeNamesByBase[baseType],
|
|
248
|
+
operatorName,
|
|
249
|
+
description,
|
|
250
|
+
resolve: (i, v) => pg_sql2_1.default.fragment `${i} ${pg_sql2_1.default.raw(op)} ${v}`
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Sort by operator name for deterministic schema output
|
|
255
|
+
allSpecs.sort((a, b) => a.operatorName.localeCompare(b.operatorName));
|
|
256
|
+
// Convert to ConnectionFilterOperatorRegistration format.
|
|
257
|
+
// Each InternalSpec may target multiple type names; we expand each
|
|
258
|
+
// into individual registrations keyed by typeName.
|
|
259
|
+
const registrations = [];
|
|
260
|
+
for (const spec of allSpecs) {
|
|
261
|
+
for (const typeName of spec.typeNames) {
|
|
262
|
+
registrations.push({
|
|
263
|
+
typeNames: typeName,
|
|
264
|
+
operatorName: spec.operatorName,
|
|
265
|
+
spec: {
|
|
266
|
+
description: spec.description,
|
|
267
|
+
resolveType: (fieldType) => fieldType,
|
|
268
|
+
resolve(sqlIdentifier, sqlValue, _input, _$where, _details) {
|
|
269
|
+
return spec.resolve(sqlIdentifier, sqlValue);
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return registrations;
|
|
276
|
+
};
|
|
277
|
+
}
|
package/preset.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ import type { GraphileConfig } from 'graphile-config';
|
|
|
5
5
|
* A preset that includes all PostGIS plugins for PostGraphile v5.
|
|
6
6
|
* Use this as the recommended way to add PostGIS support.
|
|
7
7
|
*
|
|
8
|
+
* Includes:
|
|
9
|
+
* - Geometry/geography type codecs and GeoJSON scalar
|
|
10
|
+
* - PostGIS extension auto-detection
|
|
11
|
+
* - PostGIS inflection (type names for subtypes, Z/M variants)
|
|
12
|
+
* - Geometry field plugins (coordinates, GeoJSON output)
|
|
13
|
+
* - Connection filter operators (26 spatial operators via declarative factory API)
|
|
14
|
+
*
|
|
8
15
|
* @example
|
|
9
16
|
* ```typescript
|
|
10
17
|
* import { GraphilePostgisPreset } from 'graphile-postgis';
|
package/preset.js
CHANGED
|
@@ -6,12 +6,20 @@ const inflection_1 = require("./plugins/inflection");
|
|
|
6
6
|
const detect_extension_1 = require("./plugins/detect-extension");
|
|
7
7
|
const register_types_1 = require("./plugins/register-types");
|
|
8
8
|
const geometry_fields_1 = require("./plugins/geometry-fields");
|
|
9
|
+
const connection_filter_operators_1 = require("./plugins/connection-filter-operators");
|
|
9
10
|
/**
|
|
10
11
|
* GraphilePostgisPreset
|
|
11
12
|
*
|
|
12
13
|
* A preset that includes all PostGIS plugins for PostGraphile v5.
|
|
13
14
|
* Use this as the recommended way to add PostGIS support.
|
|
14
15
|
*
|
|
16
|
+
* Includes:
|
|
17
|
+
* - Geometry/geography type codecs and GeoJSON scalar
|
|
18
|
+
* - PostGIS extension auto-detection
|
|
19
|
+
* - PostGIS inflection (type names for subtypes, Z/M variants)
|
|
20
|
+
* - Geometry field plugins (coordinates, GeoJSON output)
|
|
21
|
+
* - Connection filter operators (26 spatial operators via declarative factory API)
|
|
22
|
+
*
|
|
15
23
|
* @example
|
|
16
24
|
* ```typescript
|
|
17
25
|
* import { GraphilePostgisPreset } from 'graphile-postgis';
|
|
@@ -28,6 +36,11 @@ exports.GraphilePostgisPreset = {
|
|
|
28
36
|
detect_extension_1.PostgisExtensionDetectionPlugin,
|
|
29
37
|
register_types_1.PostgisRegisterTypesPlugin,
|
|
30
38
|
geometry_fields_1.PostgisGeometryFieldsPlugin
|
|
31
|
-
]
|
|
39
|
+
],
|
|
40
|
+
schema: {
|
|
41
|
+
connectionFilterOperatorFactories: [
|
|
42
|
+
(0, connection_filter_operators_1.createPostgisOperatorFactory)(),
|
|
43
|
+
],
|
|
44
|
+
},
|
|
32
45
|
};
|
|
33
46
|
exports.default = exports.GraphilePostgisPreset;
|