diff --git a/apps/server/src/becca/entities/battribute.ts b/apps/server/src/becca/entities/battribute.ts index 77a15c2fd1..dbb6502113 100644 --- a/apps/server/src/becca/entities/battribute.ts +++ b/apps/server/src/becca/entities/battribute.ts @@ -202,6 +202,11 @@ class BAttribute extends AbstractBeccaEntity { this.utcDateModified = dateUtils.utcNowDateTime(); + // Recompute normalized fields in case name/value were modified directly + // (e.g., attr.value = "..." followed by attr.save()) + this.normalizedName = normalize(this.name); + this.normalizedValue = normalize(this.value); + super.beforeSaving(); this.becca.attributes[this.attributeId] = this; diff --git a/apps/server/src/services/search/services/search.ts b/apps/server/src/services/search/services/search.ts index ea1d20c263..0523aeb98f 100644 --- a/apps/server/src/services/search/services/search.ts +++ b/apps/server/src/services/search/services/search.ts @@ -595,10 +595,11 @@ function extractAttributeSnippet(noteId: string, searchTokens: string[], maxLeng // Look for attributes that match the search tokens for (const attr of attributes) { - const attrName = attr.name?.toLowerCase() || ""; - const attrValue = attr.value?.toLowerCase() || ""; + // Use pre-normalized fields from BAttribute for diacritic-insensitive matching + const attrName = attr.normalizedName || normalize(attr.name || ""); + const attrValue = attr.normalizedValue || normalize(attr.value || ""); const attrType = attr.type || ""; - + // Check if any search token matches the attribute name or value const hasMatch = searchTokens.some(token => { const normalizedToken = normalize(token); diff --git a/apps/server/src/services/search/utils/text_utils.ts b/apps/server/src/services/search/utils/text_utils.ts index 7528571f86..1993924555 100644 --- a/apps/server/src/services/search/utils/text_utils.ts +++ b/apps/server/src/services/search/utils/text_utils.ts @@ -282,7 +282,9 @@ export function fuzzyMatchWordWithResult(token: string, text: string, maxDistanc // Exact match check first (most common case) if (normalizedText.includes(normalizedToken)) { - return token; + // Find the exact match position and return the original substring with case preserved + const matchIndex = normalizedText.indexOf(normalizedToken); + return text.substring(matchIndex, matchIndex + normalizedToken.length); } // For fuzzy matching, split into words and check each against the token