Skip to content

Commit a6609d3

Browse files
Merge pull request #188 from Chandra-Prakash-TS/search-enhanced
search enhanced in GoFr
2 parents 06aa1a5 + 71c4224 commit a6609d3

File tree

2 files changed

+192
-33
lines changed

2 files changed

+192
-33
lines changed

src/components/Search.jsx

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -123,27 +123,45 @@ function HighlightQuery({ text, query }) {
123123
)
124124
}
125125

126-
function SearchResult({ result, autocomplete, collection, query }) {
127-
let id = useId()
128-
126+
function SearchResult({ result, autocomplete, collection, query, index }) {
129127
let sectionTitle = navigation.find((section) =>
130128
section.links.find((link) => link.href === result.url.split('#')[0]),
131129
)?.title
130+
132131
let hierarchy = [sectionTitle, result.pageTitle].filter(
133132
(x) => typeof x === 'string',
134133
)
135134

135+
const id = useId()
136+
136137
return (
137138
<li
138-
className="group block cursor-default rounded-lg px-3 py-2 aria-selected:bg-slate-100 dark:aria-selected:bg-slate-700/30"
139+
className="block cursor-default rounded-lg px-3 py-2 "
139140
aria-labelledby={`${id}-hierarchy ${id}-title`}
140141
{...autocomplete.getItemProps({
141142
item: result,
142143
source: collection.source,
143144
})}
145+
onMouseEnter={(e) => {
146+
e.currentTarget.classList.add('bg-slate-100', 'dark:bg-slate-700/30')
147+
const titleEl = e.currentTarget.querySelector('[data-title]')
148+
if (titleEl) {
149+
titleEl.classList.add('text-sky-600', 'dark:text-sky-400')
150+
titleEl.classList.remove('text-slate-700', 'dark:text-slate-300')
151+
}
152+
}}
153+
onMouseLeave={(e) => {
154+
e.currentTarget.classList.remove('bg-slate-100', 'dark:bg-slate-700/30')
155+
const titleEl = e.currentTarget.querySelector('[data-title]')
156+
if (titleEl) {
157+
titleEl.classList.remove('text-sky-600', 'dark:text-sky-400')
158+
titleEl.classList.add('text-slate-700', 'dark:text-slate-300')
159+
}
160+
}}
144161
>
145162
<div
146163
id={`${id}-title`}
164+
data-title
147165
aria-hidden="true"
148166
className="text-sm text-slate-700 group-aria-selected:text-sky-600 dark:text-slate-300 dark:group-aria-selected:text-sky-400"
149167
>
@@ -155,28 +173,30 @@ function SearchResult({ result, autocomplete, collection, query }) {
155173
aria-hidden="true"
156174
className="mt-0.5 truncate whitespace-nowrap text-xs text-slate-500 dark:text-slate-400"
157175
>
158-
{hierarchy.map((item, itemIndex, items) => (
159-
<Fragment key={itemIndex}>
160-
<HighlightQuery text={item} query={query} />
161-
<span
162-
className={
163-
itemIndex === items.length - 1
164-
? 'sr-only'
165-
: 'mx-2 text-slate-300 dark:text-slate-700'
166-
}
167-
>
168-
/
169-
</span>
170-
</Fragment>
171-
))}
176+
{hierarchy
177+
.filter((item) => item && item.trim().toLowerCase() !== 'untitled')
178+
.map((item, itemIndex, items) => (
179+
<Fragment key={itemIndex}>
180+
<HighlightQuery text={item} query={query} />
181+
<span
182+
className={
183+
itemIndex === items.length - 1
184+
? 'sr-only'
185+
: 'mx-2 text-slate-300 dark:text-slate-700'
186+
}
187+
>
188+
/
189+
</span>
190+
</Fragment>
191+
))}
172192
</div>
173193
)}
174194
</li>
175195
)
176196
}
177197

178198
function SearchResults({ autocomplete, query, collection }) {
179-
if (collection.items.length === 0) {
199+
if (collection.items[0].items.length === 0) {
180200
return (
181201
<p className="px-4 py-8 text-center text-sm text-slate-700 dark:text-slate-400">
182202
Couldn't find what you are looking for?&nbsp;
@@ -191,21 +211,55 @@ function SearchResults({ autocomplete, query, collection }) {
191211
)
192212
}
193213

214+
let rawItems = []
215+
216+
rawItems = collection.items[0].items
217+
218+
let filtered = rawItems.filter(
219+
(item) =>
220+
item &&
221+
item.title &&
222+
item.title.trim() !== '' &&
223+
item.title.trim().toLowerCase() !== 'untitled',
224+
)
225+
226+
// if (filtered.length === 0) {
227+
// return (
228+
// <p className="px-4 py-8 text-center text-sm text-slate-700 dark:text-slate-400">
229+
// No results found for &ldquo;
230+
// <span className="break-words text-slate-900 dark:text-white">
231+
// {query}
232+
// </span>
233+
// &rdquo;
234+
// </p>
235+
// )
236+
// }
237+
let seenUrls = new Set()
238+
let deduped = []
239+
for (let item of filtered) {
240+
// Only the part before the '#'
241+
let baseUrl = item.url.split('#')
242+
if (!seenUrls.has(baseUrl)) {
243+
seenUrls.add(baseUrl)
244+
deduped.push(item)
245+
}
246+
}
247+
194248
return (
195249
<ul {...autocomplete.getListProps()}>
196-
{collection.items.map((result) => (
250+
{deduped.map((result, index) => (
197251
<SearchResult
198-
key={result.url}
252+
key={`${result.url}-${index}`}
199253
result={result}
200254
autocomplete={autocomplete}
201255
collection={collection}
202256
query={query}
257+
index={index}
203258
/>
204259
))}
205260
</ul>
206261
)
207262
}
208-
209263
const SearchInput = forwardRef(function SearchInput(
210264
{ autocomplete, autocompleteState, onClose },
211265
inputRef,

src/markdoc/search.mjs

Lines changed: 116 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,123 @@ export default function withSearch(nextConfig = {}) {
110110
}
111111
112112
export function search(query, options = {}) {
113-
let result = sectionIndex.search(query, {
114-
...options,
115-
enrich: true,
116-
})
117-
if (result.length === 0) {
118-
return []
113+
try {
114+
let defaultOptions = {
115+
limit: 10,
116+
...options
117+
}
118+
119+
let queryLower = query.toLowerCase().trim()
120+
let results = []
121+
122+
// Search through all indexed content
123+
for (let [url, data] of urlToData) {
124+
let score = 0
125+
126+
// Exact phrase match (highest priority)
127+
if (data.searchText.includes(queryLower)) {
128+
score += 10
129+
130+
// Bonus for title matches
131+
if (data.title.toLowerCase().includes(queryLower)) {
132+
score += 5
133+
}
134+
135+
// Bonus for early position in content
136+
let position = data.searchText.indexOf(queryLower)
137+
if (position < 100) {
138+
score += 3
139+
}
140+
141+
results.push({
142+
url: data.url || url,
143+
title: data.title || '',
144+
pageTitle: data.pageTitle || '',
145+
content: data.content || '',
146+
score: score
147+
})
148+
}
149+
// Partial word matching
150+
else {
151+
let queryWords = queryLower.split(/\\s+/).filter(w => w.length > 2)
152+
let matchCount = 0
153+
154+
for (let word of queryWords) {
155+
if (data.searchText.includes(word)) {
156+
matchCount++
157+
}
158+
}
159+
160+
if (matchCount > 0) {
161+
score = matchCount / queryWords.length
162+
results.push({
163+
...data,
164+
score: score
165+
})
166+
}
167+
}
168+
}
169+
170+
171+
// Sort by score and limit results
172+
results.sort((a, b) => b.score - a.score)
173+
results = results.slice(0, defaultOptions.limit)
174+
return { items: results }
175+
} catch (error) {
176+
console.error('Search failed:', error)
177+
return { items: [] }
178+
}
179+
}
180+
181+
export function searchExact(query, options = {}) {
182+
try {
183+
let queryLower = query.toLowerCase().trim()
184+
let exactMatches = []
185+
186+
for (let [url, data] of urlToData) {
187+
if (data.searchText.includes(queryLower)) {
188+
exactMatches.push({
189+
url: data.url || url,
190+
title: data.title || '',
191+
pageTitle: data.pageTitle || '',
192+
content: data.content || '',
193+
score: 10
194+
})
195+
}
196+
}
197+
198+
return { items: exactMatches.slice(0, options.limit || 10) }
199+
} catch (error) {
200+
console.error('Exact search failed:', error)
201+
return { items: [] }
202+
}
203+
}
204+
205+
// Debug function to check what content is indexed
206+
export function debugSearch(query) {
207+
console.log('Total indexed items:', urlToData.size)
208+
console.log('Sample content:')
209+
let count = 0
210+
for (let [url, data] of urlToData) {
211+
if (count < 3) {
212+
console.log('URL:', url)
213+
console.log('Content preview:', data.content.substring(0, 200) + '...')
214+
console.log('---')
215+
count++
216+
}
217+
}
218+
219+
if (query) {
220+
let queryLower = query.toLowerCase()
221+
for (let [url, data] of urlToData) {
222+
if (data.searchText.includes(queryLower)) {
223+
console.log('Found in:', url)
224+
let index = data.searchText.indexOf(queryLower)
225+
console.log('Context:', data.searchText.substring(Math.max(0, index - 50), index + query.length + 50))
226+
break
227+
}
228+
}
119229
}
120-
return result[0].result.map((item) => ({
121-
url: item.id,
122-
title: item.doc.title,
123-
pageTitle: item.doc.pageTitle,
124-
}))
125230
}
126231
`
127232
}),

0 commit comments

Comments
 (0)