mirror of https://github.com/searxng/searxng.git
[feat] implement feeling lucky feature
This commit is contained in:
parent
71508abcbf
commit
dcee823345
|
@ -150,7 +150,7 @@ class LanguageParser(QueryPartParser):
|
||||||
class ExternalBangParser(QueryPartParser):
|
class ExternalBangParser(QueryPartParser):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check(raw_value):
|
def check(raw_value):
|
||||||
return raw_value.startswith('!!')
|
return raw_value.startswith('!!') and len(raw_value) > 2
|
||||||
|
|
||||||
def __call__(self, raw_value):
|
def __call__(self, raw_value):
|
||||||
value = raw_value[2:]
|
value = raw_value[2:]
|
||||||
|
@ -177,7 +177,8 @@ class ExternalBangParser(QueryPartParser):
|
||||||
class BangParser(QueryPartParser):
|
class BangParser(QueryPartParser):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check(raw_value):
|
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):
|
def __call__(self, raw_value):
|
||||||
value = raw_value[1:].replace('-', ' ').replace('_', ' ')
|
value = raw_value[1:].replace('-', ' ').replace('_', ' ')
|
||||||
|
@ -235,14 +236,25 @@ class BangParser(QueryPartParser):
|
||||||
self._add_autocomplete(first_char + engine_shortcut)
|
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:
|
class RawTextQuery:
|
||||||
"""parse raw text query (the value from the html input)"""
|
"""parse raw text query (the value from the html input)"""
|
||||||
|
|
||||||
PARSER_CLASSES = [
|
PARSER_CLASSES = [
|
||||||
TimeoutParser, # this force the timeout
|
TimeoutParser, # force the timeout
|
||||||
LanguageParser, # this force a language
|
LanguageParser, # force a language
|
||||||
ExternalBangParser, # external bang (must be before BangParser)
|
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):
|
def __init__(self, query, disabled_engines):
|
||||||
|
@ -261,6 +273,7 @@ class RawTextQuery:
|
||||||
self.query_parts = [] # use self.getFullQuery()
|
self.query_parts = [] # use self.getFullQuery()
|
||||||
self.user_query_parts = [] # use self.getQuery()
|
self.user_query_parts = [] # use self.getQuery()
|
||||||
self.autocomplete_location = None
|
self.autocomplete_location = None
|
||||||
|
self.redirect_to_first_result = False
|
||||||
self._parse_query()
|
self._parse_query()
|
||||||
|
|
||||||
def _parse_query(self):
|
def _parse_query(self):
|
||||||
|
@ -330,5 +343,6 @@ class RawTextQuery:
|
||||||
+ f"enginerefs={self.enginerefs!r}\n "
|
+ f"enginerefs={self.enginerefs!r}\n "
|
||||||
+ f"autocomplete_list={self.autocomplete_list!r}\n "
|
+ f"autocomplete_list={self.autocomplete_list!r}\n "
|
||||||
+ f"query_parts={self.query_parts!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}"
|
||||||
)
|
)
|
||||||
|
|
|
@ -37,6 +37,7 @@ class SearchQuery:
|
||||||
'timeout_limit',
|
'timeout_limit',
|
||||||
'external_bang',
|
'external_bang',
|
||||||
'engine_data',
|
'engine_data',
|
||||||
|
'redirect_to_first_result',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -50,6 +51,7 @@ class SearchQuery:
|
||||||
timeout_limit: typing.Optional[float] = None,
|
timeout_limit: typing.Optional[float] = None,
|
||||||
external_bang: typing.Optional[str] = None,
|
external_bang: typing.Optional[str] = None,
|
||||||
engine_data: typing.Optional[typing.Dict[str, str]] = None,
|
engine_data: typing.Optional[typing.Dict[str, str]] = None,
|
||||||
|
redirect_to_first_result: typing.Optional[bool] = None,
|
||||||
):
|
):
|
||||||
self.query = query
|
self.query = query
|
||||||
self.engineref_list = engineref_list
|
self.engineref_list = engineref_list
|
||||||
|
@ -60,6 +62,7 @@ class SearchQuery:
|
||||||
self.timeout_limit = timeout_limit
|
self.timeout_limit = timeout_limit
|
||||||
self.external_bang = external_bang
|
self.external_bang = external_bang
|
||||||
self.engine_data = engine_data or {}
|
self.engine_data = engine_data or {}
|
||||||
|
self.redirect_to_first_result = redirect_to_first_result
|
||||||
|
|
||||||
self.locale = None
|
self.locale = None
|
||||||
if self.lang:
|
if self.lang:
|
||||||
|
@ -73,7 +76,7 @@ class SearchQuery:
|
||||||
return list(set(map(lambda engineref: engineref.category, self.engineref_list)))
|
return list(set(map(lambda engineref: engineref.category, self.engineref_list)))
|
||||||
|
|
||||||
def __repr__(self):
|
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.query,
|
||||||
self.engineref_list,
|
self.engineref_list,
|
||||||
self.lang,
|
self.lang,
|
||||||
|
@ -82,6 +85,7 @@ class SearchQuery:
|
||||||
self.time_range,
|
self.time_range,
|
||||||
self.timeout_limit,
|
self.timeout_limit,
|
||||||
self.external_bang,
|
self.external_bang,
|
||||||
|
self.redirect_to_first_result,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
@ -94,6 +98,7 @@ class SearchQuery:
|
||||||
and self.time_range == other.time_range
|
and self.time_range == other.time_range
|
||||||
and self.timeout_limit == other.timeout_limit
|
and self.timeout_limit == other.timeout_limit
|
||||||
and self.external_bang == other.external_bang
|
and self.external_bang == other.external_bang
|
||||||
|
and self.redirect_to_first_result == other.redirect_to_first_result
|
||||||
)
|
)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
|
@ -107,6 +112,7 @@ class SearchQuery:
|
||||||
self.time_range,
|
self.time_range,
|
||||||
self.timeout_limit,
|
self.timeout_limit,
|
||||||
self.external_bang,
|
self.external_bang,
|
||||||
|
self.redirect_to_first_result,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -121,4 +127,5 @@ class SearchQuery:
|
||||||
self.timeout_limit,
|
self.timeout_limit,
|
||||||
self.external_bang,
|
self.external_bang,
|
||||||
self.engine_data,
|
self.engine_data,
|
||||||
|
self.redirect_to_first_result,
|
||||||
)
|
)
|
||||||
|
|
|
@ -254,6 +254,7 @@ def get_search_query_from_webapp(
|
||||||
query_time_range = parse_time_range(form)
|
query_time_range = parse_time_range(form)
|
||||||
query_timeout = parse_timeout(form, raw_text_query)
|
query_timeout = parse_timeout(form, raw_text_query)
|
||||||
external_bang = raw_text_query.external_bang
|
external_bang = raw_text_query.external_bang
|
||||||
|
redirect_to_first_result = raw_text_query.redirect_to_first_result
|
||||||
engine_data = parse_engine_data(form)
|
engine_data = parse_engine_data(form)
|
||||||
|
|
||||||
query_lang = parse_lang(preferences, form, raw_text_query)
|
query_lang = parse_lang(preferences, form, raw_text_query)
|
||||||
|
@ -288,6 +289,7 @@ def get_search_query_from_webapp(
|
||||||
query_timeout,
|
query_timeout,
|
||||||
external_bang=external_bang,
|
external_bang=external_bang,
|
||||||
engine_data=engine_data,
|
engine_data=engine_data,
|
||||||
|
redirect_to_first_result=redirect_to_first_result,
|
||||||
),
|
),
|
||||||
raw_text_query,
|
raw_text_query,
|
||||||
query_engineref_list_unknown,
|
query_engineref_list_unknown,
|
||||||
|
|
|
@ -697,6 +697,10 @@ def search():
|
||||||
previous_result = None
|
previous_result = None
|
||||||
|
|
||||||
results = result_container.get_ordered_results()
|
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:
|
for result in results:
|
||||||
if output_format == 'html':
|
if output_format == 'html':
|
||||||
if 'content' in result and result['content']:
|
if 'content' in result and result['content']:
|
||||||
|
|
|
@ -225,18 +225,6 @@ class TestExternalBangParser(SearxTestCase):
|
||||||
a = query.autocomplete_list[0]
|
a = query.autocomplete_list[0]
|
||||||
self.assertEqual(query.get_autocomplete_full_query(a), a + ' the query')
|
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):
|
class TestBang(SearxTestCase):
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ class SearchQueryTestCase(SearxTestCase):
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
s = SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g')
|
s = SearchQuery('test', [EngineRef('bing', 'general')], 'all', 0, 1, '1', 5.0, 'g')
|
||||||
self.assertEqual(
|
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
|
) # noqa
|
||||||
|
|
||||||
def test_eq(self):
|
def test_eq(self):
|
||||||
|
|
Loading…
Reference in New Issue