diff options
author | Alexandre Flament <alex@al-f.net> | 2021-05-27 14:27:11 +0200 |
---|---|---|
committer | Alexandre Flament <alex@al-f.net> | 2021-05-27 14:27:11 +0200 |
commit | 2ea34a3c36d444fa5afac381a417d66bc96849e5 (patch) | |
tree | a806f5a2660767e3bb33fd2a4ca9355ca98da722 /searx/engines/sqlite.py | |
parent | 7c76cef746a90542f7db7a9ca876e763d0aad1a3 (diff) | |
download | searxng-2ea34a3c36d444fa5afac381a417d66bc96849e5.tar.gz searxng-2ea34a3c36d444fa5afac381a417d66bc96849e5.zip |
[enh] add offline engine for sqlite database
To test & demonstrate this implementation download:
https://liste.mediathekview.de/filmliste-v2.db.bz2
and unpack into searx/data/filmliste-v2.db, in your settings.yml define a sqlite
engine named "demo"::
- name : demo
engine : sqlite
shortcut: demo
categories: general
result_template: default.html
database : searx/data/filmliste-v2.db
query_str : >-
SELECT title || ' (' || time(duration, 'unixepoch') || ')' AS title,
COALESCE( NULLIF(url_video_hd,''), NULLIF(url_video_sd,''), url_video) AS url,
description AS content
FROM film
WHERE title LIKE :wildcard OR description LIKE :wildcard
ORDER BY duration DESC
disabled : False
Query to test: "!demo concert"
This is a rewrite of the implementation from commit [1]
[1] searx/searx@8e90a21
Suggested-by: @virtadpt searx/searx#2808
Diffstat (limited to 'searx/engines/sqlite.py')
-rw-r--r-- | searx/engines/sqlite.py | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/searx/engines/sqlite.py b/searx/engines/sqlite.py new file mode 100644 index 000000000..84db74d62 --- /dev/null +++ b/searx/engines/sqlite.py @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# lint: pylint +# pylint: disable=missing-function-docstring + +"""SQLite database (Offline) + +""" + +import sqlite3 +import contextlib + +from searx import logger + + +logger = logger.getChild('SQLite engine') + +engine_type = 'offline' +database = "" +query_str = "" +limit = 10 +paging = True +result_template = 'key-value.html' + + +def init(engine_settings): + if 'query_str' not in engine_settings: + raise ValueError('query_str cannot be empty') + + if not engine_settings['query_str'].lower().startswith('select '): + raise ValueError('only SELECT query is supported') + + +@contextlib.contextmanager +def sqlite_cursor(): + """Implements a `Context Manager`_ for a :py:obj:`sqlite3.Cursor`. + + Open database in read only mode: if the database doesn't exist. + The default mode creates an empty file on the file system. + + see: + * https://docs.python.org/3/library/sqlite3.html#sqlite3.connect + * https://www.sqlite.org/uri.html + """ + global database # pylint: disable=global-statement + uri = 'file:' + database + '?mode=ro' + with contextlib.closing(sqlite3.connect(uri, uri=True)) as connect: + connect.row_factory = sqlite3.Row + with contextlib.closing(connect.cursor()) as cursor: + yield cursor + + +def search(query, params): + global query_str, result_template # pylint: disable=global-statement + results = [] + + query_params = { + 'query': query, + 'wildcard': r'%' + query.replace(' ', r'%') + r'%', + 'limit': limit, + 'offset': (params['pageno'] - 1) * limit + } + query_to_run = query_str + ' LIMIT :limit OFFSET :offset' + + with sqlite_cursor() as cur: + + cur.execute(query_to_run, query_params) + col_names = [cn[0] for cn in cur.description] + + for row in cur.fetchall(): + item = dict( zip(col_names, map(str, row)) ) + item['template'] = result_template + logger.debug("append result --> %s", item) + results.append(item) + + return results |