summaryrefslogtreecommitdiff
path: root/searx/engines/sqlite.py
diff options
context:
space:
mode:
authorAlexandre Flament <alex@al-f.net>2021-05-27 14:27:11 +0200
committerAlexandre Flament <alex@al-f.net>2021-05-27 14:27:11 +0200
commit2ea34a3c36d444fa5afac381a417d66bc96849e5 (patch)
treea806f5a2660767e3bb33fd2a4ca9355ca98da722 /searx/engines/sqlite.py
parent7c76cef746a90542f7db7a9ca876e763d0aad1a3 (diff)
downloadsearxng-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.py75
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