From 2ea34a3c36d444fa5afac381a417d66bc96849e5 Mon Sep 17 00:00:00 2001
From: Alexandre Flament <alex@al-f.net>
Date: Thu, 27 May 2021 14:27:11 +0200
Subject: [PATCH] [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
---
 searx/engines/sqlite.py | 75 +++++++++++++++++++++++++++++++++++++++++
 searx/settings.yml      | 20 +++++++++++
 2 files changed, 95 insertions(+)
 create mode 100644 searx/engines/sqlite.py

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
diff --git a/searx/settings.yml b/searx/settings.yml
index b0c425e4f..21c4e8e0e 100644
--- a/searx/settings.yml
+++ b/searx/settings.yml
@@ -1003,6 +1003,26 @@ engines:
     timeout : 3.0
     disabled : True
 
+  # For this demo of the sqlite engine download:
+  #   https://liste.mediathekview.de/filmliste-v2.db.bz2
+  # and unpack into searx/data/filmliste-v2.db
+  # Query to test: "!demo concert"
+  #
+  # - 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
+
   - name : torrentz
     engine : torrentz
     shortcut : tor