@framers/sql-storage-adapter 0.7.4 → 0.7.5
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/README.md +24 -13
- package/dist/cjs/core/contracts/features.js +4 -2
- package/dist/cjs/core/contracts/fts.d.ts +20 -8
- package/dist/cjs/core/portable.d.ts +1 -0
- package/dist/cjs/core/portable.js +3 -0
- package/dist/cjs/fts/PostgresFts.d.ts +14 -13
- package/dist/cjs/fts/PostgresFts.js +61 -33
- package/dist/cjs/fts/SqliteFts4.d.ts +28 -0
- package/dist/cjs/fts/SqliteFts4.js +67 -0
- package/dist/cjs/fts/SqliteFts5.d.ts +1 -0
- package/dist/cjs/fts/SqliteFts5.js +3 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +3 -0
- package/dist/core/contracts/features.d.ts.map +1 -1
- package/dist/core/contracts/features.js +6 -2
- package/dist/core/contracts/features.js.map +1 -1
- package/dist/core/contracts/fts.d.ts +20 -8
- package/dist/core/contracts/fts.d.ts.map +1 -1
- package/dist/core/portable.d.ts +1 -0
- package/dist/core/portable.d.ts.map +1 -1
- package/dist/core/portable.js +1 -0
- package/dist/core/portable.js.map +1 -1
- package/dist/fts/PostgresFts.d.ts +14 -13
- package/dist/fts/PostgresFts.d.ts.map +1 -1
- package/dist/fts/PostgresFts.js +83 -39
- package/dist/fts/PostgresFts.js.map +1 -1
- package/dist/fts/SqliteFts4.d.ts +28 -0
- package/dist/fts/SqliteFts4.d.ts.map +1 -0
- package/dist/fts/SqliteFts4.js +56 -0
- package/dist/fts/SqliteFts4.js.map +1 -0
- package/dist/fts/SqliteFts5.d.ts +1 -0
- package/dist/fts/SqliteFts5.d.ts.map +1 -1
- package/dist/fts/SqliteFts5.js +3 -0
- package/dist/fts/SqliteFts5.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -730,30 +730,41 @@ const ddl = `CREATE TABLE notes (id ${d.columnType('uuid')} PRIMARY KEY DEFAULT
|
|
|
730
730
|
- Column values follow each engine: a PostgreSQL `BOOLEAN` returns `true` and the SQLite `INTEGER` returns `1`, and a `TIMESTAMPTZ` returns a `Date` where SQLite returns text. `typeParsers` (see "Values") narrows the PostgreSQL side.
|
|
731
731
|
- `pragma()` returns null on PostgreSQL and `advisoryTransactionLock()` returns null on SQLite: skip the statement when it is null.
|
|
732
732
|
|
|
733
|
-
### IFullTextSearch — FTS5 & tsvector
|
|
733
|
+
### IFullTextSearch — FTS5, FTS4 & tsvector
|
|
734
|
+
|
|
735
|
+
`createStorageFeatures` gives each engine the full-text search it has:
|
|
736
|
+
- FTS5 on better-sqlite3;
|
|
737
|
+
- FTS4 on sql.js and IndexedDB, whose stock sql.js build compiles FTS3 and FTS4 and no FTS5;
|
|
738
|
+
- a tsvector column with a GIN index on PostgreSQL, the Supabase adapter included.
|
|
739
|
+
|
|
740
|
+
The same calls return the same rows on all three, and `tests/fts.parity.spec.ts` runs them on each.
|
|
734
741
|
|
|
735
742
|
```typescript
|
|
736
743
|
const { fts } = features;
|
|
737
744
|
|
|
738
|
-
//
|
|
739
|
-
await
|
|
745
|
+
// FTS5 or FTS4 virtual table (SQLite), or a _tsv column with a GIN index (PostgreSQL)
|
|
746
|
+
await db.exec(fts.createIndex({
|
|
740
747
|
table: 'docs_fts',
|
|
741
748
|
columns: ['title', 'body'],
|
|
742
749
|
contentTable: 'documents',
|
|
743
750
|
tokenizer: 'porter ascii',
|
|
751
|
+
weights: { title: 'A', body: 'C' }, // PostgreSQL ranks with them; SQLite ignores them
|
|
744
752
|
}));
|
|
753
|
+
await db.exec(fts.rebuildCommand('docs_fts'));
|
|
754
|
+
|
|
755
|
+
// The best match first on every engine; a named placeholder binds once wherever it appears
|
|
756
|
+
const rows = await db.all(
|
|
757
|
+
`SELECT t.id FROM ${fts.joinClause('documents', 't', 'docs_fts', 'docs_fts')}
|
|
758
|
+
WHERE ${fts.matchClause('docs_fts', '@q')} ORDER BY ${fts.relevanceOrder('docs_fts', '@q')}`,
|
|
759
|
+
{ q: fts.sanitizeQuery(userText) },
|
|
760
|
+
);
|
|
761
|
+
```
|
|
745
762
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
WHERE ${fts.matchClause('docs_fts', '?')}
|
|
751
|
-
ORDER BY ${fts.rankExpression('fts')}
|
|
752
|
-
`;
|
|
763
|
+
- **Ordering.** `relevanceOrder` orders best first everywhere. `rankExpression` keeps each engine's own direction: FTS5's `rank` is lower-better, FTS4's `length(offsets())` and PostgreSQL's `ts_rank` are higher-better.
|
|
764
|
+
- **Queries.** `sanitizeQuery` turns text into its words, quoted and joined with OR, on every engine. PostgreSQL reads queries with `websearch_to_tsquery`: words are ANDed, and `or`, `-word` and quoted phrases work as in a search box.
|
|
765
|
+
- **One index, many features objects.** On PostgreSQL, `createIndex` records each index's table, columns, language and weights for the whole process, so a features object made later syncs, rebuilds and queries an index another one created, and each index keeps its own language. Run `createIndex` (it is idempotent) in each process before `syncInsert` or `rebuildCommand`.
|
|
766
|
+
- **Aliases.** On SQLite, alias the index as itself in `joinClause` (or pass the alias as `indexName` to `matchClause`), since a MATCH names the table as the query writes it.
|
|
753
767
|
|
|
754
|
-
// Rebuild index
|
|
755
|
-
await adapter.exec(fts.rebuildCommand('docs_fts'));
|
|
756
|
-
```
|
|
757
768
|
|
|
758
769
|
### IBlobCodec — Cross-Platform Binary Encoding
|
|
759
770
|
|
|
@@ -23,6 +23,7 @@ module.exports = __toCommonJS(features_exports);
|
|
|
23
23
|
var import_SqliteDialect = require("../../dialects/SqliteDialect.js");
|
|
24
24
|
var import_PostgresDialect = require("../../dialects/PostgresDialect.js");
|
|
25
25
|
var import_SqliteFts5 = require("../../fts/SqliteFts5.js");
|
|
26
|
+
var import_SqliteFts4 = require("../../fts/SqliteFts4.js");
|
|
26
27
|
var import_PostgresFts = require("../../fts/PostgresFts.js");
|
|
27
28
|
var import_NodeBlobCodec = require("../../codecs/NodeBlobCodec.js");
|
|
28
29
|
var import_BrowserBlobCodec = require("../../codecs/BrowserBlobCodec.js");
|
|
@@ -45,12 +46,13 @@ function extractPostgresConnectionString(adapter) {
|
|
|
45
46
|
return `postgresql://${auth}${host}:${port}/${options.database}${sslQuery}`;
|
|
46
47
|
}
|
|
47
48
|
function createStorageFeatures(adapter) {
|
|
48
|
-
const isPostgres = adapter.kind === "postgres";
|
|
49
|
+
const isPostgres = adapter.kind === "postgres" || adapter.kind === "supabase-postgres";
|
|
50
|
+
const hasFts5 = adapter.kind !== "sqljs" && adapter.kind !== "indexeddb";
|
|
49
51
|
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
50
52
|
const postgresConnectionString = isPostgres ? extractPostgresConnectionString(adapter) : void 0;
|
|
51
53
|
return {
|
|
52
54
|
dialect: isPostgres ? new import_PostgresDialect.PostgresDialect() : new import_SqliteDialect.SqliteDialect(),
|
|
53
|
-
fts: isPostgres ? new import_PostgresFts.PostgresFts() : new import_SqliteFts5.SqliteFts5(),
|
|
55
|
+
fts: isPostgres ? new import_PostgresFts.PostgresFts() : hasFts5 ? new import_SqliteFts5.SqliteFts5() : new import_SqliteFts4.SqliteFts4(),
|
|
54
56
|
blobCodec: isBrowser ? new import_BrowserBlobCodec.BrowserBlobCodec() : new import_NodeBlobCodec.NodeBlobCodec(),
|
|
55
57
|
exporter: isPostgres ? new import_PostgresExporter.PostgresExporter(postgresConnectionString) : new import_SqliteFileExporter.SqliteFileExporter(adapter)
|
|
56
58
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Full-text search abstraction.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* better-sqlite3 uses FTS5 virtual tables; sql.js and IndexedDB use FTS4, which
|
|
5
|
+
* their stock sql.js build has (it has no FTS5). Postgres implementations use
|
|
6
|
+
* tsvector columns with GIN indexes.
|
|
6
7
|
* Methods return SQL strings — no database calls.
|
|
7
8
|
*/
|
|
8
9
|
export interface IFullTextSearch {
|
|
@@ -13,25 +14,36 @@ export interface IFullTextSearch {
|
|
|
13
14
|
* @param config.columns - Columns to index.
|
|
14
15
|
* @param config.contentTable - Source table (for external-content FTS5).
|
|
15
16
|
* @param config.tokenizer - Tokenizer config (e.g. 'porter ascii').
|
|
17
|
+
* @param config.weights - PostgreSQL: a weight per column (A highest, D lowest) for ranking. The SQLite engines rank without it.
|
|
16
18
|
*/
|
|
17
19
|
createIndex(config: {
|
|
18
20
|
table: string;
|
|
19
21
|
columns: string[];
|
|
20
22
|
contentTable?: string;
|
|
21
23
|
tokenizer?: string;
|
|
24
|
+
weights?: Record<string, 'A' | 'B' | 'C' | 'D'>;
|
|
22
25
|
}): string;
|
|
23
26
|
/**
|
|
24
27
|
* Generate a WHERE clause fragment for full-text matching.
|
|
25
28
|
* SQLite: `memory_traces_fts MATCH ?`
|
|
26
|
-
* Postgres: `
|
|
29
|
+
* Postgres: `_tsv @@ websearch_to_tsquery('english', $1)`, in the index's own language
|
|
27
30
|
*/
|
|
28
31
|
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
31
|
-
* SQLite: `memory_traces_fts.rank`
|
|
32
|
-
*
|
|
33
|
+
* A rank value, in each engine's own direction.
|
|
34
|
+
* SQLite FTS5: `memory_traces_fts.rank` (lower is better)
|
|
35
|
+
* SQLite FTS4: `length(offsets(memory_traces_fts))` (higher is better)
|
|
36
|
+
* Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1))` (higher is better)
|
|
37
|
+
* Use relevanceOrder to order the same way on every engine.
|
|
33
38
|
*/
|
|
34
39
|
rankExpression(indexName: string, queryPlaceholder?: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* An ORDER BY fragment that puts the best match first on every engine.
|
|
42
|
+
* SQLite FTS5: `memory_traces_fts.rank`
|
|
43
|
+
* SQLite FTS4: `length(offsets(memory_traces_fts)) DESC`
|
|
44
|
+
* Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1)) DESC`
|
|
45
|
+
*/
|
|
46
|
+
relevanceOrder(indexName: string, queryPlaceholder?: string): string;
|
|
35
47
|
/**
|
|
36
48
|
* Generate the rebuild/reindex command.
|
|
37
49
|
* SQLite: `INSERT INTO fts_table(fts_table) VALUES('rebuild')`
|
|
@@ -46,8 +58,8 @@ export interface IFullTextSearch {
|
|
|
46
58
|
syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
|
|
47
59
|
/**
|
|
48
60
|
* Sanitize natural-language input into a safe search query.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
61
|
+
* Every engine: the words, quoted, joined with OR, with the operators of
|
|
62
|
+
* the query syntax stripped, so it matches the same rows everywhere.
|
|
51
63
|
*/
|
|
52
64
|
sanitizeQuery(input: string): string;
|
|
53
65
|
/**
|
|
@@ -10,6 +10,7 @@ export * from '../types/index.js';
|
|
|
10
10
|
export { SqliteDialect } from '../dialects/SqliteDialect.js';
|
|
11
11
|
export { PostgresDialect } from '../dialects/PostgresDialect.js';
|
|
12
12
|
export { SqliteFts5 } from '../fts/SqliteFts5.js';
|
|
13
|
+
export { SqliteFts4 } from '../fts/SqliteFts4.js';
|
|
13
14
|
export { PostgresFts } from '../fts/PostgresFts.js';
|
|
14
15
|
export { NodeBlobCodec } from '../codecs/NodeBlobCodec.js';
|
|
15
16
|
export { BrowserBlobCodec } from '../codecs/BrowserBlobCodec.js';
|
|
@@ -25,6 +25,7 @@ __export(portable_exports, {
|
|
|
25
25
|
PostgresFts: () => import_PostgresFts.PostgresFts,
|
|
26
26
|
SqliteDialect: () => import_SqliteDialect.SqliteDialect,
|
|
27
27
|
SqliteFileExporter: () => import_SqliteFileExporter.SqliteFileExporter,
|
|
28
|
+
SqliteFts4: () => import_SqliteFts4.SqliteFts4,
|
|
28
29
|
SqliteFts5: () => import_SqliteFts5.SqliteFts5,
|
|
29
30
|
createStorageFeatures: () => import_features.createStorageFeatures,
|
|
30
31
|
getStorageLogger: () => import_logger.getStorageLogger,
|
|
@@ -36,6 +37,7 @@ __reExport(portable_exports, require("../types/index.js"), module.exports);
|
|
|
36
37
|
var import_SqliteDialect = require("../dialects/SqliteDialect.js");
|
|
37
38
|
var import_PostgresDialect = require("../dialects/PostgresDialect.js");
|
|
38
39
|
var import_SqliteFts5 = require("../fts/SqliteFts5.js");
|
|
40
|
+
var import_SqliteFts4 = require("../fts/SqliteFts4.js");
|
|
39
41
|
var import_PostgresFts = require("../fts/PostgresFts.js");
|
|
40
42
|
var import_NodeBlobCodec = require("../codecs/NodeBlobCodec.js");
|
|
41
43
|
var import_BrowserBlobCodec = require("../codecs/BrowserBlobCodec.js");
|
|
@@ -56,6 +58,7 @@ __reExport(portable_exports, require("../shared/parameterUtils.js"), module.expo
|
|
|
56
58
|
PostgresFts,
|
|
57
59
|
SqliteDialect,
|
|
58
60
|
SqliteFileExporter,
|
|
61
|
+
SqliteFts4,
|
|
59
62
|
SqliteFts5,
|
|
60
63
|
createStorageFeatures,
|
|
61
64
|
getStorageLogger,
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import type { IFullTextSearch } from '../core/contracts/fts.js';
|
|
2
|
+
type Weight = 'A' | 'B' | 'C' | 'D';
|
|
2
3
|
/**
|
|
3
4
|
* Postgres full-text search using tsvector columns and GIN indexes.
|
|
4
5
|
*
|
|
5
|
-
* Unlike FTS5 (separate virtual table), Postgres stores the tsvector
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Unlike FTS5 (separate virtual table), Postgres stores the tsvector as a
|
|
7
|
+
* column `_tsv` on the content table itself. The `createIndex()` call adds the
|
|
8
|
+
* column, creates the GIN index, and backfills existing rows. Queries go
|
|
9
|
+
* through `websearch_to_tsquery`, which reads raw user text without syntax
|
|
10
|
+
* errors: words are ANDed, and `or`, `-word` and quoted phrases work as in a
|
|
11
|
+
* web search box.
|
|
8
12
|
*/
|
|
9
13
|
export declare class PostgresFts implements IFullTextSearch {
|
|
10
|
-
/** The content table is stored during createIndex for use by other methods. */
|
|
11
|
-
private _contentTable;
|
|
12
|
-
private _columns;
|
|
13
|
-
private _lang;
|
|
14
14
|
createIndex(config: {
|
|
15
15
|
table: string;
|
|
16
16
|
columns: string[];
|
|
17
17
|
contentTable?: string;
|
|
18
18
|
tokenizer?: string;
|
|
19
|
+
weights?: Record<string, Weight>;
|
|
19
20
|
}): string;
|
|
20
|
-
matchClause(
|
|
21
|
-
rankExpression(
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
22
|
+
rankExpression(indexName: string, queryPlaceholder?: string): string;
|
|
23
|
+
relevanceOrder(indexName: string, queryPlaceholder?: string): string;
|
|
24
|
+
rebuildCommand(indexName: string): string;
|
|
25
|
+
syncInsert(indexName: string, _rowIdExpr: string, columns: string[]): string;
|
|
24
26
|
sanitizeQuery(input: string): string;
|
|
25
27
|
joinClause(contentTable: string, contentAlias: string, _ftsAlias: string, _indexName: string): string;
|
|
26
|
-
/** Map FTS5-style tokenizer strings to Postgres text search configurations. */
|
|
27
|
-
private _tokenizerToLang;
|
|
28
28
|
}
|
|
29
|
+
export {};
|
|
29
30
|
//# sourceMappingURL=PostgresFts.d.ts.map
|
|
@@ -20,57 +20,85 @@ __export(PostgresFts_exports, {
|
|
|
20
20
|
PostgresFts: () => PostgresFts
|
|
21
21
|
});
|
|
22
22
|
module.exports = __toCommonJS(PostgresFts_exports);
|
|
23
|
+
const INDEXES = /* @__PURE__ */ new Map();
|
|
24
|
+
const WEIGHTS = /* @__PURE__ */ new Set(["A", "B", "C", "D"]);
|
|
23
25
|
class PostgresFts {
|
|
24
|
-
constructor() {
|
|
25
|
-
this._contentTable = "";
|
|
26
|
-
this._columns = [];
|
|
27
|
-
this._lang = "english";
|
|
28
|
-
}
|
|
29
26
|
createIndex(config) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
const index = {
|
|
28
|
+
contentTable: config.contentTable ?? config.table,
|
|
29
|
+
columns: config.columns,
|
|
30
|
+
lang: tokenizerToLang(config.tokenizer),
|
|
31
|
+
weights: checkWeights(config.columns, config.weights)
|
|
32
|
+
};
|
|
33
|
+
INDEXES.set(config.table, index);
|
|
34
|
+
const ct = index.contentTable;
|
|
36
35
|
return [
|
|
37
36
|
`ALTER TABLE ${ct} ADD COLUMN IF NOT EXISTS _tsv tsvector`,
|
|
38
37
|
`CREATE INDEX IF NOT EXISTS idx_${config.table}_tsv ON ${ct} USING GIN(_tsv)`,
|
|
39
|
-
`UPDATE ${ct} SET _tsv =
|
|
38
|
+
`UPDATE ${ct} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))} WHERE _tsv IS NULL`
|
|
40
39
|
].join(";\n");
|
|
41
40
|
}
|
|
42
|
-
matchClause(
|
|
43
|
-
return `_tsv @@
|
|
41
|
+
matchClause(indexName, queryPlaceholder) {
|
|
42
|
+
return `_tsv @@ websearch_to_tsquery('${langOf(indexName)}', ${queryPlaceholder})`;
|
|
44
43
|
}
|
|
45
|
-
rankExpression(
|
|
44
|
+
rankExpression(indexName, queryPlaceholder) {
|
|
46
45
|
const qp = queryPlaceholder ?? "$1";
|
|
47
|
-
return `ts_rank(_tsv,
|
|
46
|
+
return `ts_rank(_tsv, websearch_to_tsquery('${langOf(indexName)}', ${qp}))`;
|
|
47
|
+
}
|
|
48
|
+
relevanceOrder(indexName, queryPlaceholder) {
|
|
49
|
+
return `${this.rankExpression(indexName, queryPlaceholder)} DESC`;
|
|
48
50
|
}
|
|
49
|
-
rebuildCommand(
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
return `UPDATE ${ct} SET _tsv = to_tsvector('${this._lang}', ${colConcat})`;
|
|
51
|
+
rebuildCommand(indexName) {
|
|
52
|
+
const index = described(indexName);
|
|
53
|
+
return `UPDATE ${index.contentTable} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))}`;
|
|
53
54
|
}
|
|
54
|
-
syncInsert(
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
return `UPDATE ${
|
|
55
|
+
syncInsert(indexName, _rowIdExpr, columns) {
|
|
56
|
+
const index = described(indexName);
|
|
57
|
+
const values = columns.map((_, i) => `COALESCE($${i + 2}, '')`);
|
|
58
|
+
return `UPDATE ${index.contentTable} SET _tsv = ${tsvector({ ...index, columns }, values)} WHERE id = $1`;
|
|
58
59
|
}
|
|
59
60
|
sanitizeQuery(input) {
|
|
60
|
-
return input;
|
|
61
|
+
return input.replace(/[*()":^~{}[\]\\-]/g, " ").split(/\s+/).filter((word) => word.length > 0 && word.toLowerCase() !== "or").map((word) => `"${word}"`).join(" or ");
|
|
61
62
|
}
|
|
62
63
|
joinClause(contentTable, contentAlias, _ftsAlias, _indexName) {
|
|
63
64
|
return `${contentTable} ${contentAlias}`;
|
|
64
65
|
}
|
|
65
|
-
/** Map FTS5-style tokenizer strings to Postgres text search configurations. */
|
|
66
|
-
_tokenizerToLang(tokenizer) {
|
|
67
|
-
if (!tokenizer)
|
|
68
|
-
return "english";
|
|
69
|
-
if (tokenizer.includes("porter"))
|
|
70
|
-
return "english";
|
|
71
|
-
return "simple";
|
|
72
|
-
}
|
|
73
66
|
}
|
|
67
|
+
const tokenizerToLang = (tokenizer) => {
|
|
68
|
+
if (!tokenizer)
|
|
69
|
+
return "english";
|
|
70
|
+
if (tokenizer.includes("porter"))
|
|
71
|
+
return "english";
|
|
72
|
+
return "simple";
|
|
73
|
+
};
|
|
74
|
+
const checkWeights = (columns, weights) => {
|
|
75
|
+
if (!weights)
|
|
76
|
+
return void 0;
|
|
77
|
+
for (const [column, weight] of Object.entries(weights)) {
|
|
78
|
+
if (!columns.includes(column))
|
|
79
|
+
throw new Error(`weights names ${JSON.stringify(column)}, which is not one of the indexed columns.`);
|
|
80
|
+
if (!WEIGHTS.has(weight))
|
|
81
|
+
throw new Error(`The weight of ${column} must be 'A', 'B', 'C' or 'D'; got ${JSON.stringify(weight)}.`);
|
|
82
|
+
}
|
|
83
|
+
return weights;
|
|
84
|
+
};
|
|
85
|
+
const tsvector = (index, values) => {
|
|
86
|
+
if (!index.weights)
|
|
87
|
+
return `to_tsvector('${index.lang}', ${values.join(" || ' ' || ")})`;
|
|
88
|
+
return values.map((value, i) => {
|
|
89
|
+
const weight = index.weights[index.columns[i]];
|
|
90
|
+
const vector = `to_tsvector('${index.lang}', ${value})`;
|
|
91
|
+
return weight ? `setweight(${vector}, '${weight}')` : vector;
|
|
92
|
+
}).join(" || ");
|
|
93
|
+
};
|
|
94
|
+
const described = (indexName) => {
|
|
95
|
+
const index = INDEXES.get(indexName);
|
|
96
|
+
if (!index) {
|
|
97
|
+
throw new Error(`No PostgreSQL full-text index named ${JSON.stringify(indexName)} has been described in this process: run its createIndex() first (it is idempotent).`);
|
|
98
|
+
}
|
|
99
|
+
return index;
|
|
100
|
+
};
|
|
101
|
+
const langOf = (indexName) => INDEXES.get(indexName)?.lang ?? "english";
|
|
74
102
|
// Annotate the CommonJS export names for ESM import in node:
|
|
75
103
|
0 && (module.exports = {
|
|
76
104
|
PostgresFts
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IFullTextSearch } from '../core/contracts/fts.js';
|
|
2
|
+
/**
|
|
3
|
+
* SQLite FTS4 full-text search, for sql.js and the IndexedDB adapter: the
|
|
4
|
+
* stock sql.js build compiles SQLite with FTS3 and FTS4 and without FTS5.
|
|
5
|
+
*
|
|
6
|
+
* Uses FTS4 virtual tables with external content, the porter, unicode61 or
|
|
7
|
+
* simple tokenizer, and rowid joins to the content table. FTS4 has no rank
|
|
8
|
+
* column; relevanceOrder orders by the length of `offsets()`, which grows with
|
|
9
|
+
* the number of matching terms, so the best match comes first.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SqliteFts4 implements IFullTextSearch {
|
|
12
|
+
createIndex(config: {
|
|
13
|
+
table: string;
|
|
14
|
+
columns: string[];
|
|
15
|
+
contentTable?: string;
|
|
16
|
+
tokenizer?: string;
|
|
17
|
+
}): string;
|
|
18
|
+
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
19
|
+
rankExpression(indexName: string, _queryPlaceholder?: string): string;
|
|
20
|
+
relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
|
|
21
|
+
rebuildCommand(indexName: string): string;
|
|
22
|
+
syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
|
|
23
|
+
sanitizeQuery(input: string): string;
|
|
24
|
+
joinClause(contentTable: string, contentAlias: string, ftsAlias: string, indexName: string): string;
|
|
25
|
+
/** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
|
|
26
|
+
private tokenizer;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=SqliteFts4.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var SqliteFts4_exports = {};
|
|
19
|
+
__export(SqliteFts4_exports, {
|
|
20
|
+
SqliteFts4: () => SqliteFts4
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(SqliteFts4_exports);
|
|
23
|
+
class SqliteFts4 {
|
|
24
|
+
createIndex(config) {
|
|
25
|
+
const parts = [...config.columns];
|
|
26
|
+
if (config.contentTable)
|
|
27
|
+
parts.push(`content="${config.contentTable}"`);
|
|
28
|
+
parts.push(`tokenize=${this.tokenizer(config.tokenizer)}`);
|
|
29
|
+
return `CREATE VIRTUAL TABLE IF NOT EXISTS ${config.table} USING fts4(${parts.join(", ")})`;
|
|
30
|
+
}
|
|
31
|
+
matchClause(indexName, queryPlaceholder) {
|
|
32
|
+
return `${indexName} MATCH ${queryPlaceholder}`;
|
|
33
|
+
}
|
|
34
|
+
rankExpression(indexName, _queryPlaceholder) {
|
|
35
|
+
return `length(offsets(${indexName}))`;
|
|
36
|
+
}
|
|
37
|
+
relevanceOrder(indexName, _queryPlaceholder) {
|
|
38
|
+
return `length(offsets(${indexName})) DESC`;
|
|
39
|
+
}
|
|
40
|
+
rebuildCommand(indexName) {
|
|
41
|
+
return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
|
|
42
|
+
}
|
|
43
|
+
syncInsert(indexName, rowIdExpr, columns) {
|
|
44
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
45
|
+
return `INSERT INTO ${indexName} (docid, ${columns.join(", ")}) VALUES (${rowIdExpr}, ${placeholders})`;
|
|
46
|
+
}
|
|
47
|
+
sanitizeQuery(input) {
|
|
48
|
+
return input.replace(/[*()":^~{}[\]\\]/g, "").split(/\s+/).filter((word) => word.length > 0).map((word) => `"${word}"`).join(" OR ");
|
|
49
|
+
}
|
|
50
|
+
joinClause(contentTable, contentAlias, ftsAlias, indexName) {
|
|
51
|
+
return `${indexName} ${ftsAlias} JOIN ${contentTable} ${contentAlias} ON ${contentAlias}.rowid = ${ftsAlias}.docid`;
|
|
52
|
+
}
|
|
53
|
+
/** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
|
|
54
|
+
tokenizer(tokenizer) {
|
|
55
|
+
if (!tokenizer)
|
|
56
|
+
return "unicode61";
|
|
57
|
+
if (tokenizer.includes("porter"))
|
|
58
|
+
return "porter";
|
|
59
|
+
if (tokenizer.includes("unicode61"))
|
|
60
|
+
return "unicode61";
|
|
61
|
+
return "simple";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
65
|
+
0 && (module.exports = {
|
|
66
|
+
SqliteFts4
|
|
67
|
+
});
|
|
@@ -14,6 +14,7 @@ export declare class SqliteFts5 implements IFullTextSearch {
|
|
|
14
14
|
}): string;
|
|
15
15
|
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
16
16
|
rankExpression(indexName: string, _queryPlaceholder?: string): string;
|
|
17
|
+
relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
|
|
17
18
|
rebuildCommand(indexName: string): string;
|
|
18
19
|
syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
|
|
19
20
|
sanitizeQuery(input: string): string;
|
|
@@ -38,6 +38,9 @@ class SqliteFts5 {
|
|
|
38
38
|
rankExpression(indexName, _queryPlaceholder) {
|
|
39
39
|
return `${indexName}.rank`;
|
|
40
40
|
}
|
|
41
|
+
relevanceOrder(indexName, _queryPlaceholder) {
|
|
42
|
+
return `${indexName}.rank`;
|
|
43
|
+
}
|
|
41
44
|
rebuildCommand(indexName) {
|
|
42
45
|
return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
|
|
43
46
|
}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export { segmentFilePath, segmentName, validateSegment } from './shared/segment.
|
|
|
20
20
|
export { SqliteDialect } from './dialects/SqliteDialect.js';
|
|
21
21
|
export { PostgresDialect } from './dialects/PostgresDialect.js';
|
|
22
22
|
export { SqliteFts5 } from './fts/SqliteFts5.js';
|
|
23
|
+
export { SqliteFts4 } from './fts/SqliteFts4.js';
|
|
23
24
|
export { PostgresFts } from './fts/PostgresFts.js';
|
|
24
25
|
export { NodeBlobCodec } from './codecs/NodeBlobCodec.js';
|
|
25
26
|
export { BrowserBlobCodec } from './codecs/BrowserBlobCodec.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(dist_exports, {
|
|
|
25
25
|
PostgresFts: () => import_PostgresFts.PostgresFts,
|
|
26
26
|
SqliteDialect: () => import_SqliteDialect.SqliteDialect,
|
|
27
27
|
SqliteFileExporter: () => import_SqliteFileExporter.SqliteFileExporter,
|
|
28
|
+
SqliteFts4: () => import_SqliteFts4.SqliteFts4,
|
|
28
29
|
SqliteFts5: () => import_SqliteFts5.SqliteFts5,
|
|
29
30
|
createStorageFeatures: () => import_features.createStorageFeatures,
|
|
30
31
|
getStorageLogger: () => import_logger.getStorageLogger,
|
|
@@ -57,6 +58,7 @@ var import_segment = require("./shared/segment.js");
|
|
|
57
58
|
var import_SqliteDialect = require("./dialects/SqliteDialect.js");
|
|
58
59
|
var import_PostgresDialect = require("./dialects/PostgresDialect.js");
|
|
59
60
|
var import_SqliteFts5 = require("./fts/SqliteFts5.js");
|
|
61
|
+
var import_SqliteFts4 = require("./fts/SqliteFts4.js");
|
|
60
62
|
var import_PostgresFts = require("./fts/PostgresFts.js");
|
|
61
63
|
var import_NodeBlobCodec = require("./codecs/NodeBlobCodec.js");
|
|
62
64
|
var import_BrowserBlobCodec = require("./codecs/BrowserBlobCodec.js");
|
|
@@ -74,6 +76,7 @@ var import_logger = require("./shared/logger.js");
|
|
|
74
76
|
PostgresFts,
|
|
75
77
|
SqliteDialect,
|
|
76
78
|
SqliteFileExporter,
|
|
79
|
+
SqliteFts4,
|
|
77
80
|
SqliteFts5,
|
|
78
81
|
createStorageFeatures,
|
|
79
82
|
getStorageLogger,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAoCjD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAgB9E"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SqliteDialect } from '../../dialects/SqliteDialect.js';
|
|
2
2
|
import { PostgresDialect } from '../../dialects/PostgresDialect.js';
|
|
3
3
|
import { SqliteFts5 } from '../../fts/SqliteFts5.js';
|
|
4
|
+
import { SqliteFts4 } from '../../fts/SqliteFts4.js';
|
|
4
5
|
import { PostgresFts } from '../../fts/PostgresFts.js';
|
|
5
6
|
import { NodeBlobCodec } from '../../codecs/NodeBlobCodec.js';
|
|
6
7
|
import { BrowserBlobCodec } from '../../codecs/BrowserBlobCodec.js';
|
|
@@ -29,12 +30,15 @@ function extractPostgresConnectionString(adapter) {
|
|
|
29
30
|
* dialect, FTS, BLOB codec, and exporter implementations.
|
|
30
31
|
*/
|
|
31
32
|
export function createStorageFeatures(adapter) {
|
|
32
|
-
|
|
33
|
+
// The Supabase adapter runs PostgreSQL too.
|
|
34
|
+
const isPostgres = adapter.kind === 'postgres' || adapter.kind === 'supabase-postgres';
|
|
35
|
+
// sql.js, and the IndexedDB adapter over it, have FTS3 and FTS4 and no FTS5.
|
|
36
|
+
const hasFts5 = adapter.kind !== 'sqljs' && adapter.kind !== 'indexeddb';
|
|
33
37
|
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
34
38
|
const postgresConnectionString = isPostgres ? extractPostgresConnectionString(adapter) : undefined;
|
|
35
39
|
return {
|
|
36
40
|
dialect: isPostgres ? new PostgresDialect() : new SqliteDialect(),
|
|
37
|
-
fts: isPostgres ? new PostgresFts() : new SqliteFts5(),
|
|
41
|
+
fts: isPostgres ? new PostgresFts() : hasFts5 ? new SqliteFts5() : new SqliteFts4(),
|
|
38
42
|
blobCodec: isBrowser ? new BrowserBlobCodec() : new NodeBlobCodec(),
|
|
39
43
|
exporter: isPostgres
|
|
40
44
|
? new PostgresExporter(postgresConnectionString)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features.js","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAYvE,SAAS,+BAA+B,CAAC,OAAuB;IAC9D,MAAM,OAAO,GAAI,OAAqE,CAAC,OAAO,CAAC;IAC/F,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,OAAO,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAC,gBAAgB,CAAC;IAC9D,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAExC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,OAAO,gBAAgB,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC;AAC9E,CAAC;AAeD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAuB;IAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,KAAK,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"features.js","sourceRoot":"","sources":["../../../src/core/contracts/features.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAYvE,SAAS,+BAA+B,CAAC,OAAuB;IAC9D,MAAM,OAAO,GAAI,OAAqE,CAAC,OAAO,CAAC;IAC/F,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,OAAO,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAC,gBAAgB,CAAC;IAC9D,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAExC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,OAAO,gBAAgB,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC,QAAQ,GAAG,QAAQ,EAAE,CAAC;AAC9E,CAAC;AAeD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAuB;IAC3D,4CAA4C;IAC5C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,IAAI,KAAK,mBAAmB,CAAC;IACvF,6EAA6E;IAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC;IACzE,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,WAAW,CAAC;IAC1F,MAAM,wBAAwB,GAAG,UAAU,CAAC,CAAC,CAAC,+BAA+B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnG,OAAO;QACL,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,aAAa,EAAE;QACjE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE;QACnF,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,gBAAgB,EAAE,CAAC,CAAC,CAAC,IAAI,aAAa,EAAE;QACnE,QAAQ,EAAE,UAAU;YAClB,CAAC,CAAC,IAAI,gBAAgB,CAAC,wBAAwB,CAAC;YAChD,CAAC,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC;KACpC,CAAC;AACJ,CAAC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Full-text search abstraction.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* better-sqlite3 uses FTS5 virtual tables; sql.js and IndexedDB use FTS4, which
|
|
5
|
+
* their stock sql.js build has (it has no FTS5). Postgres implementations use
|
|
6
|
+
* tsvector columns with GIN indexes.
|
|
6
7
|
* Methods return SQL strings — no database calls.
|
|
7
8
|
*/
|
|
8
9
|
export interface IFullTextSearch {
|
|
@@ -13,25 +14,36 @@ export interface IFullTextSearch {
|
|
|
13
14
|
* @param config.columns - Columns to index.
|
|
14
15
|
* @param config.contentTable - Source table (for external-content FTS5).
|
|
15
16
|
* @param config.tokenizer - Tokenizer config (e.g. 'porter ascii').
|
|
17
|
+
* @param config.weights - PostgreSQL: a weight per column (A highest, D lowest) for ranking. The SQLite engines rank without it.
|
|
16
18
|
*/
|
|
17
19
|
createIndex(config: {
|
|
18
20
|
table: string;
|
|
19
21
|
columns: string[];
|
|
20
22
|
contentTable?: string;
|
|
21
23
|
tokenizer?: string;
|
|
24
|
+
weights?: Record<string, 'A' | 'B' | 'C' | 'D'>;
|
|
22
25
|
}): string;
|
|
23
26
|
/**
|
|
24
27
|
* Generate a WHERE clause fragment for full-text matching.
|
|
25
28
|
* SQLite: `memory_traces_fts MATCH ?`
|
|
26
|
-
* Postgres: `
|
|
29
|
+
* Postgres: `_tsv @@ websearch_to_tsquery('english', $1)`, in the index's own language
|
|
27
30
|
*/
|
|
28
31
|
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
31
|
-
* SQLite: `memory_traces_fts.rank`
|
|
32
|
-
*
|
|
33
|
+
* A rank value, in each engine's own direction.
|
|
34
|
+
* SQLite FTS5: `memory_traces_fts.rank` (lower is better)
|
|
35
|
+
* SQLite FTS4: `length(offsets(memory_traces_fts))` (higher is better)
|
|
36
|
+
* Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1))` (higher is better)
|
|
37
|
+
* Use relevanceOrder to order the same way on every engine.
|
|
33
38
|
*/
|
|
34
39
|
rankExpression(indexName: string, queryPlaceholder?: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* An ORDER BY fragment that puts the best match first on every engine.
|
|
42
|
+
* SQLite FTS5: `memory_traces_fts.rank`
|
|
43
|
+
* SQLite FTS4: `length(offsets(memory_traces_fts)) DESC`
|
|
44
|
+
* Postgres: `ts_rank(_tsv, websearch_to_tsquery('english', $1)) DESC`
|
|
45
|
+
*/
|
|
46
|
+
relevanceOrder(indexName: string, queryPlaceholder?: string): string;
|
|
35
47
|
/**
|
|
36
48
|
* Generate the rebuild/reindex command.
|
|
37
49
|
* SQLite: `INSERT INTO fts_table(fts_table) VALUES('rebuild')`
|
|
@@ -46,8 +58,8 @@ export interface IFullTextSearch {
|
|
|
46
58
|
syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
|
|
47
59
|
/**
|
|
48
60
|
* Sanitize natural-language input into a safe search query.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
61
|
+
* Every engine: the words, quoted, joined with OR, with the operators of
|
|
62
|
+
* the query syntax stripped, so it matches the same rows everywhere.
|
|
51
63
|
*/
|
|
52
64
|
sanitizeQuery(input: string): string;
|
|
53
65
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/fts.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fts.d.ts","sourceRoot":"","sources":["../../../src/core/contracts/fts.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;OAQG;IACH,WAAW,CAAC,MAAM,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;KACjD,GAAG,MAAM,CAAC;IAEX;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;IAEjE;;;;;;OAMG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAErE;;;;;OAKG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAErE;;;;OAIG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1C;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE5E;;;;OAIG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAErC;;;;;;;;;;OAUG;IACH,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CACrG"}
|
package/dist/core/portable.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from '../types/index.js';
|
|
|
10
10
|
export { SqliteDialect } from '../dialects/SqliteDialect.js';
|
|
11
11
|
export { PostgresDialect } from '../dialects/PostgresDialect.js';
|
|
12
12
|
export { SqliteFts5 } from '../fts/SqliteFts5.js';
|
|
13
|
+
export { SqliteFts4 } from '../fts/SqliteFts4.js';
|
|
13
14
|
export { PostgresFts } from '../fts/PostgresFts.js';
|
|
14
15
|
export { NodeBlobCodec } from '../codecs/NodeBlobCodec.js';
|
|
15
16
|
export { BrowserBlobCodec } from '../codecs/BrowserBlobCodec.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portable.d.ts","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"portable.d.ts","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
|
package/dist/core/portable.js
CHANGED
|
@@ -10,6 +10,7 @@ export * from '../types/index.js';
|
|
|
10
10
|
export { SqliteDialect } from '../dialects/SqliteDialect.js';
|
|
11
11
|
export { PostgresDialect } from '../dialects/PostgresDialect.js';
|
|
12
12
|
export { SqliteFts5 } from '../fts/SqliteFts5.js';
|
|
13
|
+
export { SqliteFts4 } from '../fts/SqliteFts4.js';
|
|
13
14
|
export { PostgresFts } from '../fts/PostgresFts.js';
|
|
14
15
|
export { NodeBlobCodec } from '../codecs/NodeBlobCodec.js';
|
|
15
16
|
export { BrowserBlobCodec } from '../codecs/BrowserBlobCodec.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portable.js","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"portable.js","sourceRoot":"","sources":["../../src/core/portable.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,kBAAkB,CAAC;AAC1F,cAAc,yCAAyC,CAAC;AACxD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC"}
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import type { IFullTextSearch } from '../core/contracts/fts.js';
|
|
2
|
+
type Weight = 'A' | 'B' | 'C' | 'D';
|
|
2
3
|
/**
|
|
3
4
|
* Postgres full-text search using tsvector columns and GIN indexes.
|
|
4
5
|
*
|
|
5
|
-
* Unlike FTS5 (separate virtual table), Postgres stores the tsvector
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Unlike FTS5 (separate virtual table), Postgres stores the tsvector as a
|
|
7
|
+
* column `_tsv` on the content table itself. The `createIndex()` call adds the
|
|
8
|
+
* column, creates the GIN index, and backfills existing rows. Queries go
|
|
9
|
+
* through `websearch_to_tsquery`, which reads raw user text without syntax
|
|
10
|
+
* errors: words are ANDed, and `or`, `-word` and quoted phrases work as in a
|
|
11
|
+
* web search box.
|
|
8
12
|
*/
|
|
9
13
|
export declare class PostgresFts implements IFullTextSearch {
|
|
10
|
-
/** The content table is stored during createIndex for use by other methods. */
|
|
11
|
-
private _contentTable;
|
|
12
|
-
private _columns;
|
|
13
|
-
private _lang;
|
|
14
14
|
createIndex(config: {
|
|
15
15
|
table: string;
|
|
16
16
|
columns: string[];
|
|
17
17
|
contentTable?: string;
|
|
18
18
|
tokenizer?: string;
|
|
19
|
+
weights?: Record<string, Weight>;
|
|
19
20
|
}): string;
|
|
20
|
-
matchClause(
|
|
21
|
-
rankExpression(
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
22
|
+
rankExpression(indexName: string, queryPlaceholder?: string): string;
|
|
23
|
+
relevanceOrder(indexName: string, queryPlaceholder?: string): string;
|
|
24
|
+
rebuildCommand(indexName: string): string;
|
|
25
|
+
syncInsert(indexName: string, _rowIdExpr: string, columns: string[]): string;
|
|
24
26
|
sanitizeQuery(input: string): string;
|
|
25
27
|
joinClause(contentTable: string, contentAlias: string, _ftsAlias: string, _indexName: string): string;
|
|
26
|
-
/** Map FTS5-style tokenizer strings to Postgres text search configurations. */
|
|
27
|
-
private _tokenizerToLang;
|
|
28
28
|
}
|
|
29
|
+
export {};
|
|
29
30
|
//# sourceMappingURL=PostgresFts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostgresFts.d.ts","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE
|
|
1
|
+
{"version":3,"file":"PostgresFts.d.ts","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,KAAK,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAoBpC;;;;;;;;;GASG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,WAAW,CAAC,MAAM,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAClC,GAAG,MAAM;IAgBV,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAKpE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIpE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAKzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAM5E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;CAGtG"}
|
package/dist/fts/PostgresFts.js
CHANGED
|
@@ -1,62 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The indexes createIndex has described in this process, by index name. It
|
|
3
|
+
* is the module's, not an instance's, so a features object made later (or a
|
|
4
|
+
* second one) syncs, rebuilds and queries an index another one created, and
|
|
5
|
+
* each index keeps its own language.
|
|
6
|
+
*/
|
|
7
|
+
const INDEXES = new Map();
|
|
8
|
+
const WEIGHTS = new Set(['A', 'B', 'C', 'D']);
|
|
1
9
|
/**
|
|
2
10
|
* Postgres full-text search using tsvector columns and GIN indexes.
|
|
3
11
|
*
|
|
4
|
-
* Unlike FTS5 (separate virtual table), Postgres stores the tsvector
|
|
5
|
-
*
|
|
6
|
-
*
|
|
12
|
+
* Unlike FTS5 (separate virtual table), Postgres stores the tsvector as a
|
|
13
|
+
* column `_tsv` on the content table itself. The `createIndex()` call adds the
|
|
14
|
+
* column, creates the GIN index, and backfills existing rows. Queries go
|
|
15
|
+
* through `websearch_to_tsquery`, which reads raw user text without syntax
|
|
16
|
+
* errors: words are ANDed, and `or`, `-word` and quoted phrases work as in a
|
|
17
|
+
* web search box.
|
|
7
18
|
*/
|
|
8
19
|
export class PostgresFts {
|
|
9
|
-
constructor() {
|
|
10
|
-
/** The content table is stored during createIndex for use by other methods. */
|
|
11
|
-
this._contentTable = '';
|
|
12
|
-
this._columns = [];
|
|
13
|
-
this._lang = 'english';
|
|
14
|
-
}
|
|
15
20
|
createIndex(config) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const index = {
|
|
22
|
+
contentTable: config.contentTable ?? config.table,
|
|
23
|
+
columns: config.columns,
|
|
24
|
+
lang: tokenizerToLang(config.tokenizer),
|
|
25
|
+
weights: checkWeights(config.columns, config.weights),
|
|
26
|
+
};
|
|
27
|
+
INDEXES.set(config.table, index);
|
|
28
|
+
const ct = index.contentTable;
|
|
22
29
|
return [
|
|
23
30
|
`ALTER TABLE ${ct} ADD COLUMN IF NOT EXISTS _tsv tsvector`,
|
|
24
31
|
`CREATE INDEX IF NOT EXISTS idx_${config.table}_tsv ON ${ct} USING GIN(_tsv)`,
|
|
25
|
-
`UPDATE ${ct} SET _tsv =
|
|
32
|
+
`UPDATE ${ct} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))} WHERE _tsv IS NULL`,
|
|
26
33
|
].join(';\n');
|
|
27
34
|
}
|
|
28
|
-
matchClause(
|
|
29
|
-
return `_tsv @@
|
|
35
|
+
matchClause(indexName, queryPlaceholder) {
|
|
36
|
+
return `_tsv @@ websearch_to_tsquery('${langOf(indexName)}', ${queryPlaceholder})`;
|
|
30
37
|
}
|
|
31
|
-
rankExpression(
|
|
38
|
+
rankExpression(indexName, queryPlaceholder) {
|
|
32
39
|
const qp = queryPlaceholder ?? '$1';
|
|
33
|
-
return `ts_rank(_tsv,
|
|
40
|
+
return `ts_rank(_tsv, websearch_to_tsquery('${langOf(indexName)}', ${qp}))`;
|
|
41
|
+
}
|
|
42
|
+
relevanceOrder(indexName, queryPlaceholder) {
|
|
43
|
+
return `${this.rankExpression(indexName, queryPlaceholder)} DESC`;
|
|
34
44
|
}
|
|
35
|
-
rebuildCommand(
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
return `UPDATE ${ct} SET _tsv = to_tsvector('${this._lang}', ${colConcat})`;
|
|
45
|
+
rebuildCommand(indexName) {
|
|
46
|
+
const index = described(indexName);
|
|
47
|
+
return `UPDATE ${index.contentTable} SET _tsv = ${tsvector(index, index.columns.map((column) => `COALESCE(${column}, '')`))}`;
|
|
39
48
|
}
|
|
40
|
-
syncInsert(
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
.join(" || ' ' || ");
|
|
45
|
-
return `UPDATE ${ct} SET _tsv = to_tsvector('${this._lang}', ${colConcat}) WHERE id = $1`;
|
|
49
|
+
syncInsert(indexName, _rowIdExpr, columns) {
|
|
50
|
+
const index = described(indexName);
|
|
51
|
+
const values = columns.map((_, i) => `COALESCE($${i + 2}, '')`);
|
|
52
|
+
return `UPDATE ${index.contentTable} SET _tsv = ${tsvector({ ...index, columns }, values)} WHERE id = $1`;
|
|
46
53
|
}
|
|
47
54
|
sanitizeQuery(input) {
|
|
48
|
-
return input
|
|
55
|
+
return input
|
|
56
|
+
.replace(/[*()":^~{}[\]\\-]/g, ' ')
|
|
57
|
+
.split(/\s+/)
|
|
58
|
+
.filter((word) => word.length > 0 && word.toLowerCase() !== 'or')
|
|
59
|
+
.map((word) => `"${word}"`)
|
|
60
|
+
.join(' or ');
|
|
49
61
|
}
|
|
50
62
|
joinClause(contentTable, contentAlias, _ftsAlias, _indexName) {
|
|
51
63
|
return `${contentTable} ${contentAlias}`;
|
|
52
64
|
}
|
|
53
|
-
/** Map FTS5-style tokenizer strings to Postgres text search configurations. */
|
|
54
|
-
_tokenizerToLang(tokenizer) {
|
|
55
|
-
if (!tokenizer)
|
|
56
|
-
return 'english';
|
|
57
|
-
if (tokenizer.includes('porter'))
|
|
58
|
-
return 'english';
|
|
59
|
-
return 'simple';
|
|
60
|
-
}
|
|
61
65
|
}
|
|
66
|
+
/** Map FTS5-style tokenizer strings to Postgres text search configurations. */
|
|
67
|
+
const tokenizerToLang = (tokenizer) => {
|
|
68
|
+
if (!tokenizer)
|
|
69
|
+
return 'english';
|
|
70
|
+
if (tokenizer.includes('porter'))
|
|
71
|
+
return 'english';
|
|
72
|
+
return 'simple';
|
|
73
|
+
};
|
|
74
|
+
const checkWeights = (columns, weights) => {
|
|
75
|
+
if (!weights)
|
|
76
|
+
return undefined;
|
|
77
|
+
for (const [column, weight] of Object.entries(weights)) {
|
|
78
|
+
if (!columns.includes(column))
|
|
79
|
+
throw new Error(`weights names ${JSON.stringify(column)}, which is not one of the indexed columns.`);
|
|
80
|
+
if (!WEIGHTS.has(weight))
|
|
81
|
+
throw new Error(`The weight of ${column} must be 'A', 'B', 'C' or 'D'; got ${JSON.stringify(weight)}.`);
|
|
82
|
+
}
|
|
83
|
+
return weights;
|
|
84
|
+
};
|
|
85
|
+
/** The tsvector expression over `values`, one per indexed column, weighted when the index has weights. */
|
|
86
|
+
const tsvector = (index, values) => {
|
|
87
|
+
if (!index.weights)
|
|
88
|
+
return `to_tsvector('${index.lang}', ${values.join(" || ' ' || ")})`;
|
|
89
|
+
return values
|
|
90
|
+
.map((value, i) => {
|
|
91
|
+
const weight = index.weights[index.columns[i]];
|
|
92
|
+
const vector = `to_tsvector('${index.lang}', ${value})`;
|
|
93
|
+
return weight ? `setweight(${vector}, '${weight}')` : vector;
|
|
94
|
+
})
|
|
95
|
+
.join(' || ');
|
|
96
|
+
};
|
|
97
|
+
const described = (indexName) => {
|
|
98
|
+
const index = INDEXES.get(indexName);
|
|
99
|
+
if (!index) {
|
|
100
|
+
throw new Error(`No PostgreSQL full-text index named ${JSON.stringify(indexName)} has been described in this process: run its createIndex() first (it is idempotent).`);
|
|
101
|
+
}
|
|
102
|
+
return index;
|
|
103
|
+
};
|
|
104
|
+
/** The language of an index, or 'english' for an index this process has not described. */
|
|
105
|
+
const langOf = (indexName) => INDEXES.get(indexName)?.lang ?? 'english';
|
|
62
106
|
//# sourceMappingURL=PostgresFts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostgresFts.js","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PostgresFts.js","sourceRoot":"","sources":["../../src/fts/PostgresFts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IFullTextSearch } from '../core/contracts/fts.js';
|
|
2
|
+
/**
|
|
3
|
+
* SQLite FTS4 full-text search, for sql.js and the IndexedDB adapter: the
|
|
4
|
+
* stock sql.js build compiles SQLite with FTS3 and FTS4 and without FTS5.
|
|
5
|
+
*
|
|
6
|
+
* Uses FTS4 virtual tables with external content, the porter, unicode61 or
|
|
7
|
+
* simple tokenizer, and rowid joins to the content table. FTS4 has no rank
|
|
8
|
+
* column; relevanceOrder orders by the length of `offsets()`, which grows with
|
|
9
|
+
* the number of matching terms, so the best match comes first.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SqliteFts4 implements IFullTextSearch {
|
|
12
|
+
createIndex(config: {
|
|
13
|
+
table: string;
|
|
14
|
+
columns: string[];
|
|
15
|
+
contentTable?: string;
|
|
16
|
+
tokenizer?: string;
|
|
17
|
+
}): string;
|
|
18
|
+
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
19
|
+
rankExpression(indexName: string, _queryPlaceholder?: string): string;
|
|
20
|
+
relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
|
|
21
|
+
rebuildCommand(indexName: string): string;
|
|
22
|
+
syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
|
|
23
|
+
sanitizeQuery(input: string): string;
|
|
24
|
+
joinClause(contentTable: string, contentAlias: string, ftsAlias: string, indexName: string): string;
|
|
25
|
+
/** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
|
|
26
|
+
private tokenizer;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=SqliteFts4.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteFts4.d.ts","sourceRoot":"","sources":["../../src/fts/SqliteFts4.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;;;;GAQG;AACH,qBAAa,UAAW,YAAW,eAAe;IAChD,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAO5G,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAK3E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAInG,gEAAgE;IAChE,OAAO,CAAC,SAAS;CAMlB"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite FTS4 full-text search, for sql.js and the IndexedDB adapter: the
|
|
3
|
+
* stock sql.js build compiles SQLite with FTS3 and FTS4 and without FTS5.
|
|
4
|
+
*
|
|
5
|
+
* Uses FTS4 virtual tables with external content, the porter, unicode61 or
|
|
6
|
+
* simple tokenizer, and rowid joins to the content table. FTS4 has no rank
|
|
7
|
+
* column; relevanceOrder orders by the length of `offsets()`, which grows with
|
|
8
|
+
* the number of matching terms, so the best match comes first.
|
|
9
|
+
*/
|
|
10
|
+
export class SqliteFts4 {
|
|
11
|
+
createIndex(config) {
|
|
12
|
+
const parts = [...config.columns];
|
|
13
|
+
if (config.contentTable)
|
|
14
|
+
parts.push(`content="${config.contentTable}"`);
|
|
15
|
+
parts.push(`tokenize=${this.tokenizer(config.tokenizer)}`);
|
|
16
|
+
return `CREATE VIRTUAL TABLE IF NOT EXISTS ${config.table} USING fts4(${parts.join(', ')})`;
|
|
17
|
+
}
|
|
18
|
+
matchClause(indexName, queryPlaceholder) {
|
|
19
|
+
return `${indexName} MATCH ${queryPlaceholder}`;
|
|
20
|
+
}
|
|
21
|
+
rankExpression(indexName, _queryPlaceholder) {
|
|
22
|
+
return `length(offsets(${indexName}))`;
|
|
23
|
+
}
|
|
24
|
+
relevanceOrder(indexName, _queryPlaceholder) {
|
|
25
|
+
return `length(offsets(${indexName})) DESC`;
|
|
26
|
+
}
|
|
27
|
+
rebuildCommand(indexName) {
|
|
28
|
+
return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
|
|
29
|
+
}
|
|
30
|
+
syncInsert(indexName, rowIdExpr, columns) {
|
|
31
|
+
const placeholders = columns.map(() => '?').join(', ');
|
|
32
|
+
return `INSERT INTO ${indexName} (docid, ${columns.join(', ')}) VALUES (${rowIdExpr}, ${placeholders})`;
|
|
33
|
+
}
|
|
34
|
+
sanitizeQuery(input) {
|
|
35
|
+
return input
|
|
36
|
+
.replace(/[*()":^~{}[\]\\]/g, '')
|
|
37
|
+
.split(/\s+/)
|
|
38
|
+
.filter((word) => word.length > 0)
|
|
39
|
+
.map((word) => `"${word}"`)
|
|
40
|
+
.join(' OR ');
|
|
41
|
+
}
|
|
42
|
+
joinClause(contentTable, contentAlias, ftsAlias, indexName) {
|
|
43
|
+
return `${indexName} ${ftsAlias} JOIN ${contentTable} ${contentAlias} ON ${contentAlias}.rowid = ${ftsAlias}.docid`;
|
|
44
|
+
}
|
|
45
|
+
/** An FTS5-style tokenizer string as an FTS4 tokenizer name. */
|
|
46
|
+
tokenizer(tokenizer) {
|
|
47
|
+
if (!tokenizer)
|
|
48
|
+
return 'unicode61';
|
|
49
|
+
if (tokenizer.includes('porter'))
|
|
50
|
+
return 'porter';
|
|
51
|
+
if (tokenizer.includes('unicode61'))
|
|
52
|
+
return 'unicode61';
|
|
53
|
+
return 'simple';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=SqliteFts4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteFts4.js","sourceRoot":"","sources":["../../src/fts/SqliteFts4.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,OAAO,UAAU;IACrB,WAAW,CAAC,MAAuF;QACjG,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,YAAY;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC3D,OAAO,sCAAsC,MAAM,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9F,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,gBAAwB;QACrD,OAAO,GAAG,SAAS,UAAU,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,kBAAkB,SAAS,IAAI,CAAC;IACzC,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,kBAAkB,SAAS,SAAS,CAAC;IAC9C,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,eAAe,SAAS,IAAI,SAAS,qBAAqB,CAAC;IACpE,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,SAAiB,EAAE,OAAiB;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,eAAe,SAAS,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,YAAY,GAAG,CAAC;IAC1G,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,KAAK;aACT,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;aAChC,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACjC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC;aAC1B,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,YAAoB,EAAE,YAAoB,EAAE,QAAgB,EAAE,SAAiB;QACxF,OAAO,GAAG,SAAS,IAAI,QAAQ,SAAS,YAAY,IAAI,YAAY,OAAO,YAAY,YAAY,QAAQ,QAAQ,CAAC;IACtH,CAAC;IAED,gEAAgE;IACxD,SAAS,CAAC,SAAkB;QAClC,IAAI,CAAC,SAAS;YAAE,OAAO,WAAW,CAAC;QACnC,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,OAAO,QAAQ,CAAC;QAClD,IAAI,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAO,WAAW,CAAC;QACxD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
package/dist/fts/SqliteFts5.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare class SqliteFts5 implements IFullTextSearch {
|
|
|
14
14
|
}): string;
|
|
15
15
|
matchClause(indexName: string, queryPlaceholder: string): string;
|
|
16
16
|
rankExpression(indexName: string, _queryPlaceholder?: string): string;
|
|
17
|
+
relevanceOrder(indexName: string, _queryPlaceholder?: string): string;
|
|
17
18
|
rebuildCommand(indexName: string): string;
|
|
18
19
|
syncInsert(indexName: string, rowIdExpr: string, columns: string[]): string;
|
|
19
20
|
sanitizeQuery(input: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteFts5.d.ts","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,eAAe;IAChD,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAY5G,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAK3E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;CAGpG"}
|
|
1
|
+
{"version":3,"file":"SqliteFts5.d.ts","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,eAAe;IAChD,WAAW,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAY5G,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM;IAIhE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIzC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAK3E,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IASpC,UAAU,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;CAGpG"}
|
package/dist/fts/SqliteFts5.js
CHANGED
|
@@ -22,6 +22,9 @@ export class SqliteFts5 {
|
|
|
22
22
|
rankExpression(indexName, _queryPlaceholder) {
|
|
23
23
|
return `${indexName}.rank`;
|
|
24
24
|
}
|
|
25
|
+
relevanceOrder(indexName, _queryPlaceholder) {
|
|
26
|
+
return `${indexName}.rank`;
|
|
27
|
+
}
|
|
25
28
|
rebuildCommand(indexName) {
|
|
26
29
|
return `INSERT INTO ${indexName}(${indexName}) VALUES('rebuild')`;
|
|
27
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteFts5.js","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACrB,WAAW,CAAC,MAAuF;QACjG,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,sCAAsC,MAAM,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9F,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,gBAAwB;QACrD,OAAO,GAAG,SAAS,UAAU,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,GAAG,SAAS,OAAO,CAAC;IAC7B,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,eAAe,SAAS,IAAI,SAAS,qBAAqB,CAAC;IACpE,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,SAAiB,EAAE,OAAiB;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,eAAe,SAAS,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,YAAY,GAAG,CAAC;IAC1G,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,KAAK;aACT,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;aAChC,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,YAAoB,EAAE,YAAoB,EAAE,QAAgB,EAAE,SAAiB;QACxF,OAAO,GAAG,SAAS,IAAI,QAAQ,SAAS,YAAY,IAAI,YAAY,OAAO,YAAY,YAAY,QAAQ,QAAQ,CAAC;IACtH,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"SqliteFts5.js","sourceRoot":"","sources":["../../src/fts/SqliteFts5.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,UAAU;IACrB,WAAW,CAAC,MAAuF;QACjG,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,sCAAsC,MAAM,CAAC,KAAK,eAAe,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9F,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,gBAAwB;QACrD,OAAO,GAAG,SAAS,UAAU,gBAAgB,EAAE,CAAC;IAClD,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,GAAG,SAAS,OAAO,CAAC;IAC7B,CAAC;IAED,cAAc,CAAC,SAAiB,EAAE,iBAA0B;QAC1D,OAAO,GAAG,SAAS,OAAO,CAAC;IAC7B,CAAC;IAED,cAAc,CAAC,SAAiB;QAC9B,OAAO,eAAe,SAAS,IAAI,SAAS,qBAAqB,CAAC;IACpE,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,SAAiB,EAAE,OAAiB;QAChE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,OAAO,eAAe,SAAS,YAAY,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,SAAS,KAAK,YAAY,GAAG,CAAC;IAC1G,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,OAAO,KAAK;aACT,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;aAChC,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;aAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,YAAoB,EAAE,YAAoB,EAAE,QAAgB,EAAE,SAAiB;QACxF,OAAO,GAAG,SAAS,IAAI,QAAQ,SAAS,YAAY,IAAI,YAAY,OAAO,YAAY,YAAY,QAAQ,QAAQ,CAAC;IACtH,CAAC;CACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export { segmentFilePath, segmentName, validateSegment } from './shared/segment.
|
|
|
20
20
|
export { SqliteDialect } from './dialects/SqliteDialect.js';
|
|
21
21
|
export { PostgresDialect } from './dialects/PostgresDialect.js';
|
|
22
22
|
export { SqliteFts5 } from './fts/SqliteFts5.js';
|
|
23
|
+
export { SqliteFts4 } from './fts/SqliteFts4.js';
|
|
23
24
|
export { PostgresFts } from './fts/PostgresFts.js';
|
|
24
25
|
export { NodeBlobCodec } from './codecs/NodeBlobCodec.js';
|
|
25
26
|
export { BrowserBlobCodec } from './codecs/BrowserBlobCodec.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAQ9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAM5C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AAGxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAQ9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAM5C,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAMjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ export { segmentFilePath, segmentName, validateSegment } from './shared/segment.
|
|
|
34
34
|
export { SqliteDialect } from './dialects/SqliteDialect.js';
|
|
35
35
|
export { PostgresDialect } from './dialects/PostgresDialect.js';
|
|
36
36
|
export { SqliteFts5 } from './fts/SqliteFts5.js';
|
|
37
|
+
export { SqliteFts4 } from './fts/SqliteFts4.js';
|
|
37
38
|
export { PostgresFts } from './fts/PostgresFts.js';
|
|
38
39
|
export { NodeBlobCodec } from './codecs/NodeBlobCodec.js';
|
|
39
40
|
export { BrowserBlobCodec } from './codecs/BrowserBlobCodec.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,SAAS,CAAC;AAExB,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,+EAA+E;AAC/E,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAE9C,wEAAwE;AACxE,yDAAyD;AACzD,2HAA2H;AAC3H,mEAAmE;AAEnE,+EAA+E;AAC/E,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAE5C,uFAAuF;AACvF,sFAAsF;AAEtF,+EAA+E;AAC/E,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEjF,6FAA6F;AAC7F,uGAAuG;AAEvG,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,SAAS,CAAC;AAExB,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,+EAA+E;AAC/E,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC;AACpC,cAAc,+BAA+B,CAAC;AAE9C,wEAAwE;AACxE,yDAAyD;AACzD,2HAA2H;AAC3H,mEAAmE;AAEnE,+EAA+E;AAC/E,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAE5C,uFAAuF;AACvF,sFAAsF;AAEtF,+EAA+E;AAC/E,cAAc,yBAAyB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEjF,6FAA6F;AAC7F,uGAAuG;AAEvG,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAsB,MAAM,iBAAiB,CAAC"}
|
package/package.json
CHANGED