search target fix
This commit is contained in:
+34
-16
@@ -206,28 +206,46 @@ export function extractMatchedTextFromSnippet(
|
||||
const firstStart = first.index ?? 0;
|
||||
const lastEnd = (last.index ?? 0) + last[0].length;
|
||||
|
||||
const span = snippetHtml.slice(firstStart, lastEnd);
|
||||
const plain = normalizeQuotes(
|
||||
decodeSnippetEntities(span.replace(/<[^>]*>/g, ""))
|
||||
// Marked-span text — just the tokens Typesense highlighted.
|
||||
const markedSpan = snippetHtml.slice(firstStart, lastEnd);
|
||||
const markedPlain = normalizeQuotes(
|
||||
decodeSnippetEntities(markedSpan.replace(/<[^>]*>/g, ""))
|
||||
.replace(/\s+/g, " ")
|
||||
.trim(),
|
||||
);
|
||||
|
||||
if (!plain) return null;
|
||||
// Full-snippet text — includes Typesense's surrounding context. Typesense
|
||||
// snippets are direct slices of the source field, so the surrounding text
|
||||
// exists verbatim in the destination page. Using it as the prefix makes
|
||||
// single-token matches (e.g. `<mark>Therefore</mark> material senses...`)
|
||||
// land on the *correct* occurrence on the destination page rather than the
|
||||
// first instance of that token alphabetically.
|
||||
const fullPlain = normalizeQuotes(
|
||||
decodeSnippetEntities(snippetHtml.replace(/<[^>]*>/g, ""))
|
||||
.replace(/\s+/g, " ")
|
||||
// strip leading/trailing ellipsis Typesense sometimes adds
|
||||
.replace(/^(?:…|\.{3,})\s*/, "")
|
||||
.replace(/\s*(?:…|\.{3,})$/, "")
|
||||
.trim(),
|
||||
);
|
||||
|
||||
// If the span between first and last mark is unreasonably long
|
||||
// (e.g. snippet has marks at far ends of a long passage), fall back to
|
||||
// just the first marked term — that's still a real match in the doc.
|
||||
if (plain.length > 200) {
|
||||
const firstPlain = normalizeQuotes(
|
||||
decodeSnippetEntities(first[1].replace(/<[^>]*>/g, ""))
|
||||
.replace(/\s+/g, " ")
|
||||
.trim(),
|
||||
);
|
||||
return firstPlain || null;
|
||||
// Pick the best candidate:
|
||||
// - if the marked span alone is already ≥6 words, it's a multi-token phrase
|
||||
// and is specific enough — use it.
|
||||
// - otherwise prefer the full snippet (more context = more uniquely placed),
|
||||
// capped at 200 chars to keep URLs sane.
|
||||
// - fall back to marked span if the full snippet is empty / unusable.
|
||||
const markedWordCount = markedPlain
|
||||
? markedPlain.split(/\s+/).filter(Boolean).length
|
||||
: 0;
|
||||
|
||||
if (markedWordCount >= 6) {
|
||||
return markedPlain || null;
|
||||
}
|
||||
|
||||
return plain;
|
||||
if (fullPlain && fullPlain.length <= 200) {
|
||||
return fullPlain;
|
||||
}
|
||||
return markedPlain || null;
|
||||
}
|
||||
|
||||
// Decode the HTML entities Typesense commonly emits in match snippets.
|
||||
|
||||
Reference in New Issue
Block a user