Skip to content

Commit 053eddc

Browse files
committed
fix(search): keep two-letter terms whole and capitalized acronyms ranked
singularKey folded two-letter words to one letter (OS to o, IE to i), so they matched unrelated single letters; words of two letters or fewer are now their own key. A two-letter word written in capitals (IT, US) is an acronym, so it is no longer dropped as a stopword.
1 parent f52d593 commit 053eddc

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

‎apps/sim/lib/sim-search/live/text-match.test.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
confluenceTextClause,
44
holdsHalfTheTerms,
55
jiraTextClause,
6+
queryRelevance,
67
} from '@/lib/sim-search/live/text-match'
78

89
describe('text match clauses', () => {
@@ -92,4 +93,17 @@ describe('text match clauses', () => {
9293
expect(holdsHalfTheTerms('contractor offboarding')('Contractor directory')).toBe(true)
9394
})
9495
})
96+
97+
describe('queryRelevance', () => {
98+
it('never folds a two-letter term to a single letter', () => {
99+
const relevance = queryRelevance('OS performance')
100+
expect(relevance('OS performance reference guide', '')).toEqual([2, 2, 0.5])
101+
expect(relevance('O(n) performance', '')).toEqual([1, 1, 1 / 3])
102+
})
103+
104+
it('ranks a two-letter acronym in capitals but not the same word in lowercase', () => {
105+
expect(queryRelevance('IT onboarding')('IT onboarding', '')).toEqual([2, 2, 1])
106+
expect(queryRelevance('reset it')('Reset it', '')).toEqual([1, 1, 0.5])
107+
})
108+
})
95109
})

‎apps/sim/lib/sim-search/live/text-match.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ export function significantTerms(query: string): string[] {
9090
return distinctTerms(query, (term) => term.toLowerCase())
9191
}
9292

93+
/** A two-letter word written in capitals, such as `IT` or `US`, is an acronym, not a stopword. */
94+
const isStopword = (term: string) =>
95+
STOPWORDS.has(term.toLowerCase()) && !(term.length === 2 && term === term.toUpperCase())
96+
9397
function distinctTerms(
9498
query: string,
9599
keyOf: (term: string) => string,
@@ -99,7 +103,7 @@ function distinctTerms(
99103
return queryTerms(query, minLength)
100104
.filter((term) => {
101105
const key = keyOf(term)
102-
if (STOPWORDS.has(term.toLowerCase()) || seen.has(key)) return false
106+
if (isStopword(term) || seen.has(key)) return false
103107
seen.add(key)
104108
return true
105109
})
@@ -109,10 +113,12 @@ function distinctTerms(
109113
/**
110114
* One key for a word and its regular plural (`token`/`tokens`, `process`/`processes`). As in the
111115
* Porter stemmer, a final `-y` and `-ie(s)` fold to `i`, so `policy`/`policies` and
112-
* `movie`/`movies` each share a key.
116+
* `movie`/`movies` each share a key. A word of two letters or fewer is its own key, so `OS` never
117+
* folds to the letter `o`.
113118
*/
114119
function singularKey(term: string): string {
115120
const key = term.toLowerCase()
121+
if (key.length <= 2) return key
116122
const stripped = /[^su]s$/.test(key) ? key.slice(0, -1) : key
117123
const stem = /(?:s|x|z|ch|sh|i)e$/.test(stripped) ? stripped.slice(0, -1) : stripped
118124
return stem.replace(/y$/, 'i')

0 commit comments

Comments
 (0)