[fix] JSON & CSV format return error 500

For CSV and JSON output, the LegacyResult and the Result objects needs to be
converted to a python dictionary.

Closes: https://github.com/searxng/searxng/issues/4244
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
This commit is contained in:
Markus Heiser 2025-01-28 16:51:07 +01:00 committed by Markus Heiser
parent 906b9e7d4c
commit df3344e5d5
2 changed files with 15 additions and 10 deletions

View File

@ -110,6 +110,9 @@ class Result(msgspec.Struct, kw_only=True):
return iter(self.__struct_fields__) return iter(self.__struct_fields__)
def as_dict(self):
return {f: getattr(self, f) for f in self.__struct_fields__}
class LegacyResult(dict): class LegacyResult(dict):
"""A wrapper around a legacy result item. The SearXNG core uses this class """A wrapper around a legacy result item. The SearXNG core uses this class
@ -130,10 +133,12 @@ class LegacyResult(dict):
UNSET = object() UNSET = object()
WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U) WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U)
def as_dict(self):
return self
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.__dict__ = self
# Init fields with defaults / compare with defaults of the fields in class Result # Init fields with defaults / compare with defaults of the fields in class Result
self.engine = self.get("engine", "") self.engine = self.get("engine", "")

View File

@ -123,17 +123,18 @@ def write_csv_response(csv: CSVWriter, rc: ResultContainer) -> None: # pylint:
""" """
results = rc.get_ordered_results()
keys = ('title', 'url', 'content', 'host', 'engine', 'score', 'type') keys = ('title', 'url', 'content', 'host', 'engine', 'score', 'type')
csv.writerow(keys) csv.writerow(keys)
for row in results: for res in rc.get_ordered_results():
row = res.as_dict()
row['host'] = row['parsed_url'].netloc row['host'] = row['parsed_url'].netloc
row['type'] = 'result' row['type'] = 'result'
csv.writerow([row.get(key, '') for key in keys]) csv.writerow([row.get(key, '') for key in keys])
for a in rc.answers: for a in rc.answers:
row = {'title': a, 'type': 'answer'} row = a.as_dict()
row['host'] = row['parsed_url'].netloc
csv.writerow([row.get(key, '') for key in keys]) csv.writerow([row.get(key, '') for key in keys])
for a in rc.suggestions: for a in rc.suggestions:
@ -158,18 +159,17 @@ class JSONEncoder(json.JSONEncoder): # pylint: disable=missing-class-docstring
def get_json_response(sq: SearchQuery, rc: ResultContainer) -> str: def get_json_response(sq: SearchQuery, rc: ResultContainer) -> str:
"""Returns the JSON string of the results to a query (``application/json``)""" """Returns the JSON string of the results to a query (``application/json``)"""
results = rc.number_of_results data = {
x = {
'query': sq.query, 'query': sq.query,
'number_of_results': results, 'number_of_results': rc.number_of_results,
'results': rc.get_ordered_results(), 'results': [_.as_dict() for _ in rc.get_ordered_results()],
'answers': list(rc.answers), 'answers': [_.as_dict() for _ in rc.answers],
'corrections': list(rc.corrections), 'corrections': list(rc.corrections),
'infoboxes': rc.infoboxes, 'infoboxes': rc.infoboxes,
'suggestions': list(rc.suggestions), 'suggestions': list(rc.suggestions),
'unresponsive_engines': get_translated_errors(rc.unresponsive_engines), 'unresponsive_engines': get_translated_errors(rc.unresponsive_engines),
} }
response = json.dumps(x, cls=JSONEncoder) response = json.dumps(data, cls=JSONEncoder)
return response return response