mirror of https://github.com/searxng/searxng.git
[enh] Allowing using multiple autocomplete engines
This commit is contained in:
parent
0fb3f0e4ae
commit
e1d3389047
|
@ -5,12 +5,13 @@
|
||||||
# pylint: disable=use-dict-literal
|
# pylint: disable=use-dict-literal
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import random
|
||||||
from urllib.parse import urlencode, quote_plus
|
from urllib.parse import urlencode, quote_plus
|
||||||
|
|
||||||
import lxml
|
import lxml
|
||||||
from httpx import HTTPError
|
from httpx import HTTPError
|
||||||
|
|
||||||
from searx import settings
|
from searx import settings, logger
|
||||||
from searx.engines import (
|
from searx.engines import (
|
||||||
engines,
|
engines,
|
||||||
google,
|
google,
|
||||||
|
@ -252,11 +253,36 @@ backends = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def search_autocomplete(backend_name, query, sxng_locale):
|
def search_autocomplete(backend_names, query, sxng_locale):
|
||||||
backend = backends.get(backend_name)
|
|
||||||
if backend is None:
|
enabled_backends = set()
|
||||||
return []
|
for name in backend_names:
|
||||||
|
backend = backends.get(name)
|
||||||
|
if backend is None:
|
||||||
|
logger.error("%s is not valid! Must be one of %s", repr(backend), backend.keys())
|
||||||
|
continue
|
||||||
|
if backend not in enabled_backends:
|
||||||
|
enabled_backends.add(backend)
|
||||||
|
|
||||||
|
len_enabled_backends = len(enabled_backends)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return backend(query, sxng_locale)
|
results = []
|
||||||
|
|
||||||
|
for backend in enabled_backends:
|
||||||
|
backend_results = backend(query, sxng_locale)
|
||||||
|
if (len_enabled_backends > 1) and (len(backend_results) > 3):
|
||||||
|
# if more than 3 autocompleters: only get the first 3 results from each
|
||||||
|
|
||||||
|
results.extend(backend_results[:3:])
|
||||||
|
|
||||||
|
else:
|
||||||
|
results.extend(backend_results)
|
||||||
|
|
||||||
|
random.seed(1)
|
||||||
|
random.shuffle(results)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
except (HTTPError, SearxEngineResponseException):
|
except (HTTPError, SearxEngineResponseException):
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -96,6 +96,10 @@ class MultipleChoiceSetting(Setting):
|
||||||
"""Setting of values which can only come from the given choices"""
|
"""Setting of values which can only come from the given choices"""
|
||||||
|
|
||||||
def __init__(self, default_value: List[str], choices: Iterable[str], locked=False):
|
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)
|
super().__init__(default_value, locked)
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
self._validate_selections(self.value)
|
self._validate_selections(self.value)
|
||||||
|
@ -401,7 +405,7 @@ class Preferences:
|
||||||
locked=is_locked('locale'),
|
locked=is_locked('locale'),
|
||||||
choices=list(LOCALE_NAMES.keys()) + ['']
|
choices=list(LOCALE_NAMES.keys()) + ['']
|
||||||
),
|
),
|
||||||
'autocomplete': EnumStringSetting(
|
'autocomplete': MultipleChoiceSetting(
|
||||||
settings['search']['autocomplete'],
|
settings['search']['autocomplete'],
|
||||||
locked=is_locked('autocomplete'),
|
locked=is_locked('autocomplete'),
|
||||||
choices=list(autocomplete.backends.keys()) + ['']
|
choices=list(autocomplete.backends.keys()) + ['']
|
||||||
|
|
|
@ -29,10 +29,12 @@ brand:
|
||||||
search:
|
search:
|
||||||
# Filter results. 0: None, 1: Moderate, 2: Strict
|
# Filter results. 0: None, 1: Moderate, 2: Strict
|
||||||
safe_search: 0
|
safe_search: 0
|
||||||
# Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "yandex", "mwmbl",
|
# Existing autocomplete backends: "dbpedia", "duckduckgo", "google",
|
||||||
# "seznam", "startpage", "stract", "swisscows", "qwant", "wikipedia" - leave blank to turn it off
|
# "yandex", "mwmbl", "seznam", "startpage", "stract", "swisscows", "qwant", "wikipedia"
|
||||||
# by default.
|
# Uncomment the section below and edit/add/remove each engine to turn them on by default.
|
||||||
autocomplete: ""
|
# autocomplete:
|
||||||
|
# - duckduckgo
|
||||||
|
# - stract
|
||||||
# minimun characters to type before autocompleter starts
|
# minimun characters to type before autocompleter starts
|
||||||
autocomplete_min: 4
|
autocomplete_min: 4
|
||||||
# Default search language - leave blank to detect from browser information or
|
# Default search language - leave blank to detect from browser information or
|
||||||
|
|
|
@ -154,7 +154,7 @@ SCHEMA = {
|
||||||
},
|
},
|
||||||
'search': {
|
'search': {
|
||||||
'safe_search': SettingsValue((0, 1, 2), 0),
|
'safe_search': SettingsValue((0, 1, 2), 0),
|
||||||
'autocomplete': SettingsValue(str, ''),
|
'autocomplete': SettingsValue((list, str, False), ['']),
|
||||||
'autocomplete_min': SettingsValue(int, 4),
|
'autocomplete_min': SettingsValue(int, 4),
|
||||||
'default_lang': SettingsValue(tuple(SXNG_LOCALE_TAGS + ['']), ''),
|
'default_lang': SettingsValue(tuple(SXNG_LOCALE_TAGS + ['']), ''),
|
||||||
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS),
|
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS),
|
||||||
|
|
|
@ -66,6 +66,14 @@ class TestSettings(SearxTestCase): # pylint: disable=missing-class-docstring
|
||||||
|
|
||||||
# multiple choice settings
|
# 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):
|
def test_multiple_setting_invalid_default_value(self):
|
||||||
with self.assertRaises(ValidationException):
|
with self.assertRaises(ValidationException):
|
||||||
MultipleChoiceSetting(['3', '4'], choices=['0', '1', '2'])
|
MultipleChoiceSetting(['3', '4'], choices=['0', '1', '2'])
|
||||||
|
|
|
@ -121,3 +121,4 @@ class TestUserSettings(SearxTestCase): # pylint: disable=missing-class-docstrin
|
||||||
self.assertEqual(settings['server']['secret_key'], "user_settings_secret")
|
self.assertEqual(settings['server']['secret_key'], "user_settings_secret")
|
||||||
engine_names = [engine['name'] for engine in settings['engines']]
|
engine_names = [engine['name'] for engine in settings['engines']]
|
||||||
self.assertEqual(engine_names, ['wikidata', 'wikibooks', 'wikinews', 'wikiquote'])
|
self.assertEqual(engine_names, ['wikidata', 'wikibooks', 'wikinews', 'wikiquote'])
|
||||||
|
self.assertIsInstance(settings['search']['autocomplete'], (list, str))
|
||||||
|
|
Loading…
Reference in New Issue