Searching a Database With Fts5 Queries and English Stemming
Continued from FTS5 search with English stemming and AND matching GitHub repo: https://github.com/scossar/find_command/blob/search-with-fts5-query-syntax/search_fts_query.py
For completeness, here’s an implementation of FTS5 search that passes unmodified FTS5 queries to the MATCH operator. Queries are run against the tmux key binding data supplied by
tmux_key_bindings.json
"""Search descriptions using an unmodified FTS5 query and English stemming."""
import argparse
import sqlite3
from contextlib import closing
from pathlib import Path
def search(database: Path, query: str) -> list[tuple[str, str]]:
"""Pass the query directly to MATCH, preserving FTS5 syntax."""
with closing(
sqlite3.connect(database.resolve().as_uri() + "?mode=ro", uri=True)
) as connection:
# As in search_fts.py, this index lasts only for this connection.
connection.execute("""
CREATE VIRTUAL TABLE temp.key_bindings_fts USING fts5(
key UNINDEXED,
description,
tokenize = 'porter unicode61'
)
""")
connection.execute("""
INSERT INTO temp.key_bindings_fts (rowid, key, description)
SELECT id, key, description FROM main.key_bindings
""")
# Binding protects the SQL statement while deliberately allowing FTS5
# to interpret operators, phrases, prefixes, and other query syntax.
return connection.execute(
"""
SELECT key, description
FROM temp.key_bindings_fts
WHERE key_bindings_fts MATCH ?
ORDER BY rowid
""",
(query,),
).fetchall()
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("query", help="FTS5 query, including any operators or quotes")
parser.add_argument(
"--database",
type=Path,
default=Path("tmux_key_bindings.sqlite3"),
help="SQLite file to search (default: tmux_key_bindings.sqlite3)",
)
args = parser.parse_args()
try:
rows = search(args.database, args.query)
except sqlite3.Error as error:
parser.exit(1, f"Search failed: {error}\n")
if not rows:
print("No matches found.")
for key, description in rows:
print(f"{key}\t{description}")
if __name__ == "__main__":
main()
Example queries: #
find-command ❯ uv run python search_fts_query.py 'pane AND window'
C-o Rotate the panes in the current window forwards.
! Break the current pane out of the window.
o Select the next pane in the current window.
M-o Rotate the panes in the current window backwards.
find-command ❯ uv run python search_fts_query.py 'window NOT pane'
& Kill the current window.
' Prompt for a window index to select.
, Rename the current window.
. Prompt for an index to move the current window.
0 to 9 Select windows 0 to 9.
c Create a new window.
f Prompt to search for text in open windows.
i Display some information about the current window.
l Move to the previously selected window.
n Change to the next window.
p Change to the previous window.
w Choose the current window interactively.
Space Arrange the current window in the next preset layout.
M-n Move to the next window with a bell or activity marker.
M-p Move to the previous window with a bell or activity marker.
find-command ❯ uv run python search_fts_query.py '"window pane"'
No matches found.
find-command ❯ uv run python search_fts_query.py 'NEAR(prompt window, 6)'
' Prompt for a window index to select.
f Prompt to search for text in open windows.
find_command search-with-fts5-query-syntax
find-command ❯ uv run python search_fts_query.py 'NEAR(prompt window, 5)'
' Prompt for a window index to select.