From 523bddf987b700216784af58408e5c4bba809539 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Tue, 25 May 2021 19:59:24 +0200 Subject: [PATCH 01/21] [docs] remove blog article about searx-admin - https://github.com/kvch/searx-admin last maintained 4 years ago - searx-admin does not support 'use_default_settings' [1] (b4b81a5e) [1] https://searxng.github.io/searxng/admin/settings.html#use-default-settings Signed-off-by: Markus Heiser --- docs/blog/admin.rst | 43 ------------------------------ docs/blog/index.rst | 1 - docs/blog/searx-admin-engines.png | Bin 50840 -> 0 bytes 3 files changed, 44 deletions(-) delete mode 100644 docs/blog/admin.rst delete mode 100644 docs/blog/searx-admin-engines.png diff --git a/docs/blog/admin.rst b/docs/blog/admin.rst deleted file mode 100644 index e95316192..000000000 --- a/docs/blog/admin.rst +++ /dev/null @@ -1,43 +0,0 @@ -============================================================= -Searx admin interface -============================================================= - -.. _searx-admin: https://github.com/kvch/searx-admin#searx-admin -.. _NLnet Foundation: https://nlnet.nl/ - - manage your instance from your browser - -.. sidebar:: Installation - - Installation guide can be found in the repository of searx-admin_. - -One of the biggest advantages of searx is being extremely customizable. But at -first it can be daunting to newcomers. A barrier of taking advantage of this -feature is our ugly settings file which is sometimes hard to understand and -edit. - -To make self-hosting searx more accessible a new tool is introduced, called -``searx-admin``. It is a web application which is capable of managing your -instance and manipulating its settings via a web UI. It aims to replace editing -of ``settings.yml`` for less experienced administrators or people who prefer -graphical admin interfaces. - -.. figure:: searx-admin-engines.png - :alt: Screenshot of engine list - - Configuration page of engines - -Since ``searx-admin`` acts as a supervisor for searx, we have decided to -implement it as a standalone tool instead of part of searx. Another reason for -making it a standalone tool is that the codebase and dependencies of searx -should not grow because of a fully optional feature, which does not affect -existing instances. - - -Acknowledgements -================ - -This development was sponsored by `NLnet Foundation`_. - -| Happy hacking. -| kvch // 2017.08.22 21:25 diff --git a/docs/blog/index.rst b/docs/blog/index.rst index a396ecaf6..8e0294498 100644 --- a/docs/blog/index.rst +++ b/docs/blog/index.rst @@ -8,7 +8,6 @@ Blog lxcdev-202006 python3 - admin intro-offline private-engines command-line-engines diff --git a/docs/blog/searx-admin-engines.png b/docs/blog/searx-admin-engines.png deleted file mode 100644 index 610bacdf7b5e107d4329893b323e91a53bac40b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50840 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 B5@7%U From a5610835377470b0ce19d8e40f91e48ce4aae4cc Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 09:48:50 +0200 Subject: [PATCH 02/21] [docs] move blog article "Command line engines" to admin/engines/ The article "Command line engines" should be in admin's engine documentation (like the recoll engine). Signed-off-by: Markus Heiser --- docs/admin/engines.rst | 3 ++- .../engines}/command-line-engines.rst | 13 ++++++------- docs/blog/index.rst | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) rename docs/{blog => admin/engines}/command-line-engines.rst (93%) diff --git a/docs/admin/engines.rst b/docs/admin/engines.rst index 3ad206303..520fda35c 100644 --- a/docs/admin/engines.rst +++ b/docs/admin/engines.rst @@ -13,7 +13,8 @@ Special Engine Settings .. toctree:: :maxdepth: 1 - engines/recoll.rst + engines/recoll + engines/command-line-engines .. _engines generic: diff --git a/docs/blog/command-line-engines.rst b/docs/admin/engines/command-line-engines.rst similarity index 93% rename from docs/blog/command-line-engines.rst rename to docs/admin/engines/command-line-engines.rst index 09eb84fb4..b4568f7d5 100644 --- a/docs/blog/command-line-engines.rst +++ b/docs/admin/engines/command-line-engines.rst @@ -1,11 +1,14 @@ -======================================== -Running shell commands to fetch results -======================================== +.. _engine command: + +============================== +Fetch results from commandline +============================== Previously, with searx you could search over the Internet on other people's computers. Now it is possible to fetch results from your local machine without connecting to any networks from the same graphical user interface. +.. _command line engines: Command line engines ==================== @@ -59,7 +62,3 @@ This development was sponsored by `Search and Discovery Fund`_ of `NLnet Foundat .. _Search and Discovery Fund: https://nlnet.nl/discovery .. _NLnet Foundation: https://nlnet.nl/ - - -| Happy hacking. -| kvch // 2020.09.28 21:26 diff --git a/docs/blog/index.rst b/docs/blog/index.rst index 8e0294498..2817f3b2b 100644 --- a/docs/blog/index.rst +++ b/docs/blog/index.rst @@ -10,6 +10,5 @@ Blog python3 intro-offline private-engines - command-line-engines search-indexer-engines sql-engines From 1b3f0c848e9e9785635f19e4c935a9a0a8d4f7f9 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 09:53:28 +0200 Subject: [PATCH 03/21] [docs] move blog article "SQL engines" to admin/engines/ The article "SQL engines" should be in admin's engine documentation (like the recoll engine). Signed-off-by: Markus Heiser --- docs/admin/engines.rst | 1 + docs/{blog => admin/engines}/sql-engines.rst | 31 +++++++++++++------- docs/blog/index.rst | 1 - 3 files changed, 21 insertions(+), 12 deletions(-) rename docs/{blog => admin/engines}/sql-engines.rst (89%) diff --git a/docs/admin/engines.rst b/docs/admin/engines.rst index 520fda35c..c7461d3fd 100644 --- a/docs/admin/engines.rst +++ b/docs/admin/engines.rst @@ -14,6 +14,7 @@ Special Engine Settings :maxdepth: 1 engines/recoll + engines/sql-engines engines/command-line-engines diff --git a/docs/blog/sql-engines.rst b/docs/admin/engines/sql-engines.rst similarity index 89% rename from docs/blog/sql-engines.rst rename to docs/admin/engines/sql-engines.rst index 413316054..ee7f31c27 100644 --- a/docs/blog/sql-engines.rst +++ b/docs/admin/engines/sql-engines.rst @@ -1,5 +1,7 @@ +.. _sql engines: + =========== -SQL engines +SQL Engines =========== .. sidebar:: further read @@ -36,9 +38,10 @@ place the templates at:: searx/templates/{theme_name}/result_templates/{template_name} -As mentioned in previous blog posts, if you do not wish to expose these engines -on a public instance, you can still add them and limit the access by setting -``tokens`` as described in section :ref:`private engines`. +If you do not wish to expose these engines on a public instance, you can still +add them and limit the access by setting ``tokens`` as described in section +:ref:`private engines`. + Configure the engines ===================== @@ -58,6 +61,10 @@ returned results use the option ``limit``. SQLite ------ +.. sidebar:: info + + - :origin:`sqlite.py ` + .. _MediathekView: https://mediathekview.de/ SQLite is a small, fast and reliable SQL database engine. It does not require @@ -106,9 +113,10 @@ PostgreSQL .. _psycopg2: https://www.psycopg.org/install -.. sidebar:: requirements +.. sidebar:: info - ``pip install`` psycopg2_ + - :origin:`postgresql.py ` + - ``pip install`` psycopg2_ PostgreSQL is a powerful and robust open source database. Before configuring the PostgreSQL engine, you must install the dependency ``psychopg2``. You can @@ -130,9 +138,10 @@ MySQL .. _mysql-connector-python: https://pypi.org/project/mysql-connector-python -.. sidebar:: requirements +.. sidebar:: info - ``pip install`` mysql-connector-python_ + - :origin:`mysql_server.py ` + - ``pip install`` mysql-connector-python_ MySQL is said to be the most popular open source database. Before enabling MySQL engine, you must install the package ``mysql-connector-python``. @@ -152,9 +161,9 @@ example configuration for quering a MySQL server: query_str: 'SELECT * from my_table WHERE my_column=%(query)s' -Acknowledgement -=============== +Acknowledgment +============== This development was sponsored by `Search and Discovery Fund -`_ of `NLnet Foundation `_ . +`_ of `NLnet Foundation `_. diff --git a/docs/blog/index.rst b/docs/blog/index.rst index 2817f3b2b..a870c78c6 100644 --- a/docs/blog/index.rst +++ b/docs/blog/index.rst @@ -11,4 +11,3 @@ Blog intro-offline private-engines search-indexer-engines - sql-engines From b88d9d49cc38ee237cce0fd818bb84f01a5f5b88 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 09:09:55 +0200 Subject: [PATCH 04/21] [docs] remove blog article 'Introducing Python 3 support' This article is obsolete since a long time: Python 2 support has been dropped and these days, virtualenv is managed by ``make pyenv.install``. Signed-off-by: Markus Heiser --- docs/blog/index.rst | 1 - docs/blog/python3.rst | 65 ----------------------------------------- docs/blog/searxpy3.png | Bin 30947 -> 0 bytes 3 files changed, 66 deletions(-) delete mode 100644 docs/blog/python3.rst delete mode 100644 docs/blog/searxpy3.png diff --git a/docs/blog/index.rst b/docs/blog/index.rst index a870c78c6..b5b3aaae6 100644 --- a/docs/blog/index.rst +++ b/docs/blog/index.rst @@ -7,7 +7,6 @@ Blog :caption: Contents lxcdev-202006 - python3 intro-offline private-engines search-indexer-engines diff --git a/docs/blog/python3.rst b/docs/blog/python3.rst deleted file mode 100644 index 62d7052fc..000000000 --- a/docs/blog/python3.rst +++ /dev/null @@ -1,65 +0,0 @@ -============================ -Introducing Python 3 support -============================ - -.. _Python 2.7 clock: https://pythonclock.org/ - -.. sidebar:: Python 2.7 to 3 upgrade - - This chapter exists of historical reasons. Python 2.7 release schedule ends - (`Python 2.7 clock`_) after 11 years Python 3 exists - -As most operation systems are coming with Python3 installed by default. So it is -time for searx to support Python3. But don't worry support of Python2.7 won't be -dropped. - -.. image:: searxpy3.png - :scale: 50 % - :alt: hurray - :align: center - - -How to run searx using Python 3 -=============================== - -Please make sure that you run at least Python 3.5. - -To run searx, first a Python3 virtualenv should be created. After entering the -virtualenv, dependencies and searx must be installed. Then run searx from the -command line. - -.. code:: sh - - python3 -m venv venv3 - source venv3/bin/activate - pip install -U pip setuptools wheel pyyaml - pip install -e . - searx-run - -Fun facts -========= - -- 115 files were changed when implementing the support for both Python versions. - -- All of the dependencies was compatible except for the robotframework used for - browser tests. Thus, these tests were migrated to splinter. So from now on - both versions are being tested on Travis and can be tested locally. - -If you found bugs -================= - -Please open an issue on `GitHub`_. Make sure that you mention your Python -version in your issue, so we can investigate it properly. - -.. _GitHub: https://github.com/searxng/searxng/issues - -Acknowledgment -============== - -This development was sponsored by `NLnet Foundation`_. - -.. _NLnet Foundation: https://nlnet.nl/ - - -| Happy hacking. -| kvch // 2017.05.13 22:57 diff --git a/docs/blog/searxpy3.png b/docs/blog/searxpy3.png deleted file mode 100644 index 8eeaeec55b6b633fb1ad02234371c5341acbdbc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30947 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 From acb15772025ea84b1f61a9e67a4cb5f9d4685d43 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 10:07:02 +0200 Subject: [PATCH 05/21] [docs] move blog article "Offline engines" to dev/offline_engines.rst The article "Offline engines" should be in developer's documentation next to chapter "Engine overview". Signed-off-by: Markus Heiser --- docs/blog/index.rst | 1 - docs/dev/index.rst | 1 + docs/{blog/intro-offline.rst => dev/offline_engines.rst} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename docs/{blog/intro-offline.rst => dev/offline_engines.rst} (100%) diff --git a/docs/blog/index.rst b/docs/blog/index.rst index b5b3aaae6..1b2c39596 100644 --- a/docs/blog/index.rst +++ b/docs/blog/index.rst @@ -7,6 +7,5 @@ Blog :caption: Contents lxcdev-202006 - intro-offline private-engines search-indexer-engines diff --git a/docs/dev/index.rst b/docs/dev/index.rst index ba0a25a9c..b33c07da8 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -9,6 +9,7 @@ Developer documentation quickstart contribution_guide engine_overview + offline_engines search_api plugins translation diff --git a/docs/blog/intro-offline.rst b/docs/dev/offline_engines.rst similarity index 100% rename from docs/blog/intro-offline.rst rename to docs/dev/offline_engines.rst From 1c8cf1d3a8ff9ff6c23a092b3a97d19d29840629 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 12:51:14 +0200 Subject: [PATCH 06/21] [docs] add engine "Demo Offline Engine" This engine just exists for documentation purpose. Signed-off-by: Markus Heiser --- docs/src/searx.engines.demo_offline.rst | 9 +++ searx/engines/demo_offline.py | 74 +++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 docs/src/searx.engines.demo_offline.rst create mode 100644 searx/engines/demo_offline.py diff --git a/docs/src/searx.engines.demo_offline.rst b/docs/src/searx.engines.demo_offline.rst new file mode 100644 index 000000000..9424244fd --- /dev/null +++ b/docs/src/searx.engines.demo_offline.rst @@ -0,0 +1,9 @@ +.. _demo offline engine: + +=================== +Demo Offline Engine +=================== + +.. automodule:: searx.engines.demo_offline + :members: + diff --git a/searx/engines/demo_offline.py b/searx/engines/demo_offline.py new file mode 100644 index 000000000..06609d2c3 --- /dev/null +++ b/searx/engines/demo_offline.py @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# lint: pylint +"""Within this module we implement a *demo offline engine*. Do not look to +close to the implementation, its just a simple example. To get in use of this +*demo* engine add the following entry to your engines list in ``settings.yml``: + +.. code:: yaml + + - name: my offline engine + engine: demo_offline + shortcut: demo + disabled: false + +""" + +import json + +engine_type = 'offline' +categories = ['general'] +disabled = True +timeout = 2.0 + +about = { + "wikidata_id": None, + "official_api_documentation": None, + "use_official_api": False, + "require_api_key": False, + "results": 'JSON', +} + +# if there is a need for globals, use a leading underline +_my_offline_engine = None + +def init(engine_settings=None): + """Initialization of the (offline) engine. The origin of this demo engine is a + simple json string which is loaded in this example while the engine is + initialized. + + """ + global _my_offline_engine # pylint: disable=global-statement + + _my_offline_engine = ( + '[ {"value": "%s"}' + ', {"value":"first item"}' + ', {"value":"second item"}' + ', {"value":"third item"}' + ']' + + % engine_settings.get('name') + ) + +def search(query, request_params): + """Query (offline) engine and return results. Assemble the list of results from + your local engine. In this demo engine we ignore the 'query' term, usual + you would pass the 'query' term to your local engine to filter out the + results. + + """ + global _my_offline_engine # pylint: disable=global-statement + ret_val = [] + + result_list = json.loads(_my_offline_engine) + + for row in result_list: + entry = { + 'query' : query, + 'language' : request_params['language'], + 'value' : row.get("value"), + # choose a result template or comment out to use the *default* + 'template' : 'key-value.html', + } + ret_val.append(entry) + + return ret_val From 274288ea9913446c6be0ccd5fae50834bfd1bfe6 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 13:56:00 +0200 Subject: [PATCH 07/21] [docs] revision of the article "Offline engines" This patch is a a complete revision of the article "Offline engines", which also merges the content from the searx-wiki [1] into this article. [1] https://github.com/searx/searx/wiki/Offline-engines Signed-off-by: Markus Heiser --- docs/dev/offline_engines.rst | 119 ++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/docs/dev/offline_engines.rst b/docs/dev/offline_engines.rst index 3a706d038..5b93685e6 100644 --- a/docs/dev/offline_engines.rst +++ b/docs/dev/offline_engines.rst @@ -1,77 +1,78 @@ -=============================== -Preparation for offline engines -=============================== +.. _offline engines: -Offline engines +=============== +Offline Engines =============== -To extend the functionality of searx, offline engines are going to be +.. sidebar:: offline engines + + - :ref:`demo offline engine` + - :ref:`sql engines` + - :ref:`command line engines` + - :origin:`Redis ` + +To extend the functionality of SearxNG, offline engines are going to be introduced. An offline engine is an engine which does not need Internet connection to perform a search and does not use HTTP to communicate. -Offline engines can be configured as online engines, by adding those to the -`engines` list of :origin:`settings.yml `. Thus, searx -finds the engine file and imports it. - -Example skeleton for the new engines: - -.. code:: python - - from subprocess import PIPE, Popen - - categories = ['general'] - offline = True - - def init(settings): - pass - - def search(query, params): - process = Popen(['ls', query], stdout=PIPE) - return_code = process.wait() - if return_code != 0: - raise RuntimeError('non-zero return code', return_code) - - results = [] - line = process.stdout.readline() - while line: - result = parse_line(line) - results.append(results) - - line = process.stdout.readline() - - return results +Offline engines can be configured, by adding those to the `engines` list of +:origin:`settings.yml `. An example skeleton for offline +engines can be found in :ref:`demo offline engine` (:origin:`demo_offline.py +`). -Development progress -==================== +Programming Interface +===================== -First, a proposal has been created as a Github issue. Then it was moved to the -wiki as a design document. You can read it here: :wiki:`Offline-engines`. +:py:func:`init(engine_settings=None) ` + All offline engines can have their own init function to setup the engine before + accepting requests. The function gets the settings from settings.yml as a + parameter. This function can be omitted, if there is no need to setup anything + in advance. -In this development step, searx core was prepared to accept and perform offline -searches. Offline search requests are scheduled together with regular offline -requests. +:py:func:`search(query, params) ` -As offline searches can return arbitrary results depending on the engine, the -current result templates were insufficient to present such results. Thus, a new -template is introduced which is caplable of presenting arbitrary key value pairs -as a table. You can check out the pull request for more details see -:pull-searx:`1700`. + Each offline engine has a function named ``search``. This function is + responsible to perform a search and return the results in a presentable + format. (Where *presentable* means presentable by the selected result + template.) -Next steps -========== + The return value is a list of results retrieved by the engine. + +Engine representation in ``/config`` + If an engine is offline, the attribute ``offline`` is set to ``True``. + +.. _offline requirements: + +Extra Dependencies +================== + +If an offline engine depends on an external tool, SearxNG does not install it by +default. When an administrator configures such engine and starts the instance, +the process returns an error with the list of missing dependencies. Also, +required dependencies will be added to the comment/description of the engine, so +admins can install packages in advance. + +If there is a need to install additional packages in *Python's Virtual +Environment* of your SearxNG instance you need to switch into the environment +(:ref:`searx-src`) first, for this you can use :ref:`searx.sh`:: + + $ sudo utils/searx.sh shell + (searx-pyenv)$ pip install ... + + +Private engines (Security) +========================== + +To limit the access to offline engines, if an instance is available publicly, +administrators can set token(s) for each of the :ref:`private engines`. If a +query contains a valid token, then SearxNG performs the requested private +search. If not, requests from an offline engines return errors. -Today, it is possible to create/run an offline engine. However, it is going to be publicly available for everyone who knows the searx instance. So the next step is to introduce token based access for engines. This way administrators are able to limit the access to private engines. Acknowledgement =============== -This development was sponsored by `Search and Discovery Fund`_ of `NLnet Foundation`_ . - -.. _Search and Discovery Fund: https://nlnet.nl/discovery -.. _NLnet Foundation: https://nlnet.nl/ - - -| Happy hacking. -| kvch // 2019.10.21 17:03 +This development was sponsored by `Search and Discovery Fund +`_ of `NLnet Foundation `_ . From 0d31740cfd20fae749277bb7604253aa0c86c898 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 14:19:32 +0200 Subject: [PATCH 08/21] [docs] move article "XPath Engine" to to admin/engines/ Signed-off-by: Markus Heiser --- docs/admin/engines.rst | 1 + docs/admin/engines/searx.engines.xpath.rst | 9 +++++++++ docs/src/searx.engines.xpath.rst | 9 --------- 3 files changed, 10 insertions(+), 9 deletions(-) create mode 100644 docs/admin/engines/searx.engines.xpath.rst delete mode 100644 docs/src/searx.engines.xpath.rst diff --git a/docs/admin/engines.rst b/docs/admin/engines.rst index c7461d3fd..87613f1ee 100644 --- a/docs/admin/engines.rst +++ b/docs/admin/engines.rst @@ -16,6 +16,7 @@ Special Engine Settings engines/recoll engines/sql-engines engines/command-line-engines + engines/searx.engines.xpath .. _engines generic: diff --git a/docs/admin/engines/searx.engines.xpath.rst b/docs/admin/engines/searx.engines.xpath.rst new file mode 100644 index 000000000..695aa5224 --- /dev/null +++ b/docs/admin/engines/searx.engines.xpath.rst @@ -0,0 +1,9 @@ +.. _xpath engine: + +============ +XPath Engine +============ + +.. automodule:: searx.engines.xpath + :members: + diff --git a/docs/src/searx.engines.xpath.rst b/docs/src/searx.engines.xpath.rst deleted file mode 100644 index 4c73763d1..000000000 --- a/docs/src/searx.engines.xpath.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _xpath_engine: - -================ -The XPath engine -================ - -.. automodule:: searx.engines.xpath - :members: - From d965c634297fc1b6010e45b2050422611d06193b Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 14:54:20 +0200 Subject: [PATCH 09/21] [docs] reorder chapter "Engines & Settings" - Split chapter "Engines" and rename it into "Engines & Settings" - Move docs/admin/engines.rst -> docs/admin/engines/engine_settings.rst Signed-off-by: Markus Heiser --- .../engine_settings.rst} | 23 ++++--------------- docs/admin/engines/index.rst | 21 +++++++++++++++++ docs/admin/index.rst | 2 +- docs/admin/settings.rst | 1 + docs/dev/reST.rst | 4 ++-- docs/dev/search_api.rst | 2 +- 6 files changed, 30 insertions(+), 23 deletions(-) rename docs/admin/{engines.rst => engines/engine_settings.rst} (89%) create mode 100644 docs/admin/engines/index.rst diff --git a/docs/admin/engines.rst b/docs/admin/engines/engine_settings.rst similarity index 89% rename from docs/admin/engines.rst rename to docs/admin/engines/engine_settings.rst index 87613f1ee..9ca5620e2 100644 --- a/docs/admin/engines.rst +++ b/docs/admin/engines/engine_settings.rst @@ -1,28 +1,13 @@ -======= -Engines -======= +.. _general engine settings: -Special Engine Settings +======================= +General Engine Settings ======================= .. sidebar:: Further reading .. + - :ref:`engines-dev` - :ref:`settings engine` - - :ref:`engine settings` & :ref:`engine file` - -.. toctree:: - :maxdepth: 1 - - engines/recoll - engines/sql-engines - engines/command-line-engines - engines/searx.engines.xpath - - -.. _engines generic: - -General Engine Settings -======================= Explanation of the :ref:`general engine configuration` shown in the table :ref:`configured engines`. diff --git a/docs/admin/engines/index.rst b/docs/admin/engines/index.rst new file mode 100644 index 000000000..923b20a2b --- /dev/null +++ b/docs/admin/engines/index.rst @@ -0,0 +1,21 @@ +.. _engines and settings: + +================== +Engines & Settings +================== + +.. sidebar:: Further reading .. + + - :ref:`settings engine` + - :ref:`engine settings` & :ref:`engine file` + +.. toctree:: + :maxdepth: 1 + + engine_settings + recoll + sql-engines + command-line-engines + searx.engines.xpath + + diff --git a/docs/admin/index.rst b/docs/admin/index.rst index c708c4ffa..9be5b6a37 100644 --- a/docs/admin/index.rst +++ b/docs/admin/index.rst @@ -14,10 +14,10 @@ Administrator documentation installation-docker update-searx settings + engines/index api architecture filtron morty - engines plugins buildhosts diff --git a/docs/admin/settings.rst b/docs/admin/settings.rst index d0773467d..3baa83365 100644 --- a/docs/admin/settings.rst +++ b/docs/admin/settings.rst @@ -229,6 +229,7 @@ Engine settings .. sidebar:: Further reading .. + - :ref:`general engine settings` - :ref:`engines-dev` .. code:: yaml diff --git a/docs/dev/reST.rst b/docs/dev/reST.rst index 181d9829d..252120925 100644 --- a/docs/dev/reST.rst +++ b/docs/dev/reST.rst @@ -1281,10 +1281,10 @@ Templating Templating is suitable for documentation which is created generic at the build time. The sphinx-jinja_ extension evaluates jinja_ templates in the :ref:`make install` (with searx modules installed). We use this e.g. to build chapter: -:ref:`engines generic`. Below the jinja directive from the +:ref:`general engine settings`. Below the jinja directive from the :origin:`docs/admin/engines.rst` is shown: -.. literalinclude:: ../admin/engines.rst +.. literalinclude:: ../admin/engines/engine_settings.rst :language: reST :start-after: .. _configured engines: diff --git a/docs/dev/search_api.rst b/docs/dev/search_api.rst index 68fee94bf..76bc01623 100644 --- a/docs/dev/search_api.rst +++ b/docs/dev/search_api.rst @@ -20,7 +20,7 @@ Parameters - :ref:`engines-dev` - :ref:`settings.yml` - - :ref:`engines generic` + - :ref:`general engine settings` ``q`` : required The search query. This string is passed to external search services. Thus, From f8442820890f723e0139db0058156452ec735c34 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 16:15:45 +0200 Subject: [PATCH 10/21] [docs] move blog article "private engines" to admin/engines/ Signed-off-by: Markus Heiser --- docs/admin/engines/index.rst | 1 + docs/admin/engines/private-engines.rst | 49 +++++++++++++++++++ docs/blog/index.rst | 1 - docs/blog/private-engines.rst | 65 -------------------------- 4 files changed, 50 insertions(+), 66 deletions(-) create mode 100644 docs/admin/engines/private-engines.rst delete mode 100644 docs/blog/private-engines.rst diff --git a/docs/admin/engines/index.rst b/docs/admin/engines/index.rst index 923b20a2b..02d6e604b 100644 --- a/docs/admin/engines/index.rst +++ b/docs/admin/engines/index.rst @@ -13,6 +13,7 @@ Engines & Settings :maxdepth: 1 engine_settings + private-engines recoll sql-engines command-line-engines diff --git a/docs/admin/engines/private-engines.rst b/docs/admin/engines/private-engines.rst new file mode 100644 index 000000000..cc6ab2565 --- /dev/null +++ b/docs/admin/engines/private-engines.rst @@ -0,0 +1,49 @@ +.. _private engines: + +============================ +Private Engines (``tokens``) +============================ + +Administrators might find themselves wanting to limit access to some of the +enabled engines on their instances. It might be because they do not want to +expose some private information through :ref:`offline engines`. Or they would +rather share engines only with their trusted friends or colleagues. + +To solve this issue the concept of *private engines* exists. + + +A new option was added to engines named `tokens`. It expects a list of +strings. If the user making a request presents one of the tokens of an engine, +they can access information about the engine and make search requests. + +Example configuration to restrict access to the Arch Linux Wiki engine: + +.. code:: yaml + + - name: arch linux wiki + engine: archlinux + shortcut: al + tokens: [ 'my-secret-token' ] + + +Unless a user has configured the right token, the engine is going +to be hidden from him/her. It is not going to be included in the +list of engines on the Preferences page and in the output of +`/config` REST API call. + +Tokens can be added to one's configuration on the Preferences page +under "Engine tokens". The input expects a comma separated list of +strings. + +The distribution of the tokens from the administrator to the users +is not carved in stone. As providing access to such engines +implies that the admin knows and trusts the user, we do not see +necessary to come up with a strict process. Instead, +we would like to add guidelines to the documentation of the feature. + + +Acknowledgment +============== + +This development was sponsored by `Search and Discovery Fund +`_ of `NLnet Foundation `_. diff --git a/docs/blog/index.rst b/docs/blog/index.rst index 1b2c39596..e7eaa62ea 100644 --- a/docs/blog/index.rst +++ b/docs/blog/index.rst @@ -7,5 +7,4 @@ Blog :caption: Contents lxcdev-202006 - private-engines search-indexer-engines diff --git a/docs/blog/private-engines.rst b/docs/blog/private-engines.rst deleted file mode 100644 index 37b1d4cc5..000000000 --- a/docs/blog/private-engines.rst +++ /dev/null @@ -1,65 +0,0 @@ -================================== -Limit access to your searx engines -================================== - -Administrators might find themselves wanting to limit access to some of the -enabled engines on their instances. It might be because they do not want to -expose some private information through an offline engine. Or they -would rather share engines only with their trusted friends or colleagues. - -.. _private engines: - -Private engines -=============== - -To solve this issue private engines were introduced in :pull-searx:`1823`. -A new option was added to engines named `tokens`. It expects a list -of strings. If the user making a request presents one of the tokens -of an engine, they can access information about the engine -and make search requests. - -Example configuration to restrict access to the Arch Linux Wiki engine: - -.. code:: yaml - - - name : arch linux wiki - engine : archlinux - shortcut : al - tokens : [ 'my-secret-token' ] - - -Unless a user has configured the right token, the engine is going -to be hidden from him/her. It is not going to be included in the -list of engines on the Preferences page and in the output of -`/config` REST API call. - -Tokens can be added to one's configuration on the Preferences page -under "Engine tokens". The input expects a comma separated list of -strings. - -The distribution of the tokens from the administrator to the users -is not carved in stone. As providing access to such engines -implies that the admin knows and trusts the user, we do not see -necessary to come up with a strict process. Instead, -we would like to add guidelines to the documentation of the feature. - -Next steps -========== - -Now that searx has support for both offline engines and private engines, -it is possible to add concrete engines which benefit from these features. -For example engines which search on the local host running the instance. -Be it searching your file system or querying a private database. Be creative -and come up with new solutions which fit your use case. - -Acknowledgement -=============== - -This development was sponsored by `Search and Discovery Fund`_ of `NLnet Foundation`_ . - -.. _Search and Discovery Fund: https://nlnet.nl/discovery -.. _NLnet Foundation: https://nlnet.nl/ - - -| Happy hacking. -| kvch // 2020.02.28 22:26 From 79cc82a4db84515a8c31c988eacc0493746801fa Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 17:59:35 +0200 Subject: [PATCH 11/21] [docs] add engine "Demo Online Engine" This engine just exists for documentation purpose. Signed-off-by: Markus Heiser --- docs/src/searx.engines.demo_online.rst | 9 +++ searx/engines/demo_online.py | 92 ++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 docs/src/searx.engines.demo_online.rst create mode 100644 searx/engines/demo_online.py diff --git a/docs/src/searx.engines.demo_online.rst b/docs/src/searx.engines.demo_online.rst new file mode 100644 index 000000000..0a8c8e985 --- /dev/null +++ b/docs/src/searx.engines.demo_online.rst @@ -0,0 +1,9 @@ +.. _demo online engine: + +================== +Demo Online Engine +================== + +.. automodule:: searx.engines.demo_online + :members: + diff --git a/searx/engines/demo_online.py b/searx/engines/demo_online.py new file mode 100644 index 000000000..a0f736e42 --- /dev/null +++ b/searx/engines/demo_online.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# lint: pylint +"""Within this module we implement a *demo online engine*. Do not look to +close to the implementation, its just a simple example which queries `The Art +Institute of Chicago `_ + +To get in use of this *demo* engine add the following entry to your engines +list in ``settings.yml``: + +.. code:: yaml + + - name: my online engine + engine: demo_online + shortcut: demo + disabled: false + +""" + +from json import loads +from urllib.parse import urlencode + +engine_type = 'offline' +categories = ['general'] +disabled = True +timeout = 2.0 +categories = ['images'] +paging = True +page_size = 20 + +search_api = 'https://api.artic.edu/api/v1/artworks/search?' +image_api = 'https://www.artic.edu/iiif/2/' + +about = { + "website": 'https://www.artic.edu', + "wikidata_id": 'Q239303', + "official_api_documentation": 'http://api.artic.edu/docs/', + "use_official_api": True, + "require_api_key": False, + "results": 'JSON', +} + + +# if there is a need for globals, use a leading underline +_my_online_engine = None + +def init(engine_settings): + """Initialization of the (online) engine. If no initialization is needed, drop + this init function. + + """ + global _my_online_engine # pylint: disable=global-statement + _my_online_engine = engine_settings.get('name') + +def request(query, params): + """Build up the ``params`` for the online request. In this example we build a + URL to fetch images from `artic.edu `__ + + """ + args = urlencode({ + 'q' : query, + 'page' : params['pageno'], + 'fields' : 'id,title,artist_display,medium_display,image_id,date_display,dimensions,artist_titles', + 'limit' : page_size, + }) + params['url'] = search_api + args + return params + +def response(resp): + """Parse out the result items from the response. In this example we parse the + response from `api.artic.edu `__ and filter out all + images. + + """ + results = [] + json_data = loads(resp.text) + + for result in json_data['data']: + + if not result['image_id']: + continue + + results.append({ + 'url': 'https://artic.edu/artworks/%(id)s' % result, + 'title': result['title'] + " (%(date_display)s) // %(artist_display)s" % result, + 'content': result['medium_display'], + 'author': ', '.join(result['artist_titles']), + 'img_src': image_api + '/%(image_id)s/full/843,/0/default.jpg' % result, + 'img_format': result['dimensions'], + 'template': 'images.html' + }) + + return results From 97d801e8a097c33a384d0b44cdbbc61a985373a6 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 18:00:09 +0200 Subject: [PATCH 12/21] [fix] docs/conf.py - docutils docs moved to docutils.sourceforge.io Signed-off-by: Markus Heiser --- docs/conf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 83367229f..09cc22878 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -58,11 +58,11 @@ extlinks['man'] = ('https://manpages.debian.org/jump?q=%s', '') #extlinks['role'] = ( # 'https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-%s', '') extlinks['duref'] = ( - 'https://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#%s', '') + 'https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#%s', '') extlinks['durole'] = ( - 'https://docutils.sourceforge.net/docs/ref/rst/roles.html#%s', '') + 'https://docutils.sourceforge.io/docs/ref/rst/roles.html#%s', '') extlinks['dudir'] = ( - 'https://docutils.sourceforge.net/docs/ref/rst/directives.html#%s', '') + 'https://docutils.sourceforge.io/docs/ref/rst/directives.html#%s', '') extlinks['ctan'] = ( 'https://ctan.org/pkg/%s', 'CTAN: ') From f122cb0e27dbb07cb3b5926ae7910cdc65c8fcbb Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 18:02:47 +0200 Subject: [PATCH 13/21] [fix] typo: online_dictionnary --> online_dictionary Signed-off-by: Markus Heiser --- searx/engines/dictzone.py | 2 +- searx/engines/translated.py | 2 +- searx/search/processors/online_dictionary.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/searx/engines/dictzone.py b/searx/engines/dictzone.py index eaa8b6ab4..4a92a22c3 100644 --- a/searx/engines/dictzone.py +++ b/searx/engines/dictzone.py @@ -17,7 +17,7 @@ about = { "results": 'HTML', } -engine_type = 'online_dictionnary' +engine_type = 'online_dictionary' categories = ['general'] url = 'https://dictzone.com/{from_lang}-{to_lang}-dictionary/{query}' weight = 100 diff --git a/searx/engines/translated.py b/searx/engines/translated.py index 9c53d70ad..8d67ca0bb 100644 --- a/searx/engines/translated.py +++ b/searx/engines/translated.py @@ -13,7 +13,7 @@ about = { "results": 'JSON', } -engine_type = 'online_dictionnary' +engine_type = 'online_dictionary' categories = ['general'] url = 'https://api.mymemory.translated.net/get?q={query}&langpair={from_lang}|{to_lang}{key}' web_url = 'https://mymemory.translated.net/en/{from_lang}/{to_lang}/{query}' diff --git a/searx/search/processors/online_dictionary.py b/searx/search/processors/online_dictionary.py index 11ca0335d..72941d57a 100644 --- a/searx/search/processors/online_dictionary.py +++ b/searx/search/processors/online_dictionary.py @@ -12,9 +12,9 @@ from .online import OnlineProcessor parser_re = re.compile('.*?([a-z]+)-([a-z]+) ([^ ]+)$', re.I) class OnlineDictionaryProcessor(OnlineProcessor): - """Processor class used by ``online_dictionnary`` engines.""" + """Processor class used by ``online_dictionary`` engines.""" - engine_type = 'online_dictionnary' + engine_type = 'online_dictionary' def get_params(self, search_query, engine_category): params = super().get_params(search_query, engine_category) From 5caebb379f85401709fedf8552c8760f51641a11 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 18:53:00 +0200 Subject: [PATCH 14/21] [docs] revision of the article "Engine Overview" This patch revision of the article "Engine Overview": - add links & anchors - improve formating of the tables Signed-off-by: Markus Heiser --- docs/dev/engine_overview.rst | 457 ++++++++++++++++++----------------- 1 file changed, 232 insertions(+), 225 deletions(-) diff --git a/docs/dev/engine_overview.rst b/docs/dev/engine_overview.rst index 2a661ec40..cad92ecb1 100644 --- a/docs/dev/engine_overview.rst +++ b/docs/dev/engine_overview.rst @@ -1,12 +1,20 @@ - .. _engines-dev: =============== -Engine overview +Engine Overview =============== .. _metasearch-engine: https://en.wikipedia.org/wiki/Metasearch_engine +.. sidebar:: Further reading .. + + - :ref:`general engine settings` + - :ref:`settings engine` + +.. contents:: + :depth: 3 + :backlinks: entry + searx is a metasearch-engine_, so it uses different search engines to provide better results. @@ -14,297 +22,296 @@ Because there is no general search API which could be used for every search engine, an adapter has to be built between searx and the external search engines. Adapters are stored under the folder :origin:`searx/engines`. -.. contents:: - :depth: 3 - :backlinks: entry - - .. _general engine configuration: -general engine configuration +General Engine Configuration ============================ It is required to tell searx the type of results the engine provides. The -arguments can be set in the engine file or in the settings file -(normally ``settings.yml``). The arguments in the settings file override -the ones in the engine file. +arguments can be set in the engine file or in the settings file (normally +``settings.yml``). The arguments in the settings file override the ones in the +engine file. -It does not matter if an option is stored in the engine file or in the -settings. However, the standard way is the following: +It does not matter if an option is stored in the engine file or in the settings. +However, the standard way is the following: .. _engine file: -engine file +Engine File ----------- -======================= =========== ======================================================== -argument type information -======================= =========== ======================================================== -categories list pages, in which the engine is working -paging boolean support multible pages -time_range_support boolean support search time range -engine_type str ``online`` by default, other possibles values are - ``offline``, ``online_dictionnary``, ``online_currency`` -======================= =========== ======================================================== +.. table:: Common options in the engine module + :width: 100% + + ======================= =========== ======================================================== + argument type information + ======================= =========== ======================================================== + categories list pages, in which the engine is working + paging boolean support multible pages + time_range_support boolean support search time range + engine_type str - ``online`` :ref:`[ref] ` by + default, other possibles values are: + - ``offline`` :ref:`[ref] ` + - ``online_dictionary`` + - ``online_currency`` + ======================= =========== ======================================================== .. _engine settings: -settings.yml ------------- +Engine ``settings.yml`` +----------------------- -======================= =========== ============================================= -argument type information -======================= =========== ============================================= -name string name of search-engine -engine string name of searx-engine - (filename without ``.py``) -enable_http bool enable HTTP - (by default only HTTPS is enabled). -shortcut string shortcut of search-engine -timeout string specific timeout for search-engine -display_error_messages boolean display error messages on the web UI -proxies dict set proxies for a specific engine - (e.g. ``proxies : {http: socks5://proxy:port, - https: socks5://proxy:port}``) -======================= =========== ============================================= +For a more detailed description, see :ref:`settings engine` in the :ref:`settings.yml`. +.. table:: Common options in the engine setup (``settings.yml``) + :width: 100% -overrides + ======================= =========== =============================================== + argument type information + ======================= =========== =============================================== + name string name of search-engine + engine string name of searx-engine (filename without ``.py``) + enable_http bool enable HTTP (by default only HTTPS is enabled). + shortcut string shortcut of search-engine + timeout string specific timeout for search-engine + display_error_messages boolean display error messages on the web UI + proxies dict set proxies for a specific engine + (e.g. ``proxies : {http: socks5://proxy:port, + https: socks5://proxy:port}``) + ======================= =========== =============================================== + +.. _engine overrides: + +Overrides --------- -A few of the options have default values in the engine, but are often -overwritten by the settings. If ``None`` is assigned to an option in the engine -file, it has to be redefined in the settings, otherwise searx will not start -with that engine. +.. sidebar:: engine's global names -The naming of overrides is arbitrary. But the recommended overrides are the -following: + Global names with a leading underline are *private to the engine* and will + not be overwritten. -======================= =========== =========================================== -argument type information -======================= =========== =========================================== -base_url string base-url, can be overwritten to use same - engine on other URL -number_of_results int maximum number of results per request -language string ISO code of language and country like en_US -api_key string api-key if required by engine -======================= =========== =========================================== +A few of the options have default values in the namespace of engine's python +modul, but are often overwritten by the settings. If ``None`` is assigned to an +option in the engine file, it has to be redefined in the settings, otherwise +searx will not start with that engine. -example code ------------- +Here is an very simple example of the global names in the namespace of engine's +module: .. code:: python # engine dependent config categories = ['general'] paging = True + _non_overwritten_global = 'foo' +.. table:: The naming of overrides is arbitrary / recommended overrides are: + :width: 100% + + ======================= =========== =========================================== + argument type information + ======================= =========== =========================================== + base_url string base-url, can be overwritten to use same + engine on other URL + number_of_results int maximum number of results per request + language string ISO code of language and country like en_US + api_key string api-key if required by engine + ======================= =========== =========================================== + .. _engine request: -making a request +Making a Request ================ To perform a search an URL have to be specified. In addition to specifying an URL, arguments can be passed to the query. -passed arguments ----------------- +.. _engine request arguments: + +Passed Arguments (request) +-------------------------- These arguments can be used to construct the search query. Furthermore, parameters with default value can be redefined for special purposes. -If the ``engine_type`` is ``online```: -====================== ============== ======================================================================== -argument type default-value, information -====================== ============== ======================================================================== -url str ``''`` -method str ``'GET'`` -headers set ``{}`` -data set ``{}`` -cookies set ``{}`` -verify bool ``True`` -headers.User-Agent str a random User-Agent -category str current category, like ``'general'`` -safesearch int ``0``, between ``0`` and ``2`` (normal, moderate, strict) -time_range Optional[str] ``None``, can be ``day``, ``week``, ``month``, ``year`` -pageno int current pagenumber -language str specific language code like ``'en_US'``, or ``'all'`` if unspecified -====================== ============== ======================================================================== +.. table:: If the ``engine_type`` is ``online`` + :width: 100% + + ====================== ============== ======================================================================== + argument type default-value, information + ====================== ============== ======================================================================== + url str ``''`` + method str ``'GET'`` + headers set ``{}`` + data set ``{}`` + cookies set ``{}`` + verify bool ``True`` + headers.User-Agent str a random User-Agent + category str current category, like ``'general'`` + safesearch int ``0``, between ``0`` and ``2`` (normal, moderate, strict) + time_range Optional[str] ``None``, can be ``day``, ``week``, ``month``, ``year`` + pageno int current pagenumber + language str specific language code like ``'en_US'``, or ``'all'`` if unspecified + ====================== ============== ======================================================================== -If the ``engine_type`` is ``online_dictionnary```, in addition to the ``online`` arguments: +.. table:: If the ``engine_type`` is ``online_dictionary``, in addition to the + ``online`` arguments: + :width: 100% -====================== ============ ======================================================================== -argument type default-value, information -====================== ============ ======================================================================== -from_lang str specific language code like ``'en_US'`` -to_lang str specific language code like ``'en_US'`` -query str the text query without the languages -====================== ============ ======================================================================== + ====================== ============== ======================================================================== + argument type default-value, information + ====================== ============== ======================================================================== + from_lang str specific language code like ``'en_US'`` + to_lang str specific language code like ``'en_US'`` + query str the text query without the languages + ====================== ============== ======================================================================== -If the ``engine_type`` is ``online_currency```, in addition to the ``online`` arguments: +.. table:: If the ``engine_type`` is ``online_currency```, in addition to the + ``online`` arguments: + :width: 100% -====================== ============ ======================================================================== -argument type default-value, information -====================== ============ ======================================================================== -amount float the amount to convert -from str ISO 4217 code -to str ISO 4217 code -from_name str currency name -to_name str currency name -====================== ============ ======================================================================== + ====================== ============== ======================================================================== + argument type default-value, information + ====================== ============== ======================================================================== + amount float the amount to convert + from str ISO 4217 code + to str ISO 4217 code + from_name str currency name + to_name str currency name + ====================== ============== ======================================================================== -parsed arguments ----------------- +Specify Request +--------------- -The function ``def request(query, params):`` always returns the ``params`` -variable. Inside searx, the following paramters can be used to specify a search -request: +The function :py:func:`def request(query, params): +` always returns the ``params`` variable, the +following parameters can be used to specify a search request: -=================== =========== ========================================================================== -argument type information -=================== =========== ========================================================================== -url str requested url -method str HTTP request method -headers set HTTP header information -data set HTTP data information -cookies set HTTP cookies -verify bool Performing SSL-Validity check -allow_redirects bool Follow redirects -max_redirects int maximum redirects, hard limit -soft_max_redirects int maximum redirects, soft limit. Record an error but don't stop the engine -raise_for_httperror bool True by default: raise an exception if the HTTP code of response is >= 300 -=================== =========== ========================================================================== +.. table:: + :width: 100% - -example code ------------- - -.. code:: python - - # search-url - base_url = 'https://example.com/' - search_string = 'search?{query}&page={page}' - - # do search-request - def request(query, params): - search_path = search_string.format( - query=urlencode({'q': query}), - page=params['pageno']) - - params['url'] = base_url + search_path - - return params + =================== =========== ========================================================================== + argument type information + =================== =========== ========================================================================== + url str requested url + method str HTTP request method + headers set HTTP header information + data set HTTP data information + cookies set HTTP cookies + verify bool Performing SSL-Validity check + allow_redirects bool Follow redirects + max_redirects int maximum redirects, hard limit + soft_max_redirects int maximum redirects, soft limit. Record an error but don't stop the engine + raise_for_httperror bool True by default: raise an exception if the HTTP code of response is >= 300 + =================== =========== ========================================================================== .. _engine results: +.. _engine media types: -returned results -================ +Media Types +=========== -Searx is able to return results of different media-types. Currently the -following media-types are supported: +Each result item of an engine can be of different media-types. Currently the +following media-types are supported. To set another media-type as ``default``, +the parameter ``template`` must be set to the desired type. -- default_ -- images_ -- videos_ -- torrent_ -- map_ +.. table:: Parameter of the **default** media type: + :width: 100% -To set another media-type as default, the parameter ``template`` must be set to -the desired type. + ========================= ===================================================== + result-parameter information + ========================= ===================================================== + url string, url of the result + title string, title of the result + content string, general result-text + publishedDate :py:class:`datetime.datetime`, time of publish + ========================= ===================================================== -default -------- -========================= ===================================================== -result-parameter information -========================= ===================================================== -url string, url of the result -title string, title of the result -content string, general result-text -publishedDate :py:class:`datetime.datetime`, time of publish -========================= ===================================================== +.. table:: Parameter of the **images** media type: + :width: 100% -images ------- + ========================= ===================================================== + result-parameter information + ------------------------- ----------------------------------------------------- + template is set to ``images.html`` + ========================= ===================================================== + url string, url to the result site + title string, title of the result *(partly implemented)* + content *(partly implemented)* + publishedDate :py:class:`datetime.datetime`, + time of publish *(partly implemented)* + img\_src string, url to the result image + thumbnail\_src string, url to a small-preview image + ========================= ===================================================== -To use this template, the parameter: -========================= ===================================================== -result-parameter information -========================= ===================================================== -template is set to ``images.html`` -url string, url to the result site -title string, title of the result *(partly implemented)* -content *(partly implemented)* -publishedDate :py:class:`datetime.datetime`, - time of publish *(partly implemented)* -img\_src string, url to the result image -thumbnail\_src string, url to a small-preview image -========================= ===================================================== +.. table:: Parameter of the **videos** media type: + :width: 100% -videos ------- - -========================= ===================================================== -result-parameter information -========================= ===================================================== -template is set to ``videos.html`` -url string, url of the result -title string, title of the result -content *(not implemented yet)* -publishedDate :py:class:`datetime.datetime`, time of publish -thumbnail string, url to a small-preview image -========================= ===================================================== - -torrent -------- + ========================= ===================================================== + result-parameter information + ------------------------- ----------------------------------------------------- + template is set to ``videos.html`` + ========================= ===================================================== + url string, url of the result + title string, title of the result + content *(not implemented yet)* + publishedDate :py:class:`datetime.datetime`, time of publish + thumbnail string, url to a small-preview image + ========================= ===================================================== .. _magnetlink: https://en.wikipedia.org/wiki/Magnet_URI_scheme -========================= ===================================================== -result-parameter information -========================= ===================================================== -template is set to ``torrent.html`` -url string, url of the result -title string, title of the result -content string, general result-text -publishedDate :py:class:`datetime.datetime`, - time of publish *(not implemented yet)* -seed int, number of seeder -leech int, number of leecher -filesize int, size of file in bytes -files int, number of files -magnetlink string, magnetlink_ of the result -torrentfile string, torrentfile of the result -========================= ===================================================== +.. table:: Parameter of the **torrent** media type: + :width: 100% + ========================= ===================================================== + result-parameter information + ------------------------- ----------------------------------------------------- + template is set to ``torrent.html`` + ========================= ===================================================== + url string, url of the result + title string, title of the result + content string, general result-text + publishedDate :py:class:`datetime.datetime`, + time of publish *(not implemented yet)* + seed int, number of seeder + leech int, number of leecher + filesize int, size of file in bytes + files int, number of files + magnetlink string, magnetlink_ of the result + torrentfile string, torrentfile of the result + ========================= ===================================================== -map ---- +.. table:: Parameter of the **map** media type: + :width: 100% -========================= ===================================================== -result-parameter information -========================= ===================================================== -url string, url of the result -title string, title of the result -content string, general result-text -publishedDate :py:class:`datetime.datetime`, time of publish -latitude latitude of result (in decimal format) -longitude longitude of result (in decimal format) -boundingbox boundingbox of result (array of 4. values - ``[lat-min, lat-max, lon-min, lon-max]``) -geojson geojson of result (https://geojson.org/) -osm.type type of osm-object (if OSM-Result) -osm.id id of osm-object (if OSM-Result) -address.name name of object -address.road street name of object -address.house_number house number of object -address.locality city, place of object -address.postcode postcode of object -address.country country of object -========================= ===================================================== + ========================= ===================================================== + result-parameter information + ------------------------- ----------------------------------------------------- + template is set to ``map.html`` + ========================= ===================================================== + url string, url of the result + title string, title of the result + content string, general result-text + publishedDate :py:class:`datetime.datetime`, time of publish + latitude latitude of result (in decimal format) + longitude longitude of result (in decimal format) + boundingbox boundingbox of result (array of 4. values + ``[lat-min, lat-max, lon-min, lon-max]``) + geojson geojson of result (https://geojson.org/) + osm.type type of osm-object (if OSM-Result) + osm.id id of osm-object (if OSM-Result) + address.name name of object + address.road street name of object + address.house_number house number of object + address.locality city, place of object + address.postcode postcode of object + address.country country of object + ========================= ===================================================== From c647c8b7a594864a3cfbc881a9bb80efde9d73cf Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Thu, 3 Jun 2021 18:56:39 +0200 Subject: [PATCH 15/21] [docs] revision of the article "settings.yml" This patch is a marginal revision of the article "settings.yml", most changes are from normalizing the YAML syntax. Signed-off-by: Markus Heiser --- docs/admin/settings.rst | 226 ++++++++++++++++++++++------------------ 1 file changed, 125 insertions(+), 101 deletions(-) diff --git a/docs/admin/settings.rst b/docs/admin/settings.rst index 3baa83365..fd99d9587 100644 --- a/docs/admin/settings.rst +++ b/docs/admin/settings.rst @@ -22,13 +22,14 @@ file. settings.yml location ===================== -First, searx will try to load settings.yml from these locations: +The initial ``settings.yml`` we be load from these locations: 1. the full path specified in the ``SEARX_SETTINGS_PATH`` environment variable. 2. ``/etc/searx/settings.yml`` If these files don't exist (or are empty or can't be read), searx uses the -:origin:`searx/settings.yml` file. +:origin:`searx/settings.yml` file. Read :ref:`settings use_default_settings` to +see how you can simplify your *user defined* ``settings.yml``. .. _settings global: @@ -42,17 +43,19 @@ Global Settings .. code:: yaml general: - debug : False # Debug mode, only for development - instance_name : "searxng" # displayed name - contact_url: False # mailto:contact@example.com + debug: false # Debug mode, only for development + instance_name: "searxng" # displayed name + contact_url: false # mailto:contact@example.com + +.. code:: yaml brand: - git_url: https://github.com/searxng/searxng - git_branch: master - issue_url: https://github.com/searxng/searxng/issues - docs_url: https://searxng/searxng.github.io/searxng - public_instances: https://searx.space - wiki_url: https://github.com/searxng/searxng/wiki + git_url: https://github.com/searxng/searxng + git_branch: master + issue_url: https://github.com/searxng/searxng/issues + docs_url: https://searxng/searxng.github.io/searxng + public_instances: https://searx.space + wiki_url: https://github.com/searxng/searxng/wiki ``debug`` : Allow a more detailed log if you run searx directly. Display *detailed* error @@ -68,10 +71,10 @@ Global Settings If you host your own documentation, change this URL. ``wiki_url``: - Link to your wiki (or ``False``) + Link to your wiki (or ``false``) ``twitter_url``: - Link to your tweets (or ``False``) + Link to your tweets (or ``false``) ``server:`` @@ -80,19 +83,19 @@ Global Settings .. code:: yaml server: - port : 8888 - bind_address : "127.0.0.1" # address to listen on - secret_key : "ultrasecretkey" # change this! - base_url : False # set custom base_url (or False) - image_proxy : False # proxying image results through searx - default_locale : "" # default interface locale - default_theme : oscar # ui theme + port: 8888 + bind_address: "127.0.0.1" # address to listen on + secret_key: "ultrasecretkey" # change this! + base_url: false # set custom base_url (or false) + image_proxy: false # proxying image results through searx + default_locale: "" # default interface locale + default_theme: oscar # ui theme default_http_headers: - X-Content-Type-Options : nosniff - X-XSS-Protection : 1; mode=block - X-Download-Options : noopen - X-Robots-Tag : noindex, nofollow - Referrer-Policy : no-referrer + X-Content-Type-Options : nosniff + X-XSS-Protection : 1; mode=block + X-Download-Options : noopen + X-Robots-Tag : noindex, nofollow + Referrer-Policy : no-referrer ``port`` & ``bind_address``: Port number and *bind address* of the searx web application if you run it @@ -125,26 +128,30 @@ Global Settings ``outgoing:`` ------------- +Communication with search engines. + .. code:: yaml - outgoing: # communication with search engines - request_timeout : 2.0 # default timeout in seconds, can be override by engine - # max_request_timeout: 10.0 # the maximum timeout in seconds - useragent_suffix : "" # informations like an email address to the administrator - pool_connections : 100 # Maximum number of allowable connections, or None for no limits. The default is 100. - pool_maxsize : 10 # Number of allowable keep-alive connections, or None to always allow. The default is 10. - enable_http2: True # See https://www.python-httpx.org/http2/ - # uncomment below section if you want to use a proxy - # proxies: - # all://: - # - http://proxy1:8080 - # - http://proxy2:8080 - # uncomment below section only if you have more than one network interface - # which can be the source of outgoing search requests - # source_ips: - # - 1.1.1.1 - # - 1.1.1.2 - # - fe80::/126 + outgoing: + request_timeout: 2.0 # default timeout in seconds, can be override by engine + max_request_timeout: 10.0 # the maximum timeout in seconds + useragent_suffix: "" # informations like an email address to the administrator + pool_connections: 100 # Maximum number of allowable connections, or null + # for no limits. The default is 100. + pool_maxsize: 10 # Number of allowable keep-alive connections, or null + # to always allow. The default is 10. + enable_http2: true # See https://www.python-httpx.org/http2/ + # uncomment below section if you want to use a proxy + # proxies: + # all://: + # - http://proxy1:8080 + # - http://proxy2:8080 + # uncomment below section only if you have more than one network interface + # which can be the source of outgoing search requests + # source_ips: + # - 1.1.1.1 + # - 1.1.1.2 + # - fe80::/126 ``request_timeout`` : @@ -180,8 +187,8 @@ Global Settings * ``[ 192.168.0.1, fe80::/126 ]`` ``retries`` : - Number of retry in case of an HTTP error. - On each retry, searx uses an different proxy and source ip. + Number of retry in case of an HTTP error. On each retry, searx uses an + different proxy and source ip. ``retry_on_http_error`` : Retry request on some HTTP status code. @@ -193,7 +200,7 @@ Global Settings * ``[403, 429]``: on HTTP status code 403 and 429. ``enable_http2`` : - Enable by default. Set to ``False`` to disable HTTP/2. + Enable by default. Set to ``false`` to disable HTTP/2. ``max_redirects`` : 30 by default. Maximum redirect before it is an error. @@ -205,18 +212,18 @@ Global Settings .. code:: yaml locales: - en : English - de : Deutsch - he : Hebrew - hu : Magyar - fr : Français - es : Español - it : Italiano - nl : Nederlands - ja : 日本語 (Japanese) - tr : Türkçe - ru : Russian - ro : Romanian + en: English + de: Deutsch + he: Hebrew + hu: Magyar + fr: Français + es: Español + it: Italiano + nl: Nederlands + ja: 日本語 (Japanese) + tr: Türkçe + ru: Russian + ro: Romanian ``locales`` : Locales codes and their names. Available translations of searx interface. @@ -232,33 +239,46 @@ Engine settings - :ref:`general engine settings` - :ref:`engines-dev` +In the code example below a *full fledged* example of a YAML setup from a dummy +engine is shown. Most of the options have a default value or even are optional. + .. code:: yaml - - name : bing - engine : bing - shortcut : bi - base_url : 'https://{language}.wikipedia.org/' - categories : general - timeout : 3.0 - api_key : 'apikey' - disabled : True - language : en_US - #enable_http: False - #enable_http2: False - #retries: 1 - #retry_on_http_error: True # or 403 or [404, 429] - #max_connections: 100 - #max_keepalive_connections: 10 - #keepalive_expiry: 5.0 - #proxies: - # http: - # - http://proxy1:8080 - # - http://proxy2:8080 - # https: - # - http://proxy1:8080 - # - http://proxy2:8080 - # - socks5://user:password@proxy3:1080 - # - socks5h://user:password@proxy4:1080 + - name: example engine + engine: example + shortcut: demo + base_url: 'https://{language}.example.com/' + categories: general + timeout: 3.0 + api_key: 'apikey' + disabled: false + language: en_US + tokens: [ 'my-secret-token' ] + weigth: 1 + display_error_messages: true + about: + website: https://example.com + wikidata_id: Q306656 + official_api_documentation: https://example.com/api-doc + use_official_api: true + require_api_key: true + results: HTML + enable_http: false + enable_http2: false + retries: 1 + retry_on_http_error: true # or 403 or [404, 429] + max_connections: 100 + max_keepalive_connections: 10 + keepalive_expiry: 5.0 + proxies: + http: + - http://proxy1:8080 + - http://proxy2:8080 + https: + - http://proxy1:8080 + - http://proxy2:8080 + - socks5://user:password@proxy3:1080 + - socks5h://user:password@proxy4:1080 ``name`` : Name that will be used across searx to define this engine. In settings, on @@ -298,10 +318,14 @@ Engine settings by using the full ISO code of language and country, like ``fr_FR``, ``en_US``, ``de_DE``. +``tokens`` : optional + A list of secret tokens to make this engine *private*, more details see + :ref:`private engines`. + ``weigth`` : default ``1`` Weighting of the results of this engine. -``display_error_messages`` : default ``True`` +``display_error_messages`` : default ``true`` When an engine returns an error, the message is displayed on the user interface. ``network``: optional @@ -321,7 +345,7 @@ Engine settings use_default_settings ==================== -.. sidebar:: ``use_default_settings: True`` +.. sidebar:: ``use_default_settings: true`` - :ref:`settings location` - :ref:`use_default_settings.yml` @@ -330,7 +354,7 @@ use_default_settings The user defined ``settings.yml`` is loaded from the :ref:`settings location` and can relied on the default configuration :origin:`searx/settings.yml` using: - ``use_default_settings: True`` + ``use_default_settings: true`` ``server:`` In the following example, the actual settings are the default settings defined @@ -339,22 +363,22 @@ and can relied on the default configuration :origin:`searx/settings.yml` using: .. code-block:: yaml - use_default_settings: True + use_default_settings: true server: - secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA" + secret_key: "ultrasecretkey" # change this! bind_address: "0.0.0.0" ``engines:`` - With ``use_default_settings: True``, each settings can be override in a + With ``use_default_settings: true``, each settings can be override in a similar way, the ``engines`` section is merged according to the engine ``name``. In this example, searx will load all the engine and the arch linux - wiki engine has a :ref:`token`: + wiki engine has a :ref:`token `: .. code-block:: yaml - use_default_settings: True + use_default_settings: true server: - secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA" + secret_key: "ultrasecretkey" # change this! engines: - name: arch linux wiki tokens: ['$ecretValue'] @@ -367,11 +391,11 @@ and can relied on the default configuration :origin:`searx/settings.yml` using: .. code-block:: yaml use_default_settings: - engines: - remove: - - google + engines: + remove: + - google server: - secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA" + secret_key: "ultrasecretkey" # change this! engines: - name: arch linux wiki tokens: ['$ecretValue'] @@ -383,12 +407,12 @@ and can relied on the default configuration :origin:`searx/settings.yml` using: .. code-block:: yaml use_default_settings: - engines: - keep_only: - - google - - duckduckgo + engines: + keep_only: + - google + - duckduckgo server: - secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA" + secret_key: "ultrasecretkey" # change this! engines: - name: google tokens: ['$ecretValue'] From 340d25b19b53582ee6465796d83eb419d2862dcc Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Fri, 4 Jun 2021 11:29:58 +0200 Subject: [PATCH 16/21] [docs] rename 'General Engine Settings' to 'Configured Engines' Signed-off-by: Markus Heiser --- ...ne_settings.rst => configured_engines.rst} | 49 ++++++++++--------- docs/admin/engines/index.rst | 2 +- docs/admin/settings.rst | 2 +- docs/dev/engine_overview.rst | 2 +- docs/dev/reST.rst | 4 +- docs/dev/search_api.rst | 2 +- 6 files changed, 31 insertions(+), 30 deletions(-) rename docs/admin/engines/{engine_settings.rst => configured_engines.rst} (51%) diff --git a/docs/admin/engines/engine_settings.rst b/docs/admin/engines/configured_engines.rst similarity index 51% rename from docs/admin/engines/engine_settings.rst rename to docs/admin/engines/configured_engines.rst index 9ca5620e2..af20e5611 100644 --- a/docs/admin/engines/engine_settings.rst +++ b/docs/admin/engines/configured_engines.rst @@ -1,8 +1,8 @@ -.. _general engine settings: +.. _configured engines: -======================= -General Engine Settings -======================= +================== +Configured Engines +================== .. sidebar:: Further reading .. @@ -12,27 +12,28 @@ General Engine Settings Explanation of the :ref:`general engine configuration` shown in the table :ref:`configured engines`. -============= =========== ==================== ============ -:ref:`engine settings` :ref:`engine file` -------------------------- --------------------------------- -Name (cfg) Categories -------------------------- --------------------------------- -Engine .. Paging support **P** -------------------------- -------------------- ------------ -Shortcut **S** Language support **L** -Timeout **TO** Time range support **TR** -Disabled **D** Engine type **ET** -------------- ----------- -------------------- ------------ -Safe search **SS** -------------- ----------- --------------------------------- -Weigth **W** -------------- ----------- --------------------------------- -Disabled **D** -------------- ----------- --------------------------------- -Show errors **DE** -============= =========== ================================= +.. table:: The legend for the following table + :width: 100% -.. _configured engines: + ============= =========== ==================== ============ + :ref:`engine settings` :ref:`engine file` + ------------------------- --------------------------------- + Name (cfg) .. Categories + ------------- ----------- -------------------- ------------ + Engine .. Paging support **P** + ------------- ----------- -------------------- ------------ + Shortcut **S** Language support **L** + Timeout **TO** Time range support **TR** + Disabled **D** Engine type **ET** + ------------- ----------- -------------------- ------------ + Safe search **SS** + ------------- ----------- --------------------------------- + Weigth **W** + ------------- ----------- --------------------------------- + Disabled **D** + ------------- ----------- --------------------------------- + Show errors **DE** + ============= =========== ================================= .. jinja:: searx diff --git a/docs/admin/engines/index.rst b/docs/admin/engines/index.rst index 02d6e604b..2be4d0e08 100644 --- a/docs/admin/engines/index.rst +++ b/docs/admin/engines/index.rst @@ -12,7 +12,7 @@ Engines & Settings .. toctree:: :maxdepth: 1 - engine_settings + configured_engines private-engines recoll sql-engines diff --git a/docs/admin/settings.rst b/docs/admin/settings.rst index fd99d9587..1fe083a97 100644 --- a/docs/admin/settings.rst +++ b/docs/admin/settings.rst @@ -236,7 +236,7 @@ Engine settings .. sidebar:: Further reading .. - - :ref:`general engine settings` + - :ref:`configured engines` - :ref:`engines-dev` In the code example below a *full fledged* example of a YAML setup from a dummy diff --git a/docs/dev/engine_overview.rst b/docs/dev/engine_overview.rst index cad92ecb1..d79f662b8 100644 --- a/docs/dev/engine_overview.rst +++ b/docs/dev/engine_overview.rst @@ -8,7 +8,7 @@ Engine Overview .. sidebar:: Further reading .. - - :ref:`general engine settings` + - :ref:`configured engines` - :ref:`settings engine` .. contents:: diff --git a/docs/dev/reST.rst b/docs/dev/reST.rst index 252120925..230c92a78 100644 --- a/docs/dev/reST.rst +++ b/docs/dev/reST.rst @@ -1281,10 +1281,10 @@ Templating Templating is suitable for documentation which is created generic at the build time. The sphinx-jinja_ extension evaluates jinja_ templates in the :ref:`make install` (with searx modules installed). We use this e.g. to build chapter: -:ref:`general engine settings`. Below the jinja directive from the +:ref:`configured engines`. Below the jinja directive from the :origin:`docs/admin/engines.rst` is shown: -.. literalinclude:: ../admin/engines/engine_settings.rst +.. literalinclude:: ../admin/engines/configured_engines.rst :language: reST :start-after: .. _configured engines: diff --git a/docs/dev/search_api.rst b/docs/dev/search_api.rst index 76bc01623..5fcdc4560 100644 --- a/docs/dev/search_api.rst +++ b/docs/dev/search_api.rst @@ -20,7 +20,7 @@ Parameters - :ref:`engines-dev` - :ref:`settings.yml` - - :ref:`general engine settings` + - :ref:`configured engines` ``q`` : required The search query. This string is passed to external search services. Thus, From e34dc7818f65f1c2fd2b9625e5446fd8278f1939 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Fri, 4 Jun 2021 11:35:31 +0200 Subject: [PATCH 17/21] [docs] move 'settings.yml' into admin's 'Engines & Settings' Signed-off-by: Markus Heiser --- docs/admin/engines/index.rst | 1 + docs/admin/{ => engines}/settings.rst | 0 docs/admin/index.rst | 1 - 3 files changed, 1 insertion(+), 1 deletion(-) rename docs/admin/{ => engines}/settings.rst (100%) diff --git a/docs/admin/engines/index.rst b/docs/admin/engines/index.rst index 2be4d0e08..51707b792 100644 --- a/docs/admin/engines/index.rst +++ b/docs/admin/engines/index.rst @@ -12,6 +12,7 @@ Engines & Settings .. toctree:: :maxdepth: 1 + settings configured_engines private-engines recoll diff --git a/docs/admin/settings.rst b/docs/admin/engines/settings.rst similarity index 100% rename from docs/admin/settings.rst rename to docs/admin/engines/settings.rst diff --git a/docs/admin/index.rst b/docs/admin/index.rst index 9be5b6a37..3139db99c 100644 --- a/docs/admin/index.rst +++ b/docs/admin/index.rst @@ -13,7 +13,6 @@ Administrator documentation installation-apache installation-docker update-searx - settings engines/index api architecture From 27e5a7437a7af51f38a076a86deb9115c9a345b0 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Fri, 4 Jun 2021 11:48:48 +0200 Subject: [PATCH 18/21] [docs] rename 'Recoll' to 'Recoll Engine' Signed-off-by: Markus Heiser --- docs/admin/engines/recoll.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/admin/engines/recoll.rst b/docs/admin/engines/recoll.rst index cba2e81f7..9b198db7e 100644 --- a/docs/admin/engines/recoll.rst +++ b/docs/admin/engines/recoll.rst @@ -1,17 +1,17 @@ .. _engine recoll: -====== -Recoll -====== +============= +Recoll Engine +============= .. sidebar:: info - `Recoll `_ - `recoll-webui `_ + - :origin:`searx/engines/recoll.py` -Recoll_ is a desktop full-text search tool based on Xapian. By itself Recoll_ -does not offer web or API access, this can be achieved using recoll-webui_ - +Recoll_ is a desktop full-text search tool based on Xapian. By itself Recoll_ +does not offer WEB or API access, this can be achieved using recoll-webui_ Configuration From c5ccd50ffaa4254b6ad9902f6eb89c34e4f22f01 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Fri, 4 Jun 2021 13:09:26 +0200 Subject: [PATCH 19/21] [docs] revision of the blog article about local search engines The blog article 'Query your local search engines' has been renamed 'Local Search Engines', revised and moved into admin's chapter 'Engine & Settings'. Signed-off-by: Markus Heiser --- docs/admin/engines/index.rst | 3 +- docs/admin/engines/search-indexer-engines.rst | 136 ++++++++++++++++++ docs/blog/index.rst | 1 - docs/blog/search-indexer-engines.rst | 114 --------------- 4 files changed, 137 insertions(+), 117 deletions(-) create mode 100644 docs/admin/engines/search-indexer-engines.rst delete mode 100644 docs/blog/search-indexer-engines.rst diff --git a/docs/admin/engines/index.rst b/docs/admin/engines/index.rst index 51707b792..80f4120a5 100644 --- a/docs/admin/engines/index.rst +++ b/docs/admin/engines/index.rst @@ -17,7 +17,6 @@ Engines & Settings private-engines recoll sql-engines + search-indexer-engines command-line-engines searx.engines.xpath - - diff --git a/docs/admin/engines/search-indexer-engines.rst b/docs/admin/engines/search-indexer-engines.rst new file mode 100644 index 000000000..8d7c10d67 --- /dev/null +++ b/docs/admin/engines/search-indexer-engines.rst @@ -0,0 +1,136 @@ +==================== +Local Search Engines +==================== + +.. sidebar:: further read + + - `Comparison to alternatives + `_ + +Administrators might find themselves wanting to integrate locally running search +engines. The following ones are supported for now: + +* `Elasticsearch`_ +* `Meilisearch`_ +* `Solr`_ + +Each search engine is powerful, capable of full-text search. All of the engines +above are added to ``settings.yml`` just commented out, as you have to +``base_url`` for all them. + +Please note that if you are not using HTTPS to access these engines, you have to enable +HTTP requests by setting ``enable_http`` to ``True``. + +Futhermore, if you do not want to expose these engines on a public instance, you +can still add them and limit the access by setting ``tokens`` as described in +section :ref:`private engines`. + +.. _engine meilisearch: + +MeiliSearch +=========== + +.. sidebar:: info + + - :origin:`meilisearch.py ` + - `MeiliSearch `_ + - `MeiliSearch Documentation `_ + - `Install MeiliSearch + `_ + +MeiliSearch_ is aimed at individuals and small companies. It is designed for +small-scale (less than 10 million documents) data collections. E.g. it is great +for storing web pages you have visited and searching in the contents later. + +The engine supports faceted search, so you can search in a subset of documents +of the collection. Furthermore, you can search in MeiliSearch_ instances that +require authentication by setting ``auth_token``. + +Here is a simple example to query a Meilisearch instance: + +.. code:: yaml + + - name: meilisearch + engine: meilisearch + shortcut: mes + base_url: http://localhost:7700 + index: my-index + enable_http: true + + +.. _engine elasticsearch: + +Elasticsearch +============= + +.. sidebar:: info + + - :origin:`elasticsearch.py ` + - `Elasticsearch `_ + - `Elasticsearch Guide + `_ + - `Install Elasticsearch + `_ + +Elasticsearch_ supports numerous ways to query the data it is storing. At the +moment the engine supports the most popular search methods (``query_type``): + +- ``match``, +- ``simple_query_string``, +- ``term`` and +- ``terms``. + +If none of the methods fit your use case, you can select ``custom`` query type +and provide the JSON payload to submit to Elasticsearch in +``custom_query_json``. + +The following is an example configuration for an Elasticsearch_ instance with +authentication configured to read from ``my-index`` index. + +.. code:: yaml + + - name: elasticsearch + shortcut: es + engine: elasticsearch + base_url: http://localhost:9200 + username: elastic + password: changeme + index: my-index + query_type: match + # custom_query_json: '{ ... }' + enable_http: true + +.. _engine solr: + +Solr +==== + +.. sidebar:: info + + - :origin:`solr.py ` + - `Solr `_ + - `Solr Resources `_ + - `Install Solr `_ + +Solr_ is a popular search engine based on Lucene, just like Elasticsearch_. But +instead of searching in indices, you can search in collections. + +This is an example configuration for searching in the collection +``my-collection`` and get the results in ascending order. + +.. code:: yaml + + - name: solr + engine: solr + shortcut: slr + base_url: http://localhost:8983 + collection: my-collection + sort: asc + enable_http: true + + +Acknowledgment +============== + +This development was sponsored by `Search and Discovery Fund +`_ of `NLnet Foundation `_. diff --git a/docs/blog/index.rst b/docs/blog/index.rst index e7eaa62ea..245ef494c 100644 --- a/docs/blog/index.rst +++ b/docs/blog/index.rst @@ -7,4 +7,3 @@ Blog :caption: Contents lxcdev-202006 - search-indexer-engines diff --git a/docs/blog/search-indexer-engines.rst b/docs/blog/search-indexer-engines.rst deleted file mode 100644 index ca4dd3c88..000000000 --- a/docs/blog/search-indexer-engines.rst +++ /dev/null @@ -1,114 +0,0 @@ -=============================== -Query your local search engines -=============================== - -From now on, searx lets you to query your locally running search engines. The following -ones are supported now: - -* `Elasticsearch`_ -* `Meilisearch`_ -* `Solr`_ - -All of the engines above are added to ``settings.yml`` just commented out, as you have to -``base_url`` for all them. - -Please note that if you are not using HTTPS to access these engines, you have to enable -HTTP requests by setting ``enable_http`` to ``True``. - -Futhermore, if you do not want to expose these engines on a public instance, you can -still add them and limit the access by setting ``tokens`` as described in the `blog post about -private engines`_. - -Configuring searx for search engines -==================================== - -Each search engine is powerful, capable of full-text search. - -Elasticsearch -------------- - -Elasticsearch supports numerous ways to query the data it is storing. At the moment -the engine supports the most popular search methods: ``match``, ``simple_query_string``, ``term`` and ``terms``. - -If none of the methods fit your use case, you can select ``custom`` query type and provide the JSON payload -searx has to submit to Elasticsearch in ``custom_query_json``. - -The following is an example configuration for an Elasticsearch instance with authentication -configured to read from ``my-index`` index. - -.. code:: yaml - - - name : elasticsearch - shortcut : es - engine : elasticsearch - base_url : http://localhost:9200 - username : elastic - password : changeme - index : my-index - query_type : match - enable_http : True - - -Meilisearch ------------ - -This search engine is aimed at individuals and small companies. It is designed for -small-scale (less than 10 million documents) data collections. E.g. it is great for storing -web pages you have visited and searching in the contents later. - -The engine supports faceted search, so you can search in a subset of documents of the collection. -Futhermore, you can search in Meilisearch instances that require authentication by setting ``auth_token``. - -Here is a simple example to query a Meilisearch instance: - -.. code:: yaml - - - name : meilisearch - engine : meilisearch - shortcut: mes - base_url : http://localhost:7700 - index : my-index - enable_http: True - - -Solr ----- - -Solr is a popular search engine based on Lucene, just like Elasticsearch. -But instead of searching in indices, you can search in collections. - -This is an example configuration for searching in the collection ``my-collection`` and get -the results in ascending order. - -.. code:: yaml - - - name : solr - engine : solr - shortcut : slr - base_url : http://localhost:8983 - collection : my-collection - sort : asc - enable_http : True - - -Next steps -========== - -The next step is to add support for various SQL databases. - -Acknowledgement -=============== - -This development was sponsored by `Search and Discovery Fund`_ of `NLnet Foundation`_ . - -.. _blog post about private engines: private-engines.html#private-engines -.. _Elasticsearch: https://www.elastic.co/elasticsearch/ -.. _Meilisearch: https://www.meilisearch.com/ -.. _Solr: https://solr.apache.org/ -.. _Search and Discovery Fund: https://nlnet.nl/discovery -.. _NLnet Foundation: https://nlnet.nl/ - - -| Happy hacking. -| kvch // 2021.04.07 23:16 - From 5bea04869dedb616a702e5ca612315752e8bba94 Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Fri, 4 Jun 2021 14:16:02 +0200 Subject: [PATCH 20/21] [docs] move LXC blog article into developer documentation. Move article 'Developing in Linux Containers' from blog section do developer section. Since there are no more articles in the blog section, remove the section completely. Signed-off-by: Markus Heiser --- docs/blog/index.rst | 9 -- docs/dev/index.rst | 1 + .../lxcdev-202006.rst => dev/lxcdev.rst} | 99 ++++++++++--------- docs/dev/makefile.rst | 4 +- docs/index.rst | 1 - 5 files changed, 54 insertions(+), 60 deletions(-) delete mode 100644 docs/blog/index.rst rename docs/{blog/lxcdev-202006.rst => dev/lxcdev.rst} (78%) diff --git a/docs/blog/index.rst b/docs/blog/index.rst deleted file mode 100644 index 245ef494c..000000000 --- a/docs/blog/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -==== -Blog -==== - -.. toctree:: - :maxdepth: 2 - :caption: Contents - - lxcdev-202006 diff --git a/docs/dev/index.rst b/docs/dev/index.rst index b33c07da8..93c914bbb 100644 --- a/docs/dev/index.rst +++ b/docs/dev/index.rst @@ -13,5 +13,6 @@ Developer documentation search_api plugins translation + lxcdev makefile reST diff --git a/docs/blog/lxcdev-202006.rst b/docs/dev/lxcdev.rst similarity index 78% rename from docs/blog/lxcdev-202006.rst rename to docs/dev/lxcdev.rst index 24d9028c2..ef26e3734 100644 --- a/docs/blog/lxcdev-202006.rst +++ b/docs/dev/lxcdev.rst @@ -1,48 +1,48 @@ -.. _blog-lxcdev-202006: +.. _lxcdev: -======================================= -Developing in Linux containers [202006] -======================================= +============================== +Developing in Linux Containers +============================== .. _LXC: https://linuxcontainers.org/lxc/introduction/ +In this article we will show, how you can make use of Linux Containers (LXC_) in +*distributed and heterogeneous development cycles* (TL;DR; jump to the +:ref:`lxcdev summary`). + .. sidebar:: Audience - This blog post is written for experienced admins and developers / readers - should have a serious meaning about: *distributed*, *merge* and *linux - container*. + This blog post is written for experienced admins and developers. Readers + should have a serious meaning about the terms: *distributed*, *merge* and + *linux container*. .. contents:: Contents :depth: 2 :local: :backlinks: entry -In PR :PR:`1803` we added a lot of scripts to Searx's boilerplate. In this blog -post I will show you, how you can make use of them in *distributed and -heterogeneous development cycles* (TL;DR; jump to the :ref:`blog-lxcdev-202006 -abstract`). Motivation ========== -Normally in our development cycle, we edit the sources and run some test and/or -builds by using ``make`` before we commit. This cycle is simple and perfect but -might fail in some aspects we should not overlook. +Usually in our development cycle, we edit the sources and run some test and/or +builds by using ``make`` :ref:`[ref] ` before we commit. This cycle +is simple and perfect but might fail in some aspects we should not overlook. - The environment in which we run all our development processes matters! + **The environment in which we run all our development processes matters!** -The :ref:`makefile` and the :ref:`make install` encapsulate a lot for us, but they -do not have access to all prerequisites. For example, there may have +The :ref:`makefile` and the :ref:`make install` encapsulate a lot for us, but +they do not have access to all prerequisites. For example, there may have dependencies on packages that are installed on the developer's desktop, but -usually are not preinstalled on a server or client system. Another examples -are; settings have been made to the software on the developer's host that would -never be set on a *production* system. +usually are not preinstalled on a server or client system. Another example is; +settings have been made to the software on developer's desktop that would never +be set on a *production* system. -*Linux Containers* (LXC_) are isolate environments and not to mix up on -developer's all the prerequisites of all the projects he contribute to, is -always a good choice. + **Linux Containers are isolate environments and not to mix up all the + prerequisites from various projects on developer's desktop is always a good + choice.** -The scripts from PR :PR:`1803` can divide in those to install and maintain +The scripts from :ref:`searx_utils` can divide in those to install and maintain software: - :ref:`searx.sh` @@ -50,8 +50,10 @@ software: - :ref:`morty.sh` and the script :ref:`lxc.sh`, with we can scale our installation, maintenance or -even development tasks over a stack of containers, what we call: *Searx's lxc -suite*. +even development tasks over a stack of isolated containers / what we call the: + + **searxNG LXC suite** + Gentlemen, start your engines! ============================== @@ -141,18 +143,18 @@ and once for the content sanitizer (content proxy morty): ... INFO: got 200 from http://10.174.184.156/morty/ -.. sidebar:: Fully functional searx suite +.. sidebar:: Fully functional searXNG suite - From here on you have a fully functional searx suite running with bot blocker - (filtron) and Web content sanitizer (content proxy morty) needed for a - *privacy protecting* search engine. + From here on you have a fully functional searXNG suite running with bot + blocker (filtron) and WEB content sanitizer (content proxy morty), both are + needed for a *privacy protecting* search engine. On your system, the IP of your ``searx-archlinux`` container differs from http://10.174.184.156/searx, just open the URL reported in your installation protocol in your WEB browser from the desktop to test the instance from outside of the container. -In such a searx suite admins can maintain and access the debug log of the +In such a searXNG suite admins can maintain and access the debug log of the different services quite easy. .. _working in containers: @@ -176,7 +178,7 @@ searx-archlinux``: /share/searx The prompt ``[root@searx-archlinux ...]`` signals, that you are the root user in -the searx-container. To debug the running searx instance use: +the searx-container. To debug the running searXNG instance use: .. tabs:: @@ -192,8 +194,8 @@ the searx-container. To debug the running searx instance use: Back in the browser on your desktop open the service http://10.174.184.156/searx and run your application tests while the debug log is shown in the terminal from above. You can stop monitoring using ``CTRL-C``, this also disables the *"debug -option"* in searx's settings file and restarts the searx uwsgi application. To -debug services from filtron and morty analogous use: +option"* in searXNG's settings file and restarts the searXNG uwsgi application. +To debug services from filtron and morty analogous use: .. tabs:: @@ -250,18 +252,18 @@ user ``searx`` in the ``searx-archlinux`` container and the python *virtualenv* Wrap production into developer suite ==================================== -In this section we will see how to change the *"Fully functional searx suite"* +In this section we will see how to change the *"Fully functional searXNG suite"* from a LXC container (which is quite ready for production) into a developer suite. For this, we have to keep an eye on the :ref:`installation basic`: -- searx setup in: ``/etc/searx/settings.yml`` -- searx user's home: ``/usr/local/searx`` +- searXNG setup in: ``/etc/searx/settings.yml`` +- searXNG user's home: ``/usr/local/searx`` - virtualenv in: ``/usr/local/searx/searx-pyenv`` -- searx software in: ``/usr/local/searx/searx-src`` +- searXNG software in: ``/usr/local/searx/searx-src`` -The searx software is a clone of the ``git_url`` (see :ref:`settings global`) and -the working tree is checked out from the ``git_branch``. With the use of the -:ref:`searx.sh` the searx service was installed as :ref:`uWSGI application +The searXNG software is a clone of the ``git_url`` (see :ref:`settings global`) +and the working tree is checked out from the ``git_branch``. With the use of +the :ref:`searx.sh` the searx service was installed as :ref:`uWSGI application `. To maintain this service, we can use ``systemctl`` (compare :ref:`service architectures on distributions `). @@ -292,7 +294,7 @@ least you should attend the settings of ``uid``, ``chdir``, ``env`` and If you have read the :ref:`"Good to know section" ` you remember, that each container shares the root folder of the repository and the command ``utils/lxc.sh cmd`` handles relative path names **transparent**. To wrap the -searx installation into a developer one, we simple have to create a smylink to +searXNG installation into a developer one, we simple have to create a smylink to the **transparent** reposetory from the desktop. Now lets replace the repository at ``searx-src`` in the container with the working tree from outside of the container: @@ -330,7 +332,7 @@ daily usage: .. group-tab:: desktop - To *inspect* the searx instance (already described above): + To *inspect* the searXNG instance (already described above): .. code:: sh @@ -358,12 +360,12 @@ daily usage: $ sudo -H ./utils/lxc.sh cmd searx-archlinux \ make docs.html -.. _blog-lxcdev-202006 abstract: +.. _lxcdev summary: -Abstract -======== +Summary +======= -We build up a fully functional searx suite in a archlinux container: +We build up a fully functional searXNG suite in a archlinux container: .. code:: sh @@ -395,7 +397,8 @@ the container : $ ln -s /share/searx/ /usr/local/searx/searx-src $ systemctl restart uwsgi@searx -To get remarks from the suite of the archlinux container we can use: +To get information about the searxNG suite in the archlinux container we can +use: .. tabs:: diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst index 870b5d49c..b7472dad7 100644 --- a/docs/dev/makefile.rst +++ b/docs/dev/makefile.rst @@ -29,8 +29,8 @@ Calling the ``help`` target gives a first overview (``make help``): .. _make install: -Python environment -================== +Python Environment (``make install``) +===================================== .. sidebar:: activate environment diff --git a/docs/index.rst b/docs/index.rst index 71f0d8855..2b7bdeb37 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,7 +34,6 @@ anyone, you can set up your own, see :ref:`installation`. dev/index searx_extra/index utils/index - blog/index src/index .. _Searx-instances: https://searx.space From 6f7b0d72c084845ea073a82a357c5e99cd41a85f Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Fri, 4 Jun 2021 15:02:47 +0200 Subject: [PATCH 21/21] [docs] revision of the section 'Command Line Engines' Signed-off-by: Markus Heiser --- docs/admin/engines/command-line-engines.rst | 99 ++++++++++++--------- docs/dev/offline_engines.rst | 2 +- 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/docs/admin/engines/command-line-engines.rst b/docs/admin/engines/command-line-engines.rst index b4568f7d5..e9535e74f 100644 --- a/docs/admin/engines/command-line-engines.rst +++ b/docs/admin/engines/command-line-engines.rst @@ -1,41 +1,64 @@ .. _engine command: -============================== -Fetch results from commandline -============================== - -Previously, with searx you could search over the Internet on other people's -computers. Now it is possible to fetch results from your local machine without -connecting to any networks from the same graphical user interface. - -.. _command line engines: - -Command line engines +==================== +Command Line Engines ==================== -In :pull-searx:`2128` a new type of engine has been introduced called ``command``. -This engine lets administrators add engines which run arbitrary shell commands -and show its output on the web UI of searx. +.. sidebar:: info -When creating and enabling a ``command`` engine on a public searx instance, -you must be careful to avoid leaking private data. The easiest solution -is to add tokens to the engine. Thus, only those who have the appropriate token -can retrieve results from the it. + - :origin:`command.py ` + - :ref:`offline engines` -The engine base is flexible. Only your imagination can limit the power of this engine. (And -maybe security concerns.) The following options are available: +With *command engines* administrators can run engines to integrate arbitrary +shell commands. -* ``command``: A comma separated list of the elements of the command. A special token {{QUERY}} tells searx where to put the search terms of the user. Example: ``['ls', '-l', '-h', '{{QUERY}}']`` -* ``delimiter``: A dict containing a delimiter char and the "titles" of each element in keys. -* ``parse_regex``: A dict containing the regular expressions for each result key. -* ``query_type``: The expected type of user search terms. Possible values: ``path`` and ``enum``. ``path`` checks if the uesr provided path is inside the working directory. If not the query is not executed. ``enum`` is a list of allowed search terms. If the user submits something which is not included in the list, the query returns an error. -* ``query_enum``: A list containing allowed search terms if ``query_type`` is set to ``enum``. -* ``working_dir``: The directory where the command has to be executed. Default: ``.`` -* ``result_separator``: The character that separates results. Default: ``\n`` - +When creating and enabling a ``command`` engine on a public instance, you must +be careful to avoid leaking private data. The easiest solution is to limit the +access by setting ``tokens`` as described in section :ref:`private engines`. -The example engine below can be used to find files with a specific name in the configured -working directory. +The engine base is flexible. Only your imagination can limit the power of this +engine (and maybe security concerns). The following options are available: + +``command``: + A comma separated list of the elements of the command. A special token + ``{{QUERY}}`` tells where to put the search terms of the user. Example: + + .. code:: yaml + + ['ls', '-l', '-h', '{{QUERY}}'] + +``delimiter``: + A mapping containing a delimiter ``char`` and the *titles* of each element in + ``keys``. + +``parse_regex``: + A dict containing the regular expressions for each result key. + +``query_type``: + + The expected type of user search terms. Possible values: ``path`` and + ``enum``. + + ``path``: + Checks if the user provided path is inside the working directory. If not, + the query is not executed. + + ``enum``: + Is a list of allowed search terms. If the user submits something which is + not included in the list, the query returns an error. + +``query_enum``: + A list containing allowed search terms if ``query_type`` is set to ``enum``. + +``working_dir``: + + The directory where the command has to be executed. Default: ``./`` + +``result_separator``: + The character that separates results. Default: ``\n`` + +The example engine below can be used to find files with a specific name in the +configured working directory: .. code:: yaml @@ -49,16 +72,8 @@ working directory. keys: ['line'] -Next steps -========== +Acknowledgment +============== -In the next milestone, support for local search engines and indexers (e.g. Elasticsearch) -are going to be added. This way, you will be able to query your own databases/indexers. - -Acknowledgement -=============== - -This development was sponsored by `Search and Discovery Fund`_ of `NLnet Foundation`_ . - -.. _Search and Discovery Fund: https://nlnet.nl/discovery -.. _NLnet Foundation: https://nlnet.nl/ +This development was sponsored by `Search and Discovery Fund +`_ of `NLnet Foundation `_. diff --git a/docs/dev/offline_engines.rst b/docs/dev/offline_engines.rst index 5b93685e6..ce6924542 100644 --- a/docs/dev/offline_engines.rst +++ b/docs/dev/offline_engines.rst @@ -8,7 +8,7 @@ Offline Engines - :ref:`demo offline engine` - :ref:`sql engines` - - :ref:`command line engines` + - :ref:`engine command` - :origin:`Redis ` To extend the functionality of SearxNG, offline engines are going to be