[enh] Allowing using multiple autocomplete engines

This commit is contained in:
Allen 2024-07-01 15:50:52 +00:00 committed by GitHub
parent 0fb3f0e4ae
commit f0c8e50d5e
7 changed files with 58 additions and 11 deletions

View file

@ -11,6 +11,7 @@ import lxml
from httpx import HTTPError
from searx import settings
from searx.utils import unique
from searx.engines import (
engines,
google,
@ -252,11 +253,31 @@ backends = {
}
def search_autocomplete(backend_name, query, sxng_locale):
backend = backends.get(backend_name)
if backend is None:
return []
def search_autocomplete(backend_names, query, sxng_locale):
enabled_backends = list(unique(backend_names))
len_enabled_backends = len(enabled_backends)
try:
return backend(query, sxng_locale)
results = []
for backend_name in enabled_backends:
backend = backends.get(backend_name)
if backend is None:
# if somehow 'searx.preferences.ValidationException' was not raised
continue
backend_results = backend(query, sxng_locale)
if (len_enabled_backends > 2) and (len(backend_results) > 3):
# if more than 2 autocompleters: only get the first 3 results from each
results.extend(backend_results[:3])
else:
results.extend(backend_results)
return list(unique(results))
except (HTTPError, SearxEngineResponseException):
return []

View file

@ -96,6 +96,10 @@ class MultipleChoiceSetting(Setting):
"""Setting of values which can only come from the given choices"""
def __init__(self, default_value: List[str], choices: Iterable[str], locked=False):
# backwards compat for autocomplete setting (was string, now is a list of strings)
if isinstance(default_value, str):
default_value = [str(val) for val in default_value.split(",")]
super().__init__(default_value, locked)
self.choices = choices
self._validate_selections(self.value)
@ -401,7 +405,7 @@ class Preferences:
locked=is_locked('locale'),
choices=list(LOCALE_NAMES.keys()) + ['']
),
'autocomplete': EnumStringSetting(
'autocomplete': MultipleChoiceSetting(
settings['search']['autocomplete'],
locked=is_locked('autocomplete'),
choices=list(autocomplete.backends.keys()) + ['']

View file

@ -29,10 +29,12 @@ brand:
search:
# Filter results. 0: None, 1: Moderate, 2: Strict
safe_search: 0
# Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "yandex", "mwmbl",
# "seznam", "startpage", "stract", "swisscows", "qwant", "wikipedia" - leave blank to turn it off
# by default.
autocomplete: ""
# Existing autocomplete backends: "dbpedia", "duckduckgo", "google",
# "yandex", "mwmbl", "seznam", "startpage", "stract", "swisscows", "qwant", "wikipedia"
# Uncomment the section below and edit/add/remove each engine to turn them on by default.
# autocomplete:
# - duckduckgo
# - stract
# minimun characters to type before autocompleter starts
autocomplete_min: 4
# Default search language - leave blank to detect from browser information or

View file

@ -154,7 +154,7 @@ SCHEMA = {
},
'search': {
'safe_search': SettingsValue((0, 1, 2), 0),
'autocomplete': SettingsValue(str, ''),
'autocomplete': SettingsValue((list, str, False), ['']),
'autocomplete_min': SettingsValue(int, 4),
'default_lang': SettingsValue(tuple(SXNG_LOCALE_TAGS + ['']), ''),
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS),

View file

@ -329,6 +329,17 @@ def dict_subset(dictionary: MutableMapping, properties: Set[str]) -> Dict:
return {k: dictionary[k] for k in properties if k in dictionary}
def unique(iterable):
"""Yield unique elements from 'iterable' while preserving order
https://github.com/mikf/gallery-dl/blob/e03b99ba0ecbf653b89e68d00245da78694071fb/gallery_dl/util.py#L64C1-L71C26"""
seen = set()
add = seen.add
for element in iterable:
if element not in seen:
add(element)
yield element
def get_torrent_size(filesize: str, filesize_multiplier: str) -> Optional[int]:
"""

View file

@ -66,6 +66,14 @@ class TestSettings(SearxTestCase): # pylint: disable=missing-class-docstring
# multiple choice settings
def test_multiple_setting_default_value_invalid_commma_seperated(self):
with self.assertRaises(ValidationException):
MultipleChoiceSetting("duckduckgo,doesnotexist", choices=['duckduckgo', 'stract', 'qwant'])
def test_multiple_setting_default_value_valid_commma_seperated(self):
setting = MultipleChoiceSetting("duckduckgo,stract", choices=['duckduckgo', 'stract', 'qwant'])
self.assertEqual(setting.get_value(), ['duckduckgo', 'stract'])
def test_multiple_setting_invalid_default_value(self):
with self.assertRaises(ValidationException):
MultipleChoiceSetting(['3', '4'], choices=['0', '1', '2'])

View file

@ -121,3 +121,4 @@ class TestUserSettings(SearxTestCase): # pylint: disable=missing-class-docstrin
self.assertEqual(settings['server']['secret_key'], "user_settings_secret")
engine_names = [engine['name'] for engine in settings['engines']]
self.assertEqual(engine_names, ['wikidata', 'wikibooks', 'wikinews', 'wikiquote'])
self.assertIsInstance(settings['search']['autocomplete'], (list, str))