fix: proper boundary on the project in the data
This commit is contained in:
@@ -318,36 +318,54 @@ export class DatabaseConnection {
|
||||
}
|
||||
|
||||
// Create FTS5 virtual table for full-text search
|
||||
// Only stores: id (unindexed, for lookups) and content (stemmed text for matching)
|
||||
// Stores: id (unindexed, for lookups), project_id (unindexed, for filtering), content (stemmed text for matching)
|
||||
// Post data for display comes from the posts table or filesystem files
|
||||
await this.localClient.execute(`
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS posts_fts USING fts5(
|
||||
id UNINDEXED,
|
||||
project_id UNINDEXED,
|
||||
content,
|
||||
content_rowid=rowid
|
||||
);
|
||||
`);
|
||||
|
||||
// Migration: Check if old FTS schema (with multiple columns) exists and recreate
|
||||
// Old schema had: id, title, content, excerpt, tags, categories, content_stemmed
|
||||
// New schema has: id, content (stemmed only)
|
||||
// Migration: Check if old FTS schema exists and recreate with project_id
|
||||
// Old schema had: id, content (or even older: id, title, content, excerpt, tags, categories)
|
||||
// New schema has: id, project_id, content (for project-scoped search)
|
||||
try {
|
||||
// Try to query old columns - if they exist, we need to migrate
|
||||
await this.localClient.execute("SELECT title FROM posts_fts LIMIT 0");
|
||||
|
||||
// Old schema exists - recreate with new simple schema
|
||||
console.log('Migrating posts_fts table to simplified schema...');
|
||||
// Try to query project_id - if it doesn't exist, we need to migrate
|
||||
await this.localClient.execute("SELECT project_id FROM posts_fts LIMIT 0");
|
||||
// project_id exists, check for old multi-column schema
|
||||
try {
|
||||
await this.localClient.execute("SELECT title FROM posts_fts LIMIT 0");
|
||||
// Old multi-column schema exists - recreate
|
||||
console.log('Migrating posts_fts table to new schema with project_id...');
|
||||
await this.localClient.execute('DROP TABLE IF EXISTS posts_fts');
|
||||
await this.localClient.execute(`
|
||||
CREATE VIRTUAL TABLE posts_fts USING fts5(
|
||||
id UNINDEXED,
|
||||
project_id UNINDEXED,
|
||||
content,
|
||||
content_rowid=rowid
|
||||
);
|
||||
`);
|
||||
console.log('FTS table migrated - rebuild index required');
|
||||
} catch {
|
||||
// No title column - we have the correct new schema
|
||||
}
|
||||
} catch {
|
||||
// project_id doesn't exist - migrate from old schema
|
||||
console.log('Migrating posts_fts table to add project_id...');
|
||||
await this.localClient.execute('DROP TABLE IF EXISTS posts_fts');
|
||||
await this.localClient.execute(`
|
||||
CREATE VIRTUAL TABLE posts_fts USING fts5(
|
||||
id UNINDEXED,
|
||||
project_id UNINDEXED,
|
||||
content,
|
||||
content_rowid=rowid
|
||||
);
|
||||
`);
|
||||
console.log('FTS table migrated - rebuild index required');
|
||||
} catch {
|
||||
// Old columns don't exist - we have the new schema or no data, all good
|
||||
}
|
||||
|
||||
// Create default project if none exists
|
||||
|
||||
@@ -96,11 +96,13 @@ export class PostEngine extends EventEmitter {
|
||||
/**
|
||||
* Update the FTS index for a post.
|
||||
* Updates the FTS index for a post.
|
||||
* Stores only the stemmed content (combining title, excerpt, content, tags, categories).
|
||||
* Stores the stemmed content (combining title, excerpt, content, tags, categories).
|
||||
* Includes project_id for project-scoped search.
|
||||
* Only the post ID is returned from searches - actual post data comes from DB/files.
|
||||
*/
|
||||
private async updateFTSIndex(post: {
|
||||
id: string;
|
||||
projectId: string;
|
||||
title: string;
|
||||
content: string;
|
||||
excerpt?: string;
|
||||
@@ -124,10 +126,10 @@ export class PostEngine extends EventEmitter {
|
||||
|
||||
const stemmedContent = stemText(allText, this.searchLanguage);
|
||||
|
||||
// Insert with only id and stemmed content
|
||||
// Insert with id, project_id, and stemmed content
|
||||
await client.execute({
|
||||
sql: 'INSERT INTO posts_fts (id, content) VALUES (?, ?)',
|
||||
args: [post.id, stemmedContent],
|
||||
sql: 'INSERT INTO posts_fts (id, project_id, content) VALUES (?, ?, ?)',
|
||||
args: [post.id, post.projectId, stemmedContent],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -670,26 +672,25 @@ export class PostEngine extends EventEmitter {
|
||||
// Stem the query for multilingual matching
|
||||
const stemmedQuery = stemQuery(query, this.searchLanguage);
|
||||
|
||||
// Search the stemmed content, only return post IDs
|
||||
// Search the stemmed content, filtered by project_id for project isolation
|
||||
const result = await client.execute({
|
||||
sql: `SELECT id FROM posts_fts WHERE posts_fts MATCH ? ORDER BY rank LIMIT 50`,
|
||||
args: [stemmedQuery],
|
||||
sql: `SELECT id FROM posts_fts WHERE project_id = ? AND posts_fts MATCH ? ORDER BY rank LIMIT 50`,
|
||||
args: [this.currentProjectId, stemmedQuery],
|
||||
});
|
||||
|
||||
// Filter to current project and fetch actual post data
|
||||
const projectPosts = await this.getAllPostsUnpaginated();
|
||||
const projectPostMap = new Map(projectPosts.map(p => [p.id, p]));
|
||||
|
||||
// Fetch actual post data for results
|
||||
const db = getDatabase().getLocal();
|
||||
const searchResults: SearchResult[] = [];
|
||||
|
||||
for (const row of result.rows) {
|
||||
const postId = row.id as string;
|
||||
const post = projectPostMap.get(postId);
|
||||
const post = await db.select().from(posts).where(eq(posts.id, postId)).get();
|
||||
if (post) {
|
||||
searchResults.push({
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
slug: post.slug,
|
||||
excerpt: post.excerpt,
|
||||
excerpt: post.excerpt ?? undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user