Änderungen in der Suche

This commit is contained in:
2026-03-11 17:24:14 +01:00
parent 58325e8583
commit dcfadb51d3
5 changed files with 136 additions and 25 deletions

View File

@@ -21,7 +21,7 @@ const TEILE_SEARCH_COLUMNS = [
];
// Ausgabe-Spalten für Suchergebnisse (siehe Spalten.txt)
// Text aus TEXT (TEILE.MemoID = TEXT.TextId), PrsVK + Ersatz aus TSSAEF (Teil = Teil)
// Text aus TEXT: MemoID aus EKATSS (EKATSS.TEIL = Teil), dann TEXT.TextId = MemoID; PrsVK + Ersatz aus TSSAEF (Teil = Teil)
const TEILE_OUTPUT_COLUMNS = [
'Teil',
'Bez',
@@ -68,9 +68,10 @@ const buildSingleWordCondition = (columns, paramName, tableAlias = '') => {
* - Optimierte Query-Struktur mit TOP für bessere Performance
*
* @param {string} searchTerm - Der zu suchende Begriff (mehrere Wörter möglich, getrennt durch Leerzeichen)
* @param {string} [statusFilter] - Optional: 'aktiv' | 'pruefbar' | 'inaktiv' (leer/undefined = alle)
* @returns {Promise<Array>} Array mit Suchergebnissen
*/
const fullTextSearch = async (searchTerm) => {
const fullTextSearch = async (searchTerm, statusFilter) => {
if (!searchTerm || searchTerm.trim() === '') {
throw new Error('Suchbegriff darf nicht leer sein');
}
@@ -82,16 +83,32 @@ const fullTextSearch = async (searchTerm) => {
const pool = await getConnection();
// TEILE: Feste 8 Spalten mit JOINs auf TEXT (MemoID=TextId) und TSSAEF (Teil)
// TEILE: Ausgabe-Spalten mit JOINs auf EKATSS (MemoID), TEXT (TextId=MemoID), TSSAEF (Teil)
const searchPromises = SEARCH_CONFIG.map(async (tableConfig) => {
try {
// Pro Wort: (Spalte1 LIKE @searchWord0 OR ... OR SpalteN LIKE @searchWord0), alle mit AND verknüpft
// Pro Wort: Treffer in TEILE-Spalten ODER im Zusatztext (TEXT über EKATSS); alle Wörter mit AND
const wordConditions = words.map((_, i) => {
const paramName = `searchWord${i}`;
const cond = buildSingleWordCondition(tableConfig.columns, paramName, 't');
return `(${cond})`;
const teileCond = buildSingleWordCondition(tableConfig.columns, paramName, 't');
const textCond = `EXISTS (
SELECT 1
FROM EKATSS ek WITH (NOLOCK)
INNER JOIN [TEXT] tx WITH (NOLOCK) ON tx.TextId = ek.MemoID
WHERE ek.[Teil] = t.Teil
AND tx.[Text] LIKE @${paramName} COLLATE SQL_Latin1_General_CP1_CI_AS
)`;
return `(${teileCond} OR ${textCond})`;
});
const whereClause = wordConditions.join(' AND ');
let whereClause = wordConditions.join(' AND ');
// Status-Filter: aktiv = leer/NULL, pruefbar = L, inaktiv = i
if (statusFilter === 'aktiv') {
whereClause += ` AND (t.Stat IS NULL OR LTRIM(RTRIM(ISNULL(t.Stat, N''))) = N'')`;
} else if (statusFilter === 'pruefbar') {
whereClause += ` AND t.Stat = @statusFilter`;
} else if (statusFilter === 'inaktiv') {
whereClause += ` AND t.Stat = @statusFilter`;
}
const query = `
SELECT TOP 100
@@ -102,15 +119,22 @@ const fullTextSearch = async (searchTerm) => {
t.Ben8,
t.Hersteller,
t.Stat,
t.VkTeil,
txt.Text AS [Text],
ts.PrsVk AS PrsVK,
ts.Ersatz AS Ersatz
FROM [${tableConfig.tableName}] t WITH (NOLOCK)
OUTER APPLY (
SELECT TOP 1 [Text]
FROM [TEXT] WITH (NOLOCK)
WHERE TextId = COALESCE(NULLIF(RTRIM(LTRIM(t.MemoID)), N''), N'MEM-' + t.Teil)
ORDER BY LfdNr
SELECT TOP 1 MemoID
FROM EKATSS WITH (NOLOCK)
WHERE [Teil] = t.Teil
ORDER BY (SELECT NULL)
) ek
OUTER APPLY (
SELECT
STRING_AGG(tx.[Text], CHAR(10)) WITHIN GROUP (ORDER BY tx.LfdNr) AS [Text]
FROM [TEXT] tx WITH (NOLOCK)
WHERE tx.TextId = ek.MemoID
) txt
OUTER APPLY (
SELECT TOP 1 PrsVk, Ersatz
@@ -125,6 +149,11 @@ const fullTextSearch = async (searchTerm) => {
words.forEach((word, i) => {
request.input(`searchWord${i}`, sql.NVarChar, `%${word}%`);
});
if (statusFilter === 'pruefbar') {
request.input('statusFilter', sql.NVarChar(10), 'L');
} else if (statusFilter === 'inaktiv') {
request.input('statusFilter', sql.NVarChar(10), 'i');
}
const result = await request.query(query);