mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-07-23 09:24:31 +02:00
where= and where_args= parameters to search() and search_sql()
Closes #441
This commit is contained in:
parent
0b6aba696d
commit
1b09538bc6
3 changed files with 104 additions and 4 deletions
|
|
@ -2108,10 +2108,16 @@ The ``.search()`` method also accepts the following optional parameters:
|
|||
``offset`` integer
|
||||
Offset to use along side the limit parameter.
|
||||
|
||||
``where`` string
|
||||
Extra SQL fragment for the WHERE clause
|
||||
|
||||
``where_args`` dictionary
|
||||
Arguments to use for ``:param`` placeholders in the extra WHERE clause
|
||||
|
||||
``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:
|
||||
To return just the title and published columns for three matches for ``"dog"`` where the ``id`` is greater than 10 ordered by ``published`` with the most recent first, use the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
@ -2119,6 +2125,8 @@ To return just the title and published columns for three matches for ``"dog"`` o
|
|||
"dog",
|
||||
order_by="published desc",
|
||||
limit=3,
|
||||
where="id > :min_id",
|
||||
where_args={"min_id": 10},
|
||||
columns=["title", "published"]
|
||||
):
|
||||
print(article)
|
||||
|
|
@ -2128,7 +2136,7 @@ To return just the title and published columns for three matches for ``"dog"`` o
|
|||
Building SQL queries with table.search_sql()
|
||||
--------------------------------------------
|
||||
|
||||
You can generate the SQL query that would be used for a search using the ``table.search_sql()`` method. It takes the same arguments as ``table.search()`` with the exception of the search query itself, since the returned SQL includes a parameter that can be used for the search.
|
||||
You can generate the SQL query that would be used for a search using the ``table.search_sql()`` method. It takes the same arguments as ``table.search()``, with the exception of the search query and the ``where_args`` parameter, since those should be provided when the returned SQL is executed.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
|
|||
|
|
@ -2254,6 +2254,7 @@ class Table(Queryable):
|
|||
order_by: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
where: Optional[str] = None,
|
||||
) -> str:
|
||||
""" "
|
||||
Return SQL string that can be used to execute searches against this table.
|
||||
|
|
@ -2262,6 +2263,7 @@ class Table(Queryable):
|
|||
:param order_by: Column or SQL expression to sort by
|
||||
:param limit: SQL limit
|
||||
:param offset: SQL offset
|
||||
:param where: Extra SQL fragment for the WHERE clause
|
||||
"""
|
||||
# Pick names for table and rank column that don't clash
|
||||
original = "original_" if self.name == "original" else "original"
|
||||
|
|
@ -2283,7 +2285,7 @@ class Table(Queryable):
|
|||
select
|
||||
rowid,
|
||||
{columns}
|
||||
from [{dbtable}]
|
||||
from [{dbtable}]{where_clause}
|
||||
)
|
||||
select
|
||||
{columns_with_prefix}
|
||||
|
|
@ -2311,6 +2313,7 @@ class Table(Queryable):
|
|||
limit_offset += " offset {}".format(offset)
|
||||
return sql.format(
|
||||
dbtable=self.name,
|
||||
where_clause="\n where {}".format(where) if where else "",
|
||||
original=original,
|
||||
columns=columns_sql,
|
||||
columns_with_prefix=columns_with_prefix_sql,
|
||||
|
|
@ -2326,6 +2329,8 @@ class Table(Queryable):
|
|||
columns: Optional[Iterable[str]] = None,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
where: str = None,
|
||||
where_args: Optional[Union[Iterable, dict]] = None,
|
||||
quote: bool = False,
|
||||
) -> Generator[dict, None, None]:
|
||||
"""
|
||||
|
|
@ -2337,18 +2342,29 @@ class Table(Queryable):
|
|||
:param columns: List of columns to return, defaults to all columns.
|
||||
:param limit: Optional integer limit for returned rows.
|
||||
:param offset: Optional integer SQL offset.
|
||||
:param where: Extra SQL fragment for the WHERE clause
|
||||
:param where_args: Arguments to use for :param placeholders in the extra WHERE clause
|
||||
:param quote: Apply quoting to disable any special characters in the search query
|
||||
|
||||
See :ref:`python_api_fts_search`.
|
||||
"""
|
||||
args = {"query": self.db.quote_fts(q) if quote else q}
|
||||
if where_args and "query" in where_args:
|
||||
raise ValueError(
|
||||
"'query' is a reserved key and cannot be passed to where_args for .search()"
|
||||
)
|
||||
if where_args:
|
||||
args.update(where_args)
|
||||
|
||||
cursor = self.db.execute(
|
||||
self.search_sql(
|
||||
order_by=order_by,
|
||||
columns=columns,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
where=where,
|
||||
),
|
||||
{"query": self.db.quote_fts(q) if quote else q},
|
||||
args,
|
||||
)
|
||||
columns = [c[0] for c in cursor.description]
|
||||
for row in cursor:
|
||||
|
|
|
|||
|
|
@ -94,6 +94,38 @@ def test_search_limit_offset(fresh_db):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fts_version", ("FTS4", "FTS5"))
|
||||
def test_search_where(fresh_db, fts_version):
|
||||
table = fresh_db["t"]
|
||||
table.insert_all(search_records)
|
||||
table.enable_fts(["text", "country"], fts_version=fts_version)
|
||||
results = list(
|
||||
table.search("are", where="country = :country", where_args={"country": "Japan"})
|
||||
)
|
||||
assert results == [
|
||||
{
|
||||
"rowid": 1,
|
||||
"text": "tanuki are running tricksters",
|
||||
"country": "Japan",
|
||||
"not_searchable": "foo",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_search_where_args_disallows_query(fresh_db):
|
||||
table = fresh_db["t"]
|
||||
with pytest.raises(ValueError) as ex:
|
||||
list(
|
||||
table.search(
|
||||
"x", where="author = :query", where_args={"query": "not allowed"}
|
||||
)
|
||||
)
|
||||
assert (
|
||||
ex.value.args[0]
|
||||
== "'query' is a reserved key and cannot be passed to where_args for .search()"
|
||||
)
|
||||
|
||||
|
||||
def test_enable_fts_table_names_containing_spaces(fresh_db):
|
||||
table = fresh_db["test"]
|
||||
table.insert({"column with spaces": "in its name"})
|
||||
|
|
@ -415,6 +447,28 @@ def test_enable_fts_error_message_on_views():
|
|||
"limit 10"
|
||||
),
|
||||
),
|
||||
(
|
||||
{"where": "author = :author"},
|
||||
"FTS5",
|
||||
(
|
||||
"with original as (\n"
|
||||
" select\n"
|
||||
" rowid,\n"
|
||||
" *\n"
|
||||
" from [books]\n"
|
||||
" where author = :author\n"
|
||||
")\n"
|
||||
"select\n"
|
||||
" [original].*\n"
|
||||
"from\n"
|
||||
" [original]\n"
|
||||
" join [books_fts] on [original].rowid = [books_fts].rowid\n"
|
||||
"where\n"
|
||||
" [books_fts] match :query\n"
|
||||
"order by\n"
|
||||
" [books_fts].rank"
|
||||
),
|
||||
),
|
||||
(
|
||||
{"columns": ["title"]},
|
||||
"FTS4",
|
||||
|
|
@ -480,6 +534,28 @@ def test_enable_fts_error_message_on_views():
|
|||
"limit 2"
|
||||
),
|
||||
),
|
||||
(
|
||||
{"where": "author = :author"},
|
||||
"FTS4",
|
||||
(
|
||||
"with original as (\n"
|
||||
" select\n"
|
||||
" rowid,\n"
|
||||
" *\n"
|
||||
" from [books]\n"
|
||||
" where author = :author\n"
|
||||
")\n"
|
||||
"select\n"
|
||||
" [original].*\n"
|
||||
"from\n"
|
||||
" [original]\n"
|
||||
" join [books_fts] on [original].rowid = [books_fts].rowid\n"
|
||||
"where\n"
|
||||
" [books_fts] match :query\n"
|
||||
"order by\n"
|
||||
" rank_bm25(matchinfo([books_fts], 'pcnalx'))"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_search_sql(kwargs, fts, expected):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue