mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-10-08 02:17:08 +02:00
enable_fts(), populate_fts() and search() methods
This commit is contained in:
parent
c446e22f34
commit
f4907f6df5
4 changed files with 100 additions and 1 deletions
|
|
@ -116,3 +116,31 @@ The ``.schema`` property outputs the table's schema as a SQL string::
|
||||||
FOREIGN KEY ("qSiteInfo") REFERENCES [qSiteInfo](id),
|
FOREIGN KEY ("qSiteInfo") REFERENCES [qSiteInfo](id),
|
||||||
FOREIGN KEY ("qCareAssistant") REFERENCES [qCareAssistant](id),
|
FOREIGN KEY ("qCareAssistant") REFERENCES [qCareAssistant](id),
|
||||||
FOREIGN KEY ("qLegalStatus") REFERENCES [qLegalStatus](id))
|
FOREIGN KEY ("qLegalStatus") REFERENCES [qLegalStatus](id))
|
||||||
|
|
||||||
|
Enabling full-text search
|
||||||
|
=========================
|
||||||
|
|
||||||
|
You can enable full-text search on a table using ``.enable_fts(columns)``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
dogs.enable_fts(["name", "twitter"])
|
||||||
|
|
||||||
|
You can then run searches using the ``.search()`` method:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
rows = dogs.search("cleo")
|
||||||
|
|
||||||
|
If you insert additioal records into the table you will need to refresh the search index using ``populate_fts()``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
dogs.insert({
|
||||||
|
"id": 2,
|
||||||
|
"name": "Marnie",
|
||||||
|
"twitter": "MarnieTheDog",
|
||||||
|
"age": 16,
|
||||||
|
"is_good_dog": True,
|
||||||
|
}, pk="id")
|
||||||
|
dogs.populate_fts(["name", "twitter"])
|
||||||
|
|
|
||||||
2
setup.py
2
setup.py
|
|
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
|
|
||||||
VERSION = "0.3.1"
|
VERSION = "0.4"
|
||||||
|
|
||||||
|
|
||||||
def get_long_description():
|
def get_long_description():
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,28 @@ class Table:
|
||||||
self.db.conn.commit()
|
self.db.conn.commit()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def enable_fts(self, columns):
|
||||||
|
"Enables FTS on the specified columns"
|
||||||
|
sql = """
|
||||||
|
CREATE VIRTUAL TABLE "{table}_fts" USING FTS4 (
|
||||||
|
{columns},
|
||||||
|
content="{table}"
|
||||||
|
);
|
||||||
|
""".format(
|
||||||
|
table=self.name, columns=", ".join(columns)
|
||||||
|
)
|
||||||
|
self.db.conn.executescript(sql)
|
||||||
|
self.populate_fts(columns)
|
||||||
|
|
||||||
|
def populate_fts(self, columns):
|
||||||
|
sql = """
|
||||||
|
INSERT INTO "{table}_fts" (rowid, {columns})
|
||||||
|
SELECT rowid, {columns} FROM {table};
|
||||||
|
""".format(
|
||||||
|
table=self.name, columns=", ".join(columns)
|
||||||
|
)
|
||||||
|
self.db.conn.executescript(sql)
|
||||||
|
|
||||||
def detect_column_types(self, records):
|
def detect_column_types(self, records):
|
||||||
all_column_types = {}
|
all_column_types = {}
|
||||||
for record in records:
|
for record in records:
|
||||||
|
|
@ -155,6 +177,18 @@ class Table:
|
||||||
column_types[key] = t
|
column_types[key] = t
|
||||||
return column_types
|
return column_types
|
||||||
|
|
||||||
|
def search(self, q):
|
||||||
|
sql = """
|
||||||
|
select * from {table} where rowid in (
|
||||||
|
select rowid from [{table}_fts]
|
||||||
|
where [{table}_fts] match :search
|
||||||
|
)
|
||||||
|
order by rowid
|
||||||
|
""".format(
|
||||||
|
table=self.name
|
||||||
|
)
|
||||||
|
return self.db.conn.execute(sql, (q,)).fetchall()
|
||||||
|
|
||||||
def insert(self, record, pk=None, foreign_keys=None, upsert=False):
|
def insert(self, record, pk=None, foreign_keys=None, upsert=False):
|
||||||
return self.insert_all(
|
return self.insert_all(
|
||||||
[record], pk=pk, foreign_keys=foreign_keys, upsert=upsert
|
[record], pk=pk, foreign_keys=foreign_keys, upsert=upsert
|
||||||
|
|
|
||||||
37
tests/test_enable_fts.py
Normal file
37
tests/test_enable_fts.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from .fixtures import fresh_db
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
search_records = [
|
||||||
|
{"text": "tanuki are tricksters", "country": "Japan", "not_searchable": "foo"},
|
||||||
|
{"text": "racoons are trash pandas", "country": "USA", "not_searchable": "bar"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_enable_fts(fresh_db):
|
||||||
|
table = fresh_db["searchable"]
|
||||||
|
table.insert_all(search_records)
|
||||||
|
assert ["searchable"] == fresh_db.tables
|
||||||
|
table.enable_fts(["text", "country"])
|
||||||
|
assert [
|
||||||
|
"searchable",
|
||||||
|
"searchable_fts",
|
||||||
|
"searchable_fts_segments",
|
||||||
|
"searchable_fts_segdir",
|
||||||
|
"searchable_fts_docsize",
|
||||||
|
"searchable_fts_stat",
|
||||||
|
] == fresh_db.tables
|
||||||
|
assert [("tanuki are tricksters", "Japan", "foo")] == table.search("tanuki")
|
||||||
|
assert [("racoons are trash pandas", "USA", "bar")] == table.search("usa")
|
||||||
|
assert [] == table.search("bar")
|
||||||
|
|
||||||
|
|
||||||
|
def test_populate_fts(fresh_db):
|
||||||
|
table = fresh_db["populatable"]
|
||||||
|
table.insert(search_records[0])
|
||||||
|
table.enable_fts(["text", "country"])
|
||||||
|
assert [] == table.search("trash pandas")
|
||||||
|
table.insert(search_records[1])
|
||||||
|
assert [] == table.search("trash pandas")
|
||||||
|
# Now run populate_fts to make this record available
|
||||||
|
table.populate_fts(["text", "country"])
|
||||||
|
assert [("racoons are trash pandas", "USA", "bar")] == table.search("usa")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue