@@ -166,3 +166,68 @@ result is **386 PASS / 0 FAIL / 22 SKIP**. The setup, reproduction
166166recipe, and reasoning for each shim flag live in
167167[ gorm-upstream.md] ( gorm-upstream.md ) . The same recipe is enforced by
168168the ` gorm-upstream ` CI job.
169+
170+ ## Deep integration: ` vec/gorm ` and ` fts/gorm `
171+
172+ Tag-driven sidecar packages live under ` github.com/go-again/sqlite/vec/gorm `
173+ and ` github.com/go-again/sqlite/fts/gorm ` . They register as gorm
174+ plugins and own the full lifecycle of the sidecar (vec0 virtual table /
175+ FTS5 external-content table + triggers).
176+
177+ ### Tag syntax — vec
178+
179+ | Key | Required | Meaning |
180+ | ---| ---| ---|
181+ | ` dim=N ` | yes | Embedding dimension. |
182+ | ` metric=l2 \| cosine \| dot ` | no | Distance metric. Default ` l2 ` . |
183+ | ` encoding=json \| binary ` | no | Wire encoding. Default ` binary ` . |
184+ | ` table=NAME ` | no | Override sidecar table name. Default ` <source>_vec ` . |
185+ | ` column=NAME ` | no | Override embedding column. Default ` embedding ` . |
186+
187+ The tagged field's type must be either ` vecgorm.Embedding `
188+ (recommended) or ` []float32 ` with ` gorm:"-" ` alongside. The wrapper
189+ type implements gorm's ` GormDataType ` interface so the schema parser
190+ accepts it; the plugin then sets ` IgnoreMigration=true ` so no column
191+ lands on the source table.
192+
193+ ### Tag syntax — fts5
194+
195+ | Key | Required | Meaning |
196+ | ---| ---| ---|
197+ | ` tokenize=NAME[+args] ` | no | FTS5 tokenize option. Spaces escaped as ` + ` . |
198+ | ` prefix=N1,N2,... ` | no | Pre-computed prefix-match index sizes. |
199+ | ` column=NAME ` | no | Override FTS5 column name (default = lowercase field). |
200+ | ` table=NAME ` | no | Override FTS5 table. Default ` <source>_fts ` . |
201+ | ` detail=full \| column \| none ` | no | FTS5 detail= option. |
202+ | ` external=true \| false ` | no | External-content mode (default true). false → in-table FTS5 manages text itself. |
203+ | ` contentless=true ` | no | Contentless FTS5 (index only, no text). Snippet/highlight are rejected at search time. Mutually exclusive with ` external=true ` . |
204+
205+ Multiple ` fts5: ` -tagged fields on one model share ** one** FTS5 table.
206+ Conflicting table-level keys across fields are rejected at parse time.
207+
208+ ### Lifecycle matrix
209+
210+ | Event | vec/gorm behavior | fts/gorm behavior |
211+ | ---| ---| ---|
212+ | Plugin install | ` db.Use(vecgorm.Plugin()) ` | ` db.Use(ftsgorm.Plugin()) ` |
213+ | AutoMigrate | ` vecgorm.Migrate(db, &T{}) ` creates source + sidecar | ` ftsgorm.Migrate(db, &T{}) ` creates source + FTS5 table + triggers |
214+ | Create | AfterCreate callback ` BatchInsert ` (single tx) | AFTER INSERT trigger writes to FTS5 |
215+ | Save/Update | AfterUpdate callback ` (*vec.Table).Update ` | AFTER UPDATE trigger refreshes index |
216+ | Delete (hard) | AfterDelete callback ` (*vec.Table).Delete ` | AFTER DELETE trigger emits FTS5 ` 'delete' ` |
217+ | Delete (soft, via ` gorm.DeletedAt ` ) | Sidecar ` deleted ` flag flipped to 1 | FTS5's UNINDEXED ` deleted_at ` mirror set by trigger |
218+ | KNN / Search | Soft-deleted excluded by default; ` IncludeDeleted() ` overrides | Same |
219+ | DropSidecar | Drops sidecar table | Drops FTS5 table + all three triggers (for external mode) |
220+ | Source DropTable | Cascades into sidecar via DropTableHook on our gorm Dialector | Cascades into FTS5 table + triggers |
221+ | dim mismatch on re-migrate | Logged warning, existing sidecar left alone | n/a |
222+
223+ ### Tests
224+
225+ | File | Tests | Notes |
226+ | ---| ---| ---|
227+ | ` vec/gorm/vecgorm_test.go ` | 12 | Basic create/update/delete, KNN ranking, BatchInsert single-tx, soft-delete, Embedding wrapper |
228+ | ` vec/gorm/lifecycle_test.go ` | 9 | DropTable cascade, DropSidecar, composite PK rejection, tag validation, WithFilter, dim mismatch |
229+ | ` fts/gorm/ftsgorm_test.go ` | 10 | Migrate creates index + triggers, search/snippet/highlight, ranking, soft-delete, backfill |
230+ | ` fts/gorm/lifecycle_test.go ` | 8 | Conflicting tags, non-string fields, composite PK, LIMIT/OFFSET, no-plugin error, DropTable cascade |
231+ | ` fts/gorm/mode_test.go ` | 7 | external/in-table/contentless modes, conflicting modes rejected, contentless rejects snippet, in-table soft-delete |
232+
233+ 46 tests total, all passing on linux/macos.
0 commit comments