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.
- package/LICENSE +23 -0
- package/README.md +123 -0
- package/adapters/bm25.d.ts +32 -0
- package/adapters/bm25.js +119 -0
- package/adapters/index.d.ts +14 -0
- package/adapters/index.js +17 -0
- package/adapters/pgvector.d.ts +21 -0
- package/adapters/pgvector.js +125 -0
- package/adapters/trgm.d.ts +20 -0
- package/adapters/trgm.js +83 -0
- package/adapters/tsvector.d.ts +20 -0
- package/adapters/tsvector.js +60 -0
- package/codecs/bm25-codec.d.ts +42 -0
- package/codecs/bm25-codec.js +199 -0
- package/codecs/index.d.ts +12 -0
- package/codecs/index.js +22 -0
- package/codecs/operator-factories.d.ts +22 -0
- package/codecs/operator-factories.js +84 -0
- package/codecs/tsvector-codec.d.ts +53 -0
- package/codecs/tsvector-codec.js +162 -0
- package/codecs/vector-codec.d.ts +18 -0
- package/codecs/vector-codec.js +116 -0
- package/esm/adapters/bm25.d.ts +32 -0
- package/esm/adapters/bm25.js +116 -0
- package/esm/adapters/index.d.ts +14 -0
- package/esm/adapters/index.js +10 -0
- package/esm/adapters/pgvector.d.ts +21 -0
- package/esm/adapters/pgvector.js +122 -0
- package/esm/adapters/trgm.d.ts +20 -0
- package/esm/adapters/trgm.js +80 -0
- package/esm/adapters/tsvector.d.ts +20 -0
- package/esm/adapters/tsvector.js +57 -0
- package/esm/codecs/bm25-codec.d.ts +42 -0
- package/esm/codecs/bm25-codec.js +160 -0
- package/esm/codecs/index.d.ts +12 -0
- package/esm/codecs/index.js +10 -0
- package/esm/codecs/operator-factories.d.ts +22 -0
- package/esm/codecs/operator-factories.js +80 -0
- package/esm/codecs/tsvector-codec.d.ts +53 -0
- package/esm/codecs/tsvector-codec.js +155 -0
- package/esm/codecs/vector-codec.d.ts +18 -0
- package/esm/codecs/vector-codec.js +110 -0
- package/esm/index.d.ts +40 -0
- package/esm/index.js +41 -0
- package/esm/plugin.d.ts +50 -0
- package/esm/plugin.js +553 -0
- package/esm/preset.d.ts +79 -0
- package/esm/preset.js +82 -0
- package/esm/types.d.ts +171 -0
- package/esm/types.js +7 -0
- package/index.d.ts +40 -0
- package/index.js +60 -0
- package/package.json +66 -0
- package/plugin.d.ts +50 -0
- package/plugin.js +556 -0
- package/preset.d.ts +79 -0
- package/preset.js +85 -0
- package/types.d.ts +171 -0
- 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
|
+
}
|