graphile-search-plugin 3.1.0 → 3.2.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/esm/plugin.d.ts CHANGED
@@ -3,8 +3,10 @@
3
3
  *
4
4
  * Generates search condition fields for tsvector columns. When a search term
5
5
  * is provided via the condition input, this plugin applies a
6
- * `column @@ websearch_to_tsquery('english', $value)` WHERE clause and
7
- * automatically orders results by `ts_rank` (descending) for relevance.
6
+ * `column @@ websearch_to_tsquery('english', $value)` WHERE clause.
7
+ * Results are ordered by `ts_rank` only when explicitly requested via
8
+ * the `FULL_TEXT_RANK_ASC/DESC` orderBy enum values (not automatically),
9
+ * ensuring cursor pagination digests remain stable across pages.
8
10
  *
9
11
  * Additionally provides:
10
12
  * - `matches` filter operator for postgraphile-plugin-connection-filter
package/esm/plugin.js CHANGED
@@ -3,8 +3,10 @@
3
3
  *
4
4
  * Generates search condition fields for tsvector columns. When a search term
5
5
  * is provided via the condition input, this plugin applies a
6
- * `column @@ websearch_to_tsquery('english', $value)` WHERE clause and
7
- * automatically orders results by `ts_rank` (descending) for relevance.
6
+ * `column @@ websearch_to_tsquery('english', $value)` WHERE clause.
7
+ * Results are ordered by `ts_rank` only when explicitly requested via
8
+ * the `FULL_TEXT_RANK_ASC/DESC` orderBy enum values (not automatically),
9
+ * ensuring cursor pagination digests remain stable across pages.
8
10
  *
9
11
  * Additionally provides:
10
12
  * - `matches` filter operator for postgraphile-plugin-connection-filter
@@ -32,14 +34,6 @@ import 'graphile-build';
32
34
  import 'graphile-build-pg';
33
35
  import { TYPES } from '@dataplan/pg';
34
36
  const ftsRankSlots = new WeakMap();
35
- /**
36
- * FinalizationRegistry for defensive cleanup of ftsRankSlots entries.
37
- * WeakMap entries are already eligible for GC when keys are unreachable,
38
- * but this provides explicit cleanup and a hook for debugging leaks.
39
- */
40
- const ftsRankCleanup = new FinalizationRegistry((heldValue) => {
41
- ftsRankSlots.delete(heldValue);
42
- });
43
37
  function isTsvectorCodec(codec) {
44
38
  return (codec?.extensions?.pg?.schemaName === 'pg_catalog' &&
45
39
  codec?.extensions?.pg?.name === 'tsvector');
@@ -119,7 +113,6 @@ export function createPgSearchPlugin(options = {}) {
119
113
  ftsRankSlots.set(alias, {
120
114
  indices: Object.create(null),
121
115
  });
122
- ftsRankCleanup.register($select, alias);
123
116
  }
124
117
  // Return a lambda that reads the rank value from the result
125
118
  // row at a dynamically-determined index. The index is set
@@ -231,20 +224,22 @@ export function createPgSearchPlugin(options = {}) {
231
224
  slot.indices[baseFieldName] = rankIndex;
232
225
  }
233
226
  }
234
- // ORDER BY ts_rank: check if the user provided an
235
- // explicit rank orderBy enum (stored in meta during
236
- // planning). If so, use their direction. Otherwise add
237
- // automatic DESC ordering for relevance.
227
+ // ORDER BY ts_rank: only add when the user explicitly
228
+ // requested rank ordering via the FULL_TEXT_RANK_ASC/DESC
229
+ // enum values. The enum's apply stores direction in meta
230
+ // during planning; if no meta is set, skip the orderBy
231
+ // entirely so cursors remain stable across pages.
238
232
  const metaKey = `fts_order_${baseFieldName}`;
239
233
  const explicitDir = typeof $parent.getMetaRaw === 'function'
240
234
  ? $parent.getMetaRaw(metaKey)
241
235
  : undefined;
242
- const orderDirection = explicitDir ?? 'DESC';
243
- $parent.orderBy({
244
- fragment: sql `ts_rank(${columnExpr}, ${tsquery})`,
245
- codec: TYPES.float4,
246
- direction: orderDirection,
247
- });
236
+ if (explicitDir) {
237
+ $parent.orderBy({
238
+ fragment: sql `ts_rank(${columnExpr}, ${tsquery})`,
239
+ codec: TYPES.float4,
240
+ direction: explicitDir,
241
+ });
242
+ }
248
243
  },
249
244
  }),
250
245
  }, `PgSearchPlugin adding condition field '${fieldName}' for tsvector column '${attributeName}' on '${pgCodec.name}'`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-search-plugin",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Generate search conditions for your tsvector columns (PostGraphile v5)",
5
5
  "author": "Constructive <developers@constructive.io>",
6
6
  "homepage": "https://github.com/constructive-io/constructive",
@@ -42,21 +42,19 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.19.1",
45
- "graphile-test": "^4.1.0",
45
+ "graphile-test": "^4.2.0",
46
46
  "makage": "^0.1.10",
47
- "pgsql-test": "^4.1.0",
47
+ "pgsql-test": "^4.2.0",
48
48
  "postgraphile-plugin-connection-filter": "^3.0.0-rc.1"
49
49
  },
50
- "dependencies": {
51
- "@dataplan/pg": "1.0.0-rc.3",
52
- "graphile-build": "^5.0.0-rc.3",
53
- "graphile-build-pg": "^5.0.0-rc.3",
54
- "graphile-config": "1.0.0-rc.3",
55
- "pg-sql2": "^5.0.0-rc.3"
56
- },
57
50
  "peerDependencies": {
51
+ "@dataplan/pg": "^1.0.0-rc.5",
52
+ "graphile-build": "^5.0.0-rc.4",
53
+ "graphile-build-pg": "^5.0.0-rc.5",
54
+ "graphile-config": "^1.0.0-rc.5",
58
55
  "graphql": "^16.9.0",
59
- "postgraphile": "^5.0.0-rc.4",
56
+ "pg-sql2": "^5.0.0-rc.4",
57
+ "postgraphile": "^5.0.0-rc.7",
60
58
  "postgraphile-plugin-connection-filter": "^3.0.0-rc.1"
61
59
  },
62
60
  "peerDependenciesMeta": {
@@ -64,5 +62,5 @@
64
62
  "optional": true
65
63
  }
66
64
  },
67
- "gitHead": "6dac0247c675de94b41038ac1e5095dc5df0c753"
65
+ "gitHead": "b758178b808ce0bf451e86c0bd7e92079155db7c"
68
66
  }
package/plugin.d.ts CHANGED
@@ -3,8 +3,10 @@
3
3
  *
4
4
  * Generates search condition fields for tsvector columns. When a search term
5
5
  * is provided via the condition input, this plugin applies a
6
- * `column @@ websearch_to_tsquery('english', $value)` WHERE clause and
7
- * automatically orders results by `ts_rank` (descending) for relevance.
6
+ * `column @@ websearch_to_tsquery('english', $value)` WHERE clause.
7
+ * Results are ordered by `ts_rank` only when explicitly requested via
8
+ * the `FULL_TEXT_RANK_ASC/DESC` orderBy enum values (not automatically),
9
+ * ensuring cursor pagination digests remain stable across pages.
8
10
  *
9
11
  * Additionally provides:
10
12
  * - `matches` filter operator for postgraphile-plugin-connection-filter
package/plugin.js CHANGED
@@ -4,8 +4,10 @@
4
4
  *
5
5
  * Generates search condition fields for tsvector columns. When a search term
6
6
  * is provided via the condition input, this plugin applies a
7
- * `column @@ websearch_to_tsquery('english', $value)` WHERE clause and
8
- * automatically orders results by `ts_rank` (descending) for relevance.
7
+ * `column @@ websearch_to_tsquery('english', $value)` WHERE clause.
8
+ * Results are ordered by `ts_rank` only when explicitly requested via
9
+ * the `FULL_TEXT_RANK_ASC/DESC` orderBy enum values (not automatically),
10
+ * ensuring cursor pagination digests remain stable across pages.
9
11
  *
10
12
  * Additionally provides:
11
13
  * - `matches` filter operator for postgraphile-plugin-connection-filter
@@ -36,14 +38,6 @@ require("graphile-build");
36
38
  require("graphile-build-pg");
37
39
  const pg_1 = require("@dataplan/pg");
38
40
  const ftsRankSlots = new WeakMap();
39
- /**
40
- * FinalizationRegistry for defensive cleanup of ftsRankSlots entries.
41
- * WeakMap entries are already eligible for GC when keys are unreachable,
42
- * but this provides explicit cleanup and a hook for debugging leaks.
43
- */
44
- const ftsRankCleanup = new FinalizationRegistry((heldValue) => {
45
- ftsRankSlots.delete(heldValue);
46
- });
47
41
  function isTsvectorCodec(codec) {
48
42
  return (codec?.extensions?.pg?.schemaName === 'pg_catalog' &&
49
43
  codec?.extensions?.pg?.name === 'tsvector');
@@ -123,7 +117,6 @@ function createPgSearchPlugin(options = {}) {
123
117
  ftsRankSlots.set(alias, {
124
118
  indices: Object.create(null),
125
119
  });
126
- ftsRankCleanup.register($select, alias);
127
120
  }
128
121
  // Return a lambda that reads the rank value from the result
129
122
  // row at a dynamically-determined index. The index is set
@@ -235,20 +228,22 @@ function createPgSearchPlugin(options = {}) {
235
228
  slot.indices[baseFieldName] = rankIndex;
236
229
  }
237
230
  }
238
- // ORDER BY ts_rank: check if the user provided an
239
- // explicit rank orderBy enum (stored in meta during
240
- // planning). If so, use their direction. Otherwise add
241
- // automatic DESC ordering for relevance.
231
+ // ORDER BY ts_rank: only add when the user explicitly
232
+ // requested rank ordering via the FULL_TEXT_RANK_ASC/DESC
233
+ // enum values. The enum's apply stores direction in meta
234
+ // during planning; if no meta is set, skip the orderBy
235
+ // entirely so cursors remain stable across pages.
242
236
  const metaKey = `fts_order_${baseFieldName}`;
243
237
  const explicitDir = typeof $parent.getMetaRaw === 'function'
244
238
  ? $parent.getMetaRaw(metaKey)
245
239
  : undefined;
246
- const orderDirection = explicitDir ?? 'DESC';
247
- $parent.orderBy({
248
- fragment: sql `ts_rank(${columnExpr}, ${tsquery})`,
249
- codec: pg_1.TYPES.float4,
250
- direction: orderDirection,
251
- });
240
+ if (explicitDir) {
241
+ $parent.orderBy({
242
+ fragment: sql `ts_rank(${columnExpr}, ${tsquery})`,
243
+ codec: pg_1.TYPES.float4,
244
+ direction: explicitDir,
245
+ });
246
+ }
252
247
  },
253
248
  }),
254
249
  }, `PgSearchPlugin adding condition field '${fieldName}' for tsvector column '${attributeName}' on '${pgCodec.name}'`);