feat(v0211): rebuildFtsTagsForNote 헬퍼 + tags 변경 path 통합 (single write)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
altair823
2026-05-10 00:19:14 +09:00
parent 19edeab7b1
commit 726d155d04
2 changed files with 49 additions and 0 deletions

View File

@@ -161,6 +161,7 @@ export class NoteRepository {
linkTag.run(id, tagRow.id);
}
this.db.prepare(`DELETE FROM pending_jobs WHERE note_id=?`).run(id);
this.rebuildFtsTagsForNote(id);
});
tx();
}
@@ -390,6 +391,7 @@ export class NoteRepository {
const row = getOrInsert.get(t) as { id: number };
link.run(id, row.id);
}
this.rebuildFtsTagsForNote(id);
}
});
tx();
@@ -851,6 +853,23 @@ export class NoteRepository {
.run(nextRunAt, lastError.slice(0, 500), noteId);
}
/**
* v0.2.11 Cut D — note_tags 변경 후 notes_fts.tags 컬럼 (csv) 재구성.
* 단일 write path 패턴: tags 변경하는 모든 메서드가 같은 transaction 끝에서 호출.
*/
private rebuildFtsTagsForNote(noteId: string): void {
const row = this.db
.prepare(
`SELECT COALESCE(GROUP_CONCAT(t.name, ' '), '') AS csv
FROM note_tags nt JOIN tags t ON t.id = nt.tag_id
WHERE nt.note_id = ?`
)
.get(noteId) as { csv: string };
this.db
.prepare(`UPDATE notes_fts SET tags = ? WHERE note_id = ?`)
.run(row.csv, noteId);
}
private hydrate(row: Record<string, unknown>): Note {
const tags = this.db
.prepare(

View File

@@ -1074,3 +1074,33 @@ describe('NoteRepository — note_revisions', () => {
expect(rows[0]).toEqual({ raw_text: 'hello', edited_by: 'capture' });
});
});
describe('NoteRepository — notes_fts tags sync (v0.2.11 Cut D)', () => {
let db: Database.Database;
beforeEach(() => {
db = new Database(':memory:');
runMigrations(db);
});
it('updateAiResult 후 notes_fts.tags 가 csv 로 sync', () => {
const repo = new NoteRepository(db);
const { id } = repo.create({ rawText: '회의 본문' });
repo.updateAiResult(id, { title: '제목', summary: '요약', tags: ['기획', '회의'], provider: 'p' });
const row = db
.prepare(`SELECT tags FROM notes_fts WHERE note_id=?`)
.get(id) as { tags: string };
expect(row.tags.split(' ').sort()).toEqual(['기획', '회의']);
});
it('updateUserAiFields tags 갱신 후 notes_fts.tags 동기', () => {
const repo = new NoteRepository(db);
const { id } = repo.create({ rawText: '본문' });
repo.updateAiResult(id, { title: 't', summary: 's', tags: ['old'], provider: 'p' });
repo.updateUserAiFields(id, { tags: ['new1', 'new2'] });
const row = db
.prepare(`SELECT tags FROM notes_fts WHERE note_id=?`)
.get(id) as { tags: string };
expect(row.tags.split(' ').sort()).toEqual(['new1', 'new2']);
});
});