Skip to content

Commit

Permalink
Ability to add descending order indexes (#262)
Browse files Browse the repository at this point in the history
* DescIndex(column) for descending index columns, refs #260
* Ability to add desc indexes using CLI, closes #260
  • Loading branch information
simonw authored May 29, 2021
1 parent b230287 commit 51d01da
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 4 deletions.
8 changes: 8 additions & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,14 @@ Use the ``--unique`` option to create a unique index.

Use ``--if-not-exists`` to avoid attempting to create the index if one with that name already exists.

To add an index on a column in descending order, prefix the column with a hyphen. Since this can be confused for a command-line option you need to construct that like this::

$ sqlite-utils create-index mydb.db mytable -- col1 -col2 col3

This will create an index on that table on ``(col1, col2 desc, col3)``.

If your column names are already prefixed with a hyphen you'll need to manually execute a ``CREATE INDEX`` SQL statement to add indexes to them rather than using this tool.

.. _cli_fts:

Configuring full-text search
Expand Down
11 changes: 11 additions & 0 deletions docs/python-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,17 @@ By default the index will be named ``idx_{table-name}_{columns}`` - if you want
index_name="good_dogs_by_age"
)
To create an index in descending order for a column, wrap the column name in ``db.DescIndex()`` like this:
.. code-block:: python
from sqlite_utils.db import DescIndex
db["dogs"].create_index(
["is_good_dog", DescIndex("age")],
index_name="good_dogs_by_age"
)
You can create a unique index by passing ``unique=True``:
.. code-block:: python
Expand Down
16 changes: 13 additions & 3 deletions sqlite_utils/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import hashlib
import pathlib
import sqlite_utils
from sqlite_utils.db import AlterError
from sqlite_utils.db import AlterError, DescIndex
import textwrap
import io
import itertools
Expand Down Expand Up @@ -450,11 +450,21 @@ def index_foreign_keys(path, load_extension):
)
@load_extension_option
def create_index(path, table, column, name, unique, if_not_exists, load_extension):
"Add an index to the specified table covering the specified columns"
"""
Add an index to the specified table covering the specified columns.
Use "sqlite-utils create-index mydb -- -column" to specify descending
order for a column.
"""
db = sqlite_utils.Database(path)
_load_extensions(db, load_extension)
# Treat -prefix as descending for columns
columns = []
for col in column:
if col.startswith("-"):
col = DescIndex(col[1:])
columns.append(col)
db[table].create_index(
column, index_name=name, unique=unique, if_not_exists=if_not_exists
columns, index_name=name, unique=unique, if_not_exists=if_not_exists
)


Expand Down
13 changes: 12 additions & 1 deletion sqlite_utils/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ class InvalidColumns(Exception):
pass


class DescIndex(str):
pass


_COUNTS_TABLE_CREATE_SQL = """
CREATE TABLE IF NOT EXISTS [{}](
[table] TEXT PRIMARY KEY,
Expand Down Expand Up @@ -1156,6 +1160,13 @@ def create_index(self, columns, index_name=None, unique=False, if_not_exists=Fal
index_name = "idx_{}_{}".format(
self.name.replace(" ", "_"), "_".join(columns)
)
columns_sql = []
for column in columns:
if isinstance(column, DescIndex):
fmt = "[{}] desc"
else:
fmt = "[{}]"
columns_sql.append(fmt.format(column))
sql = (
textwrap.dedent(
"""
Expand All @@ -1167,7 +1178,7 @@ def create_index(self, columns, index_name=None, unique=False, if_not_exists=Fal
.format(
index_name=index_name,
table_name=self.name,
columns=", ".join("[{}]".format(c) for c in columns),
columns=", ".join(columns_sql),
unique="UNIQUE " if unique else "",
if_not_exists="IF NOT EXISTS " if if_not_exists else "",
)
Expand Down
11 changes: 11 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@ def test_create_index(db_path):
)


def test_create_index_desc(db_path):
db = Database(db_path)
assert [] == db["Gosh"].indexes
result = CliRunner().invoke(cli.cli, ["create-index", db_path, "Gosh", "--", "-c1"])
assert result.exit_code == 0
assert (
db.execute("select sql from sqlite_master where type='index'").fetchone()[0]
== "CREATE INDEX [idx_Gosh_c1]\n ON [Gosh] ([c1] desc)"
)


@pytest.mark.parametrize(
"col_name,col_type,expected_schema",
(
Expand Down
14 changes: 14 additions & 0 deletions tests/test_create.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from sqlite_utils.db import (
Index,
Database,
DescIndex,
ForeignKey,
AlterError,
NoObviousTable,
Expand Down Expand Up @@ -739,6 +740,19 @@ def test_create_index_if_not_exists(fresh_db):
dogs.create_index(["name"], if_not_exists=True)


def test_create_index_desc(fresh_db):
dogs = fresh_db["dogs"]
dogs.insert({"name": "Cleo", "twitter": "cleopaws", "age": 3, "is good dog": True})
assert [] == dogs.indexes
dogs.create_index([DescIndex("age"), "name"])
sql = fresh_db.execute(
"select sql from sqlite_master where name='idx_dogs_age_name'"
).fetchone()[0]
assert sql == (
"CREATE INDEX [idx_dogs_age_name]\n" " ON [dogs] ([age] desc, [name])"
)


@pytest.mark.parametrize(
"data_structure",
(
Expand Down

0 comments on commit 51d01da

Please sign in to comment.