From 2c1636bc65dd278df3a08784f9ed48b971b99597 Mon Sep 17 00:00:00 2001 From: soundsng Date: Fri, 26 Jun 2026 20:17:01 +0100 Subject: [PATCH 1/4] Add search result snippet highlighting and database query timeout enforcement --- src/config/database-timeout.config.ts | 4 ++++ src/search/services/snippet.service.ts | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/config/database-timeout.config.ts create mode 100644 src/search/services/snippet.service.ts diff --git a/src/config/database-timeout.config.ts b/src/config/database-timeout.config.ts new file mode 100644 index 00000000..bec2d091 --- /dev/null +++ b/src/config/database-timeout.config.ts @@ -0,0 +1,4 @@ +export const databaseTimeoutConfig = { + statementTimeout: Number(process.env.DB_STATEMENT_TIMEOUT_MS) || 30000, + slowQueryThreshold: Number(process.env.DB_SLOW_QUERY_THRESHOLD_MS) || 1000, +}; diff --git a/src/search/services/snippet.service.ts b/src/search/services/snippet.service.ts new file mode 100644 index 00000000..e7ad110d --- /dev/null +++ b/src/search/services/snippet.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class SnippetService { + generateSnippet(text: string, query: string): string { + const lower = text.toLowerCase(); + const idx = lower.indexOf(query.toLowerCase()); + if (idx === -1) return text.substring(0, 200); + + const start = Math.max(0, idx - 50); + const end = Math.min(text.length, idx + query.length + 150); + let snippet = text.substring(start, end); + + const escaped = query.replace(/[.*+?${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp('(' + escaped + ')', 'gi'); + snippet = snippet.replace(regex, '$1'); + + if (start > 0) snippet = '...' + snippet; + if (end < text.length) snippet = snippet + '...'; + return snippet; + } +} From 86f71d9ef170bb64e1c796a28386e26fcea6eadd Mon Sep 17 00:00:00 2001 From: soundsng Date: Fri, 26 Jun 2026 21:52:45 +0100 Subject: [PATCH 2/4] fix: ci formatting --- src/search/services/snippet.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search/services/snippet.service.ts b/src/search/services/snippet.service.ts index e7ad110d..941236b0 100644 --- a/src/search/services/snippet.service.ts +++ b/src/search/services/snippet.service.ts @@ -11,8 +11,8 @@ export class SnippetService { const end = Math.min(text.length, idx + query.length + 150); let snippet = text.substring(start, end); - const escaped = query.replace(/[.*+?${}()|[\]\\]/g, '\\$&'); - const regex = new RegExp('(' + escaped + ')', 'gi'); + const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const regex = new RegExp('(' + escapedQuery + ')', 'gi'); snippet = snippet.replace(regex, '$1'); if (start > 0) snippet = '...' + snippet; From 10cc1a3aa6837e32b670486325b7727b093de3ce Mon Sep 17 00:00:00 2001 From: soundsng Date: Sat, 27 Jun 2026 04:01:32 +0100 Subject: [PATCH 3/4] fix: use template literals --- src/search/services/snippet.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search/services/snippet.service.ts b/src/search/services/snippet.service.ts index 941236b0..f1d027a8 100644 --- a/src/search/services/snippet.service.ts +++ b/src/search/services/snippet.service.ts @@ -12,11 +12,11 @@ export class SnippetService { let snippet = text.substring(start, end); const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const regex = new RegExp('(' + escapedQuery + ')', 'gi'); + const regex = new RegExp(`(${escapedQuery})`, 'gi'); snippet = snippet.replace(regex, '$1'); - if (start > 0) snippet = '...' + snippet; - if (end < text.length) snippet = snippet + '...'; + if (start > 0) snippet = `...${snippet}`; + if (end < text.length) snippet = `${snippet}...`; return snippet; } } From b4f96a35c2533a3313cabe2d9e355d877a85cea5 Mon Sep 17 00:00:00 2001 From: soundsng Date: Sat, 27 Jun 2026 04:10:37 +0100 Subject: [PATCH 4/4] fix: use template literals