graphile-search 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 (59) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +123 -0
  3. package/adapters/bm25.d.ts +32 -0
  4. package/adapters/bm25.js +119 -0
  5. package/adapters/index.d.ts +14 -0
  6. package/adapters/index.js +17 -0
  7. package/adapters/pgvector.d.ts +21 -0
  8. package/adapters/pgvector.js +125 -0
  9. package/adapters/trgm.d.ts +20 -0
  10. package/adapters/trgm.js +83 -0
  11. package/adapters/tsvector.d.ts +20 -0
  12. package/adapters/tsvector.js +60 -0
  13. package/codecs/bm25-codec.d.ts +42 -0
  14. package/codecs/bm25-codec.js +199 -0
  15. package/codecs/index.d.ts +12 -0
  16. package/codecs/index.js +22 -0
  17. package/codecs/operator-factories.d.ts +22 -0
  18. package/codecs/operator-factories.js +84 -0
  19. package/codecs/tsvector-codec.d.ts +53 -0
  20. package/codecs/tsvector-codec.js +162 -0
  21. package/codecs/vector-codec.d.ts +18 -0
  22. package/codecs/vector-codec.js +116 -0
  23. package/esm/adapters/bm25.d.ts +32 -0
  24. package/esm/adapters/bm25.js +116 -0
  25. package/esm/adapters/index.d.ts +14 -0
  26. package/esm/adapters/index.js +10 -0
  27. package/esm/adapters/pgvector.d.ts +21 -0
  28. package/esm/adapters/pgvector.js +122 -0
  29. package/esm/adapters/trgm.d.ts +20 -0
  30. package/esm/adapters/trgm.js +80 -0
  31. package/esm/adapters/tsvector.d.ts +20 -0
  32. package/esm/adapters/tsvector.js +57 -0
  33. package/esm/codecs/bm25-codec.d.ts +42 -0
  34. package/esm/codecs/bm25-codec.js +160 -0
  35. package/esm/codecs/index.d.ts +12 -0
  36. package/esm/codecs/index.js +10 -0
  37. package/esm/codecs/operator-factories.d.ts +22 -0
  38. package/esm/codecs/operator-factories.js +80 -0
  39. package/esm/codecs/tsvector-codec.d.ts +53 -0
  40. package/esm/codecs/tsvector-codec.js +155 -0
  41. package/esm/codecs/vector-codec.d.ts +18 -0
  42. package/esm/codecs/vector-codec.js +110 -0
  43. package/esm/index.d.ts +40 -0
  44. package/esm/index.js +41 -0
  45. package/esm/plugin.d.ts +50 -0
  46. package/esm/plugin.js +553 -0
  47. package/esm/preset.d.ts +79 -0
  48. package/esm/preset.js +82 -0
  49. package/esm/types.d.ts +171 -0
  50. package/esm/types.js +7 -0
  51. package/index.d.ts +40 -0
  52. package/index.js +60 -0
  53. package/package.json +66 -0
  54. package/plugin.d.ts +50 -0
  55. package/plugin.js +556 -0
  56. package/preset.d.ts +79 -0
  57. package/preset.js +85 -0
  58. package/types.d.ts +171 -0
  59. package/types.js +8 -0
package/preset.js ADDED
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Unified Search Plugin Preset
4
+ *
5
+ * Convenience preset that bundles the unified search plugin with all 4 adapters
6
+ * plus the codec plugins that teach PostGraphile about tsvector, bm25query,
7
+ * and vector types.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { UnifiedSearchPreset } from 'graphile-search';
12
+ *
13
+ * const preset = {
14
+ * extends: [
15
+ * UnifiedSearchPreset(),
16
+ * ],
17
+ * };
18
+ * ```
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.UnifiedSearchPreset = UnifiedSearchPreset;
22
+ const plugin_1 = require("./plugin");
23
+ const tsvector_1 = require("./adapters/tsvector");
24
+ const bm25_1 = require("./adapters/bm25");
25
+ const trgm_1 = require("./adapters/trgm");
26
+ const pgvector_1 = require("./adapters/pgvector");
27
+ const tsvector_codec_1 = require("./codecs/tsvector-codec");
28
+ const bm25_codec_1 = require("./codecs/bm25-codec");
29
+ const vector_codec_1 = require("./codecs/vector-codec");
30
+ const operator_factories_1 = require("./codecs/operator-factories");
31
+ /**
32
+ * Creates a preset that includes the unified search plugin with all enabled adapters.
33
+ */
34
+ function UnifiedSearchPreset(options = {}) {
35
+ const { tsvector = true, bm25 = true, trgm = true, pgvector = true, enableSearchScore = true, enableFullTextSearch = true, searchScoreWeights, fullTextScalarName = 'FullText', tsConfig = 'english', } = options;
36
+ const adapters = [];
37
+ if (tsvector) {
38
+ const opts = typeof tsvector === 'object' ? tsvector : {};
39
+ adapters.push((0, tsvector_1.createTsvectorAdapter)(opts));
40
+ }
41
+ if (bm25) {
42
+ const opts = typeof bm25 === 'object' ? bm25 : {};
43
+ adapters.push((0, bm25_1.createBm25Adapter)(opts));
44
+ }
45
+ if (trgm) {
46
+ const opts = typeof trgm === 'object' ? trgm : {};
47
+ adapters.push((0, trgm_1.createTrgmAdapter)(opts));
48
+ }
49
+ if (pgvector) {
50
+ const opts = typeof pgvector === 'object' ? pgvector : {};
51
+ adapters.push((0, pgvector_1.createPgvectorAdapter)(opts));
52
+ }
53
+ const pluginOptions = {
54
+ adapters,
55
+ enableSearchScore,
56
+ enableFullTextSearch,
57
+ searchScoreWeights,
58
+ };
59
+ // Collect codec plugins based on which adapters are enabled
60
+ const codecPlugins = [];
61
+ if (tsvector)
62
+ codecPlugins.push(tsvector_codec_1.TsvectorCodecPlugin);
63
+ if (bm25)
64
+ codecPlugins.push(bm25_codec_1.Bm25CodecPlugin);
65
+ if (pgvector)
66
+ codecPlugins.push(vector_codec_1.VectorCodecPlugin);
67
+ // Collect operator factories for connection filter integration
68
+ const operatorFactories = [];
69
+ if (tsvector)
70
+ operatorFactories.push((0, operator_factories_1.createMatchesOperatorFactory)(fullTextScalarName, tsConfig));
71
+ if (trgm)
72
+ operatorFactories.push((0, operator_factories_1.createTrgmOperatorFactories)());
73
+ return {
74
+ plugins: [
75
+ ...codecPlugins,
76
+ (0, plugin_1.createUnifiedSearchPlugin)(pluginOptions),
77
+ ],
78
+ ...(operatorFactories.length > 0 ? {
79
+ schema: {
80
+ connectionFilterOperatorFactories: operatorFactories,
81
+ },
82
+ } : {}),
83
+ };
84
+ }
85
+ exports.default = UnifiedSearchPreset;
package/types.d.ts ADDED
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Unified Search Plugin Types
3
+ *
4
+ * Defines the SearchAdapter interface that each algorithm must implement,
5
+ * plus shared configuration types.
6
+ */
7
+ import type { SQL } from 'pg-sql2';
8
+ /**
9
+ * Describes a searchable column discovered by an adapter on a specific table.
10
+ */
11
+ export interface SearchableColumn {
12
+ /** The raw PostgreSQL column name (e.g. 'body', 'tsv', 'embedding') */
13
+ attributeName: string;
14
+ /** Optional extra data the adapter needs during SQL generation (e.g. BM25 index info) */
15
+ adapterData?: unknown;
16
+ }
17
+ /**
18
+ * Score semantics — tells the core plugin how to interpret this adapter's scores.
19
+ */
20
+ export interface ScoreSemantics {
21
+ /**
22
+ * The metric name used in field naming (e.g. 'rank', 'score', 'similarity', 'distance').
23
+ * Used in inflection: `{column}{Algorithm}{Metric}` → e.g. `bodyBm25Score`
24
+ */
25
+ metric: string;
26
+ /**
27
+ * If true, lower values are better (BM25: more negative = more relevant,
28
+ * pgvector: closer = more similar). If false, higher is better (tsvector rank,
29
+ * trgm similarity).
30
+ */
31
+ lowerIsBetter: boolean;
32
+ /**
33
+ * Known range bounds, or null if unbounded.
34
+ * Used for normalization in the composite searchScore.
35
+ * - trgm: [0, 1]
36
+ * - tsvector: [0, 1] (approximately)
37
+ * - BM25: null (unbounded negative)
38
+ * - pgvector: null (0 to infinity)
39
+ */
40
+ range: [number, number] | null;
41
+ }
42
+ /**
43
+ * Result of an adapter's `buildFilterApply` — the SQL fragments needed
44
+ * by the core plugin to wire up filtering, scoring, and ordering.
45
+ */
46
+ export interface FilterApplyResult {
47
+ /** SQL WHERE clause fragment, or null if no WHERE needed (e.g. BM25 without threshold) */
48
+ whereClause: SQL | null;
49
+ /** SQL expression that computes the score/distance/rank for this row */
50
+ scoreExpression: SQL;
51
+ }
52
+ /**
53
+ * The core interface that each search algorithm adapter must implement.
54
+ *
55
+ * An adapter is a plain object — not a Graphile plugin. The core unified
56
+ * plugin iterates over all registered adapters and wires them into the
57
+ * Graphile hook system.
58
+ */
59
+ export interface SearchAdapter {
60
+ /**
61
+ * Unique identifier for this algorithm (e.g. 'tsv', 'bm25', 'trgm', 'vector').
62
+ * Used as the algorithm segment in field names: `{column}{Algorithm}{Metric}`.
63
+ */
64
+ name: string;
65
+ /** Score semantics for this algorithm. */
66
+ scoreSemantics: ScoreSemantics;
67
+ /**
68
+ * The filter prefix used for filter field names on the connection filter input.
69
+ * The field name is: `{filterPrefix}{ColumnName}` (camelCase).
70
+ * E.g. 'bm25' → `bm25Body`, 'trgm' → `trgmName`, 'vector' → `vectorEmbedding`.
71
+ */
72
+ filterPrefix: string;
73
+ /**
74
+ * Whether this adapter supports plain text search queries.
75
+ * If true, the adapter's columns will be included in the automatic
76
+ * `fullTextSearch` composite filter that fans out the same text query
77
+ * to all text-compatible adapters simultaneously.
78
+ *
79
+ * Adapters that require non-text input (e.g. pgvector needs a vector array)
80
+ * should set this to false.
81
+ *
82
+ * @default false
83
+ */
84
+ supportsTextSearch?: boolean;
85
+ /**
86
+ * Build the filter value for a text search query dispatched by fullTextSearch.
87
+ * Only called when supportsTextSearch is true.
88
+ * Converts a plain text string into the adapter-specific filter input format.
89
+ *
90
+ * @param text - The user's search text
91
+ * @returns The filter value to pass to buildFilterApply (e.g. string, { query: string }, { value: string })
92
+ */
93
+ buildTextSearchInput?(text: string): unknown;
94
+ /**
95
+ * Discover which columns on a given codec are searchable by this adapter.
96
+ *
97
+ * Called once per table codec during schema build. Should inspect column
98
+ * types, indexes, or other metadata to determine eligibility.
99
+ *
100
+ * @param codec - The PgCodecWithAttributes for the table
101
+ * @param build - The Graphile build object (for accessing pgRegistry, etc.)
102
+ * @returns Array of searchable columns, or empty array if none found
103
+ */
104
+ detectColumns(codec: any, build: any): SearchableColumn[];
105
+ /**
106
+ * Register any custom GraphQL input types needed by this adapter's filter fields.
107
+ *
108
+ * Called once during the `init` hook. Adapters should call
109
+ * `build.registerInputObjectType(...)` or `build.registerEnumType(...)` here.
110
+ *
111
+ * @param build - The Graphile build object
112
+ */
113
+ registerTypes(build: any): void;
114
+ /**
115
+ * Return the name of the GraphQL input type for filter fields.
116
+ * For simple types like tsvector (which uses a plain String), return 'String'.
117
+ * For complex types, return the name registered in `registerTypes`.
118
+ *
119
+ * @param build - The Graphile build object
120
+ * @returns The GraphQL type name string
121
+ */
122
+ getFilterTypeName(build: any): string;
123
+ /**
124
+ * Build the SQL fragments for filtering and scoring.
125
+ *
126
+ * Called at filter apply time (execution phase) with the user's filter input value.
127
+ * Must return the WHERE clause and score expression, but should NOT call
128
+ * $condition.where() or qb.selectAndReturnIndex() — the core plugin handles that.
129
+ *
130
+ * @param sql - The pg-sql2 module
131
+ * @param alias - SQL alias for the table (e.g. `$condition.alias`)
132
+ * @param column - The searchable column info
133
+ * @param filterValue - The user's filter input value
134
+ * @param build - The Graphile build object
135
+ * @returns The WHERE clause and score expression SQL fragments
136
+ */
137
+ buildFilterApply(sql: any, alias: SQL, column: SearchableColumn, filterValue: any, build: any): FilterApplyResult | null;
138
+ }
139
+ /**
140
+ * Configuration options for the unified search plugin.
141
+ */
142
+ export interface UnifiedSearchOptions {
143
+ /**
144
+ * The search adapters to register. Each adapter implements the SearchAdapter
145
+ * interface for a specific algorithm (tsvector, BM25, pg_trgm, pgvector).
146
+ */
147
+ adapters: SearchAdapter[];
148
+ /**
149
+ * Whether to expose the composite `searchScore` field (normalized 0..1)
150
+ * that combines all active search signals into a single relevance score.
151
+ * @default true
152
+ */
153
+ enableSearchScore?: boolean;
154
+ /**
155
+ * Whether to expose the `fullTextSearch` composite filter field.
156
+ * When enabled, every table with at least one text-compatible adapter gets a
157
+ * `fullTextSearch: String` field on its filter type. Providing a value fans
158
+ * out the same text query to all adapters where `supportsTextSearch: true`,
159
+ * combining their WHERE clauses with OR (match any algorithm).
160
+ * @default true
161
+ */
162
+ enableFullTextSearch?: boolean;
163
+ /**
164
+ * Custom weights for the composite searchScore. Keys are adapter names,
165
+ * values are relative weights (will be normalized to sum to 1.0).
166
+ * If not provided, all active adapters contribute equally.
167
+ *
168
+ * @example { bm25: 0.5, trgm: 0.3, tsv: 0.2 }
169
+ */
170
+ searchScoreWeights?: Record<string, number>;
171
+ }
package/types.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Unified Search Plugin Types
4
+ *
5
+ * Defines the SearchAdapter interface that each algorithm must implement,
6
+ * plus shared configuration types.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });