Class: DocsKit::SearchIndex

Inherits:
Object
  • Object
show all
Defined in:
lib/docs_kit/search_index.rb,
lib/docs_kit/search_index/snippet.rb

Overview

An in-memory docs search index, built straight from the pages' Markdown twins — zero authoring, no external service, no build step. This is the structural replacement for the hand-maintained "second registry" + regex-parsed text a site used to keep: the twin already IS the page's content, split on its ## headings into searchable sections.

DocsKit::SearchIndex.new(triples).search("theme switcher")

triples is [[page_title, page_href, markdown], ...] — the controller renders each registry page through DocsKit::MarkdownExport and hands the triples in, exactly as DocsKit::LlmsText separates pure shaping from the controller's rendering. So the whole index + scorer is unit-testable with no Rails.

One entry per section (plus a page-intro entry for the text before the first ## ). Scoring is plain Ruby: case-insensitive token match, all tokens must hit (AND), a title hit outranks a heading hit outranks a body hit. Results cap at MAX_RESULTS with an HTML-safe snippet around the match (the term in ). No dependencies, no fuzzy matching (revisit if usage demands it).

Defined Under Namespace

Classes: Entry, Snippet

Constant Summary collapse

TITLE_WEIGHT =

Field weights — a match in the page title beats a section heading beats body text, so the most on-topic section floats up.

100
HEADING_WEIGHT =
10
BODY_WEIGHT =
1
MAX_RESULTS =

Never return more than this — a docs site is tens of pages, and a reader scans the top matches, not a hundred.

20

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(triples = []) ⇒ SearchIndex

triples: [[page_title, page_href, markdown], ...].



58
59
60
# File 'lib/docs_kit/search_index.rb', line 58

def initialize(triples = [])
  @entries = triples.flat_map { |title, href, markdown| entries_for(title, href, markdown) }
end

Instance Attribute Details

#entriesObject (readonly)

Returns the value of attribute entries.



62
63
64
# File 'lib/docs_kit/search_index.rb', line 62

def entries
  @entries
end

Instance Method Details

#search(query) ⇒ Object

The top MAX_RESULTS SearchHits for query, best first. Blank query → []. Every whitespace-split token must match the entry somewhere (AND); the entry's score is the sum, per token, of the best field it matched.



67
68
69
70
71
72
73
74
# File 'lib/docs_kit/search_index.rb', line 67

def search(query)
  tokens = tokenize(query)
  return [] if tokens.empty?

  scored = @entries.filter_map { |entry| score_entry(entry, tokens) }
  scored.sort_by { |hit| [-hit.score, hit.page_title, hit.section_title.to_s] }
        .first(MAX_RESULTS)
end