Skip to content

Search

DfSearch

DfSearch(db, *, table_name="df")

search

search(
    keyword, *, limit=25, offset=0,
    language=None, issuingplace=None, country=None,
    year_min=None, year_max=None, match_all=True, fuzzy=0,
) -> SearchResult

match_all decides what a multi-word keyword means: True (the default) requires every word, False matches any of them. A quoted keyword is passed to the query parser instead, so '"de ecclesia"' is an exact phrase.

fuzzy is the edit distance allowed per term. It is 0 by default because the engine makes fuzzy matching and stemming mutually exclusive — a fuzzy term skips the analysis pipeline and is matched raw against stemmed index terms, so konungen collapses from 279 hits to 6. Raise it to 1 to reach spelling variants, which is the corpus's largest recall problem, and pass a base form when you do. The query parser has no fuzziness argument, so combining it with a quoted phrase is rejected rather than silently ignored.

Raises SearchInputError — a ValueError subclass — for a blank keyword, a negative offset, a limit below 1, or fuzzy on a quoted phrase. It is a distinct type because the MCP layer returns its message to the caller verbatim, and lancedb raises plain ValueErrors whose messages quote dataset paths and internal query structure.

get_charter

get_charter(df_number: str | int) -> dict | None

Returns the charter row, or None for an unknown or non-numeric number.

SearchResult

field meaning
records The page, as a list of row dicts — every column except searchable_text, plus _score.
total_hits True match count over the filtered set, up to MAX_TOTAL_COUNT (10,000).
total_is_capped True when the count reached that bound, i.e. total_hits is a floor. format_results then renders it as 10000+ rather than passing it off as exact. Never true for df (6,876 rows); routine for tuomiokirjat.
keyword, offset, limit Echoed back, so a formatter needs no other state.

Projection

lancedb_fts_search fetches the whole ranked set to count it and discards all but limit rows, so what each row carries is a real cost. columns defaults to every column except searchable_text, which only repeats text the row already has — measured on df, that halves a 1,451-hit query from 6.1 MB to 3.3 MB. _score is added explicitly, since lancedb still auto-projects it when a select omits it but warns that it will stop.

The spine

lancedb_fts_search(
    db, table_name, keyword, *,
    limit, offset=0, where=None, columns=None, match_all=True, fuzzy=0,
) -> SearchResult

is what DfSearch calls, and what the other two corpora will call. Filters arrive as a SQL where string built by the predicate helpers:

helper produces
equals(col, v) col = 'v' (bare for ints)
at_least(col, v) / at_most(col, v) col >= v / col <= v
text_contains(col, v) lower(col) LIKE '%v%' ESCAPE '\'
combine(a, None, b) a AND b, or None when nothing is set

Two quoting details are load-bearing. String literals double their single quotes, so a place name containing an apostrophe cannot break the predicate. And column names are emitted bare, never double-quoted: LanceDB's filter parser reads "col" as a string literal, SQLite-style, so quoting an identifier silently matches nothing.

text_contains also escapes LIKE wildcards in the value, so a literal % in a filter matches a literal % rather than "anything".