summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBnyro <bnyro@tutanota.com>2023-09-10 18:44:16 +0200
committerMarkus Heiser <markus.heiser@darmarIT.de>2023-09-19 09:40:57 +0200
commitdcee82334548ad8849391b5c29cdcd868b65daad (patch)
tree5313b41db400f0215c4b46a9af8bb51f1c11a8b2
parent71508abcbf77df7e996114e4f142a5f15eced6e4 (diff)
downloadsearxng-dcee82334548ad8849391b5c29cdcd868b65daad.tar.gz
searxng-dcee82334548ad8849391b5c29cdcd868b65daad.zip
[feat] implement feeling lucky feature
-rw-r--r--searx/query.py26
-rw-r--r--searx/search/models.py9
-rw-r--r--searx/webadapter.py2
-rwxr-xr-xsearx/webapp.py4
-rw-r--r--tests/unit/test_query.py12
-rw-r--r--tests/unit/test_search.py2
6 files changed, 35 insertions, 20 deletions
diff --git a/searx/query.py b/searx/query.py
index 751308baa..49fa89a9c 100644
--- a/searx/query.py
+++ b/searx/query.py
@@ -150,7 +150,7 @@ class LanguageParser(QueryPartParser):
class ExternalBangParser(QueryPartParser):
@staticmethod
def check(raw_value):
- return raw_value.startswith('!!')
+ return raw_value.startswith('!!') and len(raw_value) > 2
def __call__(self, raw_value):
value = raw_value[2:]
@@ -177,7 +177,8 @@ class ExternalBangParser(QueryPartParser):
class BangParser(QueryPartParser):
@staticmethod
def check(raw_value):
- return raw_value[0] == '!'
+ # make sure it's not any bang with double '!!'
+ return raw_value[0] == '!' and (len(raw_value) < 2 or raw_value[1] != '!')
def __call__(self, raw_value):
value = raw_value[1:].replace('-', ' ').replace('_', ' ')
@@ -235,14 +236,25 @@ class BangParser(QueryPartParser):
self._add_autocomplete(first_char + engine_shortcut)
+class FeelingLuckyParser(QueryPartParser):
+ @staticmethod
+ def check(raw_value):
+ return raw_value == '!!'
+
+ def __call__(self, raw_value):
+ self.raw_text_query.redirect_to_first_result = True
+ return True
+
+
class RawTextQuery:
"""parse raw text query (the value from the html input)"""
PARSER_CLASSES = [
- TimeoutParser, # this force the timeout
- LanguageParser, # this force a language
+ TimeoutParser, # force the timeout
+ LanguageParser, # force a language
ExternalBangParser, # external bang (must be before BangParser)
- BangParser, # this force a engine or category
+ BangParser, # force an engine or category
+ FeelingLuckyParser, # redirect to the first link in the results list
]
def __init__(self, query, disabled_engines):
@@ -261,6 +273,7 @@ class RawTextQuery:
self.query_parts = [] # use self.getFullQuery()
self.user_query_parts = [] # use self.getQuery()
self.autocomplete_location = None
+ self.redirect_to_first_result = False
self._parse_query()
def _parse_query(self):
@@ -330,5 +343,6 @@ class RawTextQuery:
+ f"enginerefs={self.enginerefs!r}\n "
+ f"autocomplete_list={self.autocomplete_list!r}\n "
+ f"query_parts={self.query_parts!r}\n "
- + f"user_query_parts={self.user_query_parts!r} >"
+ + f"user_query_parts={self.user_query_parts!r} >\n"
+ + f"redirect_to_first_result={self.redirect_to_first_result!r}"
)
diff --git a/searx/search/models.py b/searx/search/models.py
index 91e5d5982..ec1188fbb 100644
--- a/searx/search/models.py
+++ b/searx/search/models.py
@@ -37,6 +37,7 @@ class SearchQuery:
'timeout_limit',
'external_bang',
'engine_data',
+ 'redirect_to_first_result',
)
def __init__(
@@ -50,6 +51,7 @@ class SearchQuery:
timeout_limit: typing.Optional[float] = None,
external_bang: typing.Optional[str] = None,
engine_data: typing.Optional[typing.Dict[str, str]] = None,
+ redirect_to_first_result: typing.Optional[bool] = None,
):
self.query = query
self.engineref_list = engineref_list
@@ -60,6 +62,7 @@ class SearchQuery:
self.timeout_limit = timeout_limit
self.external_bang = external_bang
self.engine_data = engine_data or {}
+ self.redirect_to_first_result = redirect_to_first_result
self.locale = None
if self.lang:
@@ -73,7 +76,7 @@ class SearchQuery:
return list(set(map(lambda engineref: engineref.category, self.engineref_list)))
def __repr__(self):
- return "SearchQuery({!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r})".format(
+ return "SearchQuery({!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r})".format(
self.query,
self.engineref_list,
self.lang,
@@ -82,6 +85,7 @@ class SearchQuery:
self.time_range,
self.timeout_limit,
self.external_bang,
+ self.redirect_to_first_result,
)
def __eq__(self, other):
@@ -94,6 +98,7 @@ class SearchQuery:
and self.time_range == other.time_range
and self.timeout_limit == other.timeout_limit
and self.external_bang == other.external_bang
+ and self.redirect_to_first_result == other.redirect_to_first_result
)
def __hash__(self):
@@ -107,6 +112,7 @@ class SearchQuery:
self.time_range,
self.timeout_limit,
self.external_bang,
+ self.redirect_to_first_result,
)
)
@@ -121,4 +127,5 @@ class SearchQuery:
self.timeout_limit,
self.external_bang,
self.engine_data,
+ self.redirect_to_first_result,
)
diff --git a/searx/webadapter.py b/searx/webadapter.py
index 121319eeb..9fbb8ea3e 100644
--- a/searx/webadapter.py
+++ b/searx/webadapter.py
@@ -254,6 +254,7 @@ def get_search_query_from_webapp(
query_time_range = parse_time_range(form)
query_timeout = parse_timeout(form, raw_text_query)
external_bang = raw_text_query.external_bang
+ redirect_to_first_result = raw_text_query.redirect_to_first_result
engine_data = parse_engine_data(form)
query_lang = parse_lang(preferences, form, raw_text_query)
@@ -288,6 +289,7 @@ def get_search_query_from_webapp(
query_timeout,
external_bang=external_bang,
engine_data=engine_data,
+ redirect_to_first_result=redirect_to_first_result,
),
raw_text_query,
query_engineref_list_unknown,
diff --git a/searx/webapp.py b/searx/webapp.py
index fe6dd6b74..b2a76ff92 100755
--- a/searx/webapp.py
+++ b/searx/webapp.py
@@ -697,6 +697,10 @@ def search():
previous_result = None
results = result_container.get_ordered_results()
+
+ if search_query.redirect_to_first_result and results:
+ return redirect(results[0]['url'], 302)
+
for result in results:
if output_format == 'html':
if 'content' in result and result['content']:
diff --git a/tests/unit/test_query.py b/tests/unit/test_query.py
index 7274a8da5..59bbd6574 100644
--- a/tests/unit/test_query.py
+++ b/tests/unit/test_query.py
@@ -225,18 +225,6 @@ class TestExternalBangParser(SearxTestCase):
a = query.autocomplete_list[0]
self.assertEqual(query.get_autocomplete_full_query(a), a + ' the query')
- def test_external_bang_autocomplete_empty(self):
- query_text = 'the query !!'
- query = RawTextQuery(query_text, [])
-
- self.assertEqual(query.getFullQuery(), 'the query !!')
- self.assertEqual(len(query.query_parts), 0)
- self.assertFalse(query.specific)
- self.assertGreater(len(query.autocomplete_list), 2)
-
- a = query.autocomplete_list[0]
- self.assertEqual(query.get_autocomplete_full_query(a), 'the query ' + a)
-
class TestBang(SearxTestCase):
diff --git a/tests/unit/test_search.py b/tests/unit/test_search.py
index 11cb8d4e4..87f5f280b 100644
--- a/tests/unit/test_search.py
+++ b/tests/unit/test_search.py
@@ -27,7 +27,7 @@ class SearchQueryTestCase(SearxTestCase):
def test_repr(self):
s = SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g')
self.assertEqual(
- repr(s), "SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g')"
+ repr(s), "SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g', None)"
) # noqa
def test_eq(self):