mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-07-23 01:14:31 +02:00
table.search(quote=True) parameter, refs #296
This commit is contained in:
parent
f0fd19267f
commit
8ae77a6961
3 changed files with 54 additions and 11 deletions
|
|
@ -1843,8 +1843,15 @@ The ``.has_counts_triggers`` property shows if a table has been configured with
|
|||
|
||||
.. _python_api_fts:
|
||||
|
||||
Enabling full-text search
|
||||
=========================
|
||||
Full-text search
|
||||
================
|
||||
|
||||
SQLite includes bundled extensions that implement `powerful full-text search <https://www.sqlite.org/fts5.html>`__.
|
||||
|
||||
.. _python_api_fts_enable:
|
||||
|
||||
Enabling full-text search for a table
|
||||
-------------------------------------
|
||||
|
||||
You can enable full-text search on a table using ``.enable_fts(columns)``:
|
||||
|
||||
|
|
@ -1947,6 +1954,9 @@ The ``.search()`` method also accepts the following optional parameters:
|
|||
``offset`` integer
|
||||
Offset to use along side the limit parameter.
|
||||
|
||||
``quote`` bool
|
||||
Apply :ref:`FTS quoting rules <python_api_quote_fts>` to the search query, disabling advanced query syntax in a way that avoids surprising errors.
|
||||
|
||||
To return just the title and published columns for three matches for ``"dog"`` ordered by ``published`` with the most recent first, use the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
|
|
|||
|
|
@ -1924,7 +1924,13 @@ class Table(Queryable):
|
|||
)
|
||||
return self
|
||||
|
||||
def search_sql(self, columns=None, order_by=None, limit=None, offset=None) -> str:
|
||||
def search_sql(
|
||||
self,
|
||||
columns: Optional[Iterable[str]] = None,
|
||||
order_by: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
) -> str:
|
||||
"Return SQL string that can be used to execute searches against this table."
|
||||
# Pick names for table and rank column that don't clash
|
||||
original = "original_" if self.name == "original" else "original"
|
||||
|
|
@ -1986,19 +1992,21 @@ class Table(Queryable):
|
|||
self,
|
||||
q: str,
|
||||
order_by: Optional[str] = None,
|
||||
columns: Optional[List[str]] = None,
|
||||
columns: Optional[Iterable[str]] = None,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
quote: bool = False,
|
||||
) -> Generator[dict, None, None]:
|
||||
"""
|
||||
Execute a search against this table using SQLite full-text search, returning a sequence of
|
||||
dictionaries for each row.
|
||||
|
||||
- ``q`` - words to search for
|
||||
- ``q`` - terms to search for
|
||||
- ``order_by`` - defaults to order by rank, or specify a column here.
|
||||
- ``columns`` - list of columns to return, defaults to all columns.
|
||||
- ``limit`` - optional integer limit for returned rows.
|
||||
- ``offset`` - optional integer SQL offset.
|
||||
- ``quote`` - apply quoting to disable any special characters in the search query
|
||||
|
||||
See :ref:`python_api_fts_search`.
|
||||
"""
|
||||
|
|
@ -2009,7 +2017,7 @@ class Table(Queryable):
|
|||
limit=limit,
|
||||
offset=offset,
|
||||
),
|
||||
{"query": q},
|
||||
{"query": self.db.quote_fts(q) if quote else q},
|
||||
)
|
||||
columns = [c[0] for c in cursor.description]
|
||||
for row in cursor:
|
||||
|
|
|
|||
|
|
@ -503,13 +503,38 @@ def test_search_sql(kwargs, fts, expected):
|
|||
assert sql == expected
|
||||
|
||||
|
||||
def test_quote_fts_query(fresh_db):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input,expected",
|
||||
(
|
||||
("dog", '"dog"'),
|
||||
("cat,", '"cat,"'),
|
||||
("cat's", '"cat\'s"'),
|
||||
("dog.", '"dog."'),
|
||||
("cat dog", '"cat" "dog"'),
|
||||
# If a phrase is already double quoted, leave it so
|
||||
('"cat dog"', '"cat dog"'),
|
||||
('"cat dog" fish', '"cat dog" "fish"'),
|
||||
# Sensibly handle unbalanced double quotes
|
||||
('cat"', '"cat"'),
|
||||
('"cat dog" "fish', '"cat dog" "fish"'),
|
||||
),
|
||||
)
|
||||
def test_quote_fts_query(fresh_db, input, expected):
|
||||
table = fresh_db["searchable"]
|
||||
table.insert_all(search_records)
|
||||
table.enable_fts(["text", "country"])
|
||||
|
||||
query = "cat's"
|
||||
result = fresh_db.quote_fts(query)
|
||||
quoted = fresh_db.quote_fts(input)
|
||||
assert quoted == expected
|
||||
# Executing query does not crash.
|
||||
list(table.search(result))
|
||||
list(table.search(quoted))
|
||||
|
||||
|
||||
def test_search_quote(fresh_db):
|
||||
table = fresh_db["searchable"]
|
||||
table.insert_all(search_records)
|
||||
table.enable_fts(["text", "country"])
|
||||
query = "cat's"
|
||||
with pytest.raises(sqlite3.OperationalError):
|
||||
list(table.search(query))
|
||||
# No exception with quote=True
|
||||
list(table.search(query, quote=True))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue