@dokebi/search 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI.md +134 -0
- package/LICENSE +21 -0
- package/README.md +8 -2
- package/dist/api.d.ts +78 -0
- package/dist/api.js +1 -0
- package/dist/cursor.d.ts +19 -0
- package/dist/cursor.js +68 -0
- package/dist/dependency.d.ts +4 -0
- package/dist/dependency.js +13 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +58 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/dist/indexes.d.ts +27 -0
- package/dist/indexes.js +87 -0
- package/dist/install.d.ts +37 -0
- package/dist/install.js +56 -0
- package/dist/options.d.ts +23 -0
- package/dist/options.js +73 -0
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +708 -0
- package/dist/sql.d.ts +37 -0
- package/dist/sql.js +114 -0
- package/dist/store.d.ts +77 -0
- package/dist/store.js +164 -0
- package/dist/terms.d.ts +31 -0
- package/dist/terms.js +58 -0
- package/options.schema.json +95 -0
- package/package.json +86 -4
- package/templates/indexes.ts +10 -0
- package/templates/migrations/dokebi_search.ts +92 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The search indexes of this application (@dokebi/search, D-0170): each index has the fields a
|
|
2
|
+
// list searches, and each field its term rules. `prefix` is the longest word prefix indexed
|
|
3
|
+
// (default 10, so a search for a stem finds the words that start with it, Korean particles
|
|
4
|
+
// included); 0 indexes whole words only. Replace the example with your own lists. After changing
|
|
5
|
+
// a field's rules, index its rows again and call search.markCurrent(<index>).
|
|
6
|
+
import { defineSearchIndexes } from '@dokebi/search';
|
|
7
|
+
|
|
8
|
+
export default defineSearchIndexes({
|
|
9
|
+
posts: { fields: { title: {}, body: {}, author: { prefix: 0 } } },
|
|
10
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// The tables of @dokebi/search (D-0170): written by `dokebi add @dokebi/search` into the
|
|
2
|
+
// migrations folder of options.dokebi-search.connection (default main). Run it with
|
|
3
|
+
// `npx dokebi-migrate up`; the app never creates tables at start. Works on PostgreSQL (pg),
|
|
4
|
+
// MySQL 8 (mysql2) and MariaDB 10.6+ (mariadb): the driver is told apart by the shape of one
|
|
5
|
+
// SELECT answer. The tables are new, so creating them locks nothing that runs. Edit only before
|
|
6
|
+
// it was applied.
|
|
7
|
+
//
|
|
8
|
+
// Every search reads the primary key of dokebi_search_terms backwards: an equality on
|
|
9
|
+
// (index_name, field, term, scope), then the display order (sort_key, item_id). Words, scopes
|
|
10
|
+
// and ids compare byte by byte (the plugin normalises the words).
|
|
11
|
+
|
|
12
|
+
interface Db {
|
|
13
|
+
query(sql: string, values?: unknown[]): Promise<unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TERMS = 'dokebi_search_terms';
|
|
17
|
+
const COUNTS = 'dokebi_search_counts';
|
|
18
|
+
const INDEXES = 'dokebi_search_indexes';
|
|
19
|
+
|
|
20
|
+
const POSTGRES = [
|
|
21
|
+
`CREATE TABLE IF NOT EXISTS ${TERMS} (
|
|
22
|
+
index_name varchar(40) COLLATE "C" NOT NULL,
|
|
23
|
+
field varchar(40) COLLATE "C" NOT NULL,
|
|
24
|
+
term varchar(64) COLLATE "C" NOT NULL,
|
|
25
|
+
scope varchar(64) COLLATE "C" NOT NULL DEFAULT '',
|
|
26
|
+
sort_key bigint NOT NULL,
|
|
27
|
+
item_id varchar(128) COLLATE "C" NOT NULL,
|
|
28
|
+
PRIMARY KEY (index_name, field, term, scope, sort_key, item_id)
|
|
29
|
+
)`,
|
|
30
|
+
`CREATE INDEX IF NOT EXISTS ${TERMS}_item ON ${TERMS} (index_name, item_id)`,
|
|
31
|
+
`CREATE TABLE IF NOT EXISTS ${COUNTS} (
|
|
32
|
+
index_name varchar(40) COLLATE "C" NOT NULL,
|
|
33
|
+
field varchar(40) COLLATE "C" NOT NULL,
|
|
34
|
+
term varchar(64) COLLATE "C" NOT NULL,
|
|
35
|
+
slot smallint NOT NULL,
|
|
36
|
+
count bigint NOT NULL,
|
|
37
|
+
PRIMARY KEY (index_name, field, term, slot)
|
|
38
|
+
)`,
|
|
39
|
+
`CREATE TABLE IF NOT EXISTS ${INDEXES} (
|
|
40
|
+
name varchar(40) COLLATE "C" PRIMARY KEY,
|
|
41
|
+
fingerprint varchar(64) NOT NULL,
|
|
42
|
+
updated_at bigint NOT NULL
|
|
43
|
+
)`,
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
// MySQL and MariaDB: binary collations, so a word matches only itself (the default collation
|
|
47
|
+
// would match other letter cases and accents) and the order is the byte order.
|
|
48
|
+
const BIN = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_bin';
|
|
49
|
+
const ASCII = 'CHARACTER SET ascii COLLATE ascii_bin';
|
|
50
|
+
const MYSQL = [
|
|
51
|
+
`CREATE TABLE IF NOT EXISTS ${TERMS} (
|
|
52
|
+
index_name VARCHAR(40) ${ASCII} NOT NULL,
|
|
53
|
+
field VARCHAR(40) ${ASCII} NOT NULL,
|
|
54
|
+
term VARCHAR(64) ${BIN} NOT NULL,
|
|
55
|
+
scope VARCHAR(64) ${BIN} NOT NULL DEFAULT '',
|
|
56
|
+
sort_key BIGINT NOT NULL,
|
|
57
|
+
item_id VARCHAR(128) ${BIN} NOT NULL,
|
|
58
|
+
PRIMARY KEY (index_name, field, term, scope, sort_key, item_id),
|
|
59
|
+
KEY ${TERMS}_item (index_name, item_id)
|
|
60
|
+
) ENGINE=InnoDB`,
|
|
61
|
+
`CREATE TABLE IF NOT EXISTS ${COUNTS} (
|
|
62
|
+
index_name VARCHAR(40) ${ASCII} NOT NULL,
|
|
63
|
+
field VARCHAR(40) ${ASCII} NOT NULL,
|
|
64
|
+
term VARCHAR(64) ${BIN} NOT NULL,
|
|
65
|
+
slot SMALLINT NOT NULL,
|
|
66
|
+
count BIGINT NOT NULL,
|
|
67
|
+
PRIMARY KEY (index_name, field, term, slot)
|
|
68
|
+
) ENGINE=InnoDB`,
|
|
69
|
+
`CREATE TABLE IF NOT EXISTS ${INDEXES} (
|
|
70
|
+
name VARCHAR(40) ${ASCII} NOT NULL PRIMARY KEY,
|
|
71
|
+
fingerprint VARCHAR(64) ${ASCII} NOT NULL,
|
|
72
|
+
updated_at BIGINT NOT NULL
|
|
73
|
+
) ENGINE=InnoDB`,
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
/** pg answers `{ rows }`; mysql2 and mariadb answer arrays. */
|
|
77
|
+
async function statements(db: Db): Promise<string[]> {
|
|
78
|
+
const probe = await db.query('SELECT 1 AS one');
|
|
79
|
+
const postgres =
|
|
80
|
+
probe !== null &&
|
|
81
|
+
typeof probe === 'object' &&
|
|
82
|
+
Array.isArray((probe as { rows?: unknown }).rows);
|
|
83
|
+
return postgres ? POSTGRES : MYSQL;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function up(db: Db): Promise<void> {
|
|
87
|
+
for (const sql of await statements(db)) await db.query(sql);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function down(db: Db): Promise<void> {
|
|
91
|
+
for (const table of [INDEXES, COUNTS, TERMS]) await db.query(`DROP TABLE IF EXISTS ${table}`);
|
|
92
|
+
}
|