3from __future__
import annotations
6from collections
import defaultdict
7from threading
import RLock
8from typing
import List, NamedTuple, Set
10from searx
import logger
as log
20 for result_engine
in result[
'engines']:
21 if hasattr(searx.engines.engines.get(result_engine),
'weight'):
22 weight *= float(searx.engines.engines[result_engine].weight)
24 weight *= len(result[
'positions'])
27 for position
in result[
'positions']:
30 if priority ==
'high':
33 score += weight / position
51 """In the result container, the results are collected, sorted and duplicates
56 main_results_map: dict[int, MainResult | LegacyResult]
57 infoboxes: list[LegacyResult]
70 self.
engine_data: dict[str, dict[str, str]] = defaultdict(dict)
80 def extend(self, engine_name: str |
None, results):
82 log.debug(
"container is closed, ignoring results: %s", results)
86 for result
in list(results):
88 if isinstance(result, Result):
89 result.engine = result.engine
or engine_name
90 result.normalize_result_fields()
94 if isinstance(result, BaseAnswer):
96 elif isinstance(result, MainResult):
101 raise NotImplementedError(f
"no handler implemented to process the result of type {result}")
104 result[
"engine"] = result.get(
"engine")
or engine_name
or ""
106 result.normalize_result_fields()
108 if "suggestion" in result:
113 if "answer" in result:
116 f
"answer results from engine {result.engine}"
117 " are without typification / migrate to Answer class.",
123 if "correction" in result:
128 if "infobox" in result:
133 if "number_of_results" in result:
138 if "engine_data" in result:
141 self.
engine_data[result.engine][result[
"key"]] = result[
"engine_data"]
149 if engine_name
in searx.engines.engines:
150 eng = searx.engines.engines[engine_name]
151 histogram_observe(main_count,
"engine", eng.name,
"result",
"count")
152 if not self.
paging and eng.paging:
158 new_id = getattr(new_infobox,
"id",
None)
159 if new_id
is not None:
162 if new_id == getattr(existing_infobox,
"id",
None):
169 result_hash = hash(result)
176 result.positions = [position]
182 merged.positions.append(position)
189 for eng_name
in result.engines:
190 counter_add(result.score,
'engine', eng_name,
'score')
193 """Returns a sorted list of results to be displayed in the main result
194 area (:ref:`result types`)."""
203 results = sorted(self.
main_results_map.values(), key=
lambda x: x.score, reverse=
True)
207 categoryPositions = {}
213 engine = searx.engines.engines.get(res.engine
or "")
215 res.category = engine.categories[0]
if len(engine.categories) > 0
else ""
218 category = f
"{res.category}:{res.template}:{'img_src' if (res.thumbnail or res.img_src) else ''}"
219 grp = categoryPositions.get(category)
225 if (grp
is not None)
and (grp[
"count"] > 0)
and (len(gresults) - grp[
"index"] < max_distance):
229 gresults.insert(index, res)
233 for item
in categoryPositions.values():
236 item[
"index"] = v + 1
244 categoryPositions[category] = {
"index": len(gresults),
"count": max_count}
252 """Returns the average of results number, returns zero if the average
253 result number is smaller than the actual result count."""
256 log.error(
"call to ResultContainer.number_of_results before ResultContainer.close")
272 log.error(
"call to ResultContainer.add_unresponsive_engine after ResultContainer.close")
274 if searx.engines.engines[engine_name].display_error_messages:
277 def add_timing(self, engine_name: str, engine_time: float, page_load_time: float):
280 log.error(
"call to ResultContainer.add_timing after ResultContainer.close")
282 self.
timings.append(
Timing(engine_name, total=engine_time, load=page_load_time))
287 log.error(
"call to ResultContainer.get_timings before ResultContainer.close")
293 """Merges the values from ``other`` into ``origin``."""
295 weight1 = getattr(searx.engines.engines[origin.engine],
"weight", 1)
296 weight2 = getattr(searx.engines.engines[other.engine],
"weight", 1)
298 if weight2 > weight1:
299 origin.engine = other.engine
301 origin.engines |= other.engines
304 url_items = origin.get(
"urls", [])
306 for url2
in other.urls:
308 entity_url2 = url2.get(
"entity")
310 for url1
in origin.get(
"urls", []):
311 if (entity_url2
is not None and entity_url2 == url1.get(
"entity"))
or (
312 url1.get(
"url") == url2.get(
"url")
317 url_items.append(url2)
319 origin.urls = url_items
322 if not origin.img_src:
323 origin.img_src = other.img_src
324 elif weight2 > weight1:
325 origin.img_src = other.img_src
328 if not origin.attributes:
329 origin.attributes = other.attributes
332 for attr
in origin.attributes:
333 label = attr.get(
"label")
335 attr_names_1.add(label)
337 entity = attr.get(
"entity")
339 attr_names_1.add(entity)
341 for attr
in other.attributes:
342 if attr.get(
"label")
not in attr_names_1
and attr.get(
'entity')
not in attr_names_1:
343 origin.attributes.append(attr)
346 if not origin.content:
347 origin.content = other.content
348 elif len(other.content) > len(origin.content):
349 origin.content = other.content
353 """Merges the values from ``other`` into ``origin``."""
355 if len(other.content) > len(origin.content):
357 origin.content = other.content
360 if len(other.title) > len(origin.title):
361 origin.title = other.title
364 if isinstance(other, MainResult)
and isinstance(origin, MainResult):
365 origin.defaults_from(other)
366 elif isinstance(other, LegacyResult)
and isinstance(origin, LegacyResult):
367 origin.defaults_from(other)
370 origin.engines.add(other.engine
or "")
373 if origin.parsed_url
and not origin.parsed_url.scheme.endswith(
"s"):
374 if other.parsed_url
and other.parsed_url.scheme.endswith(
"s"):
375 origin.parsed_url = origin.parsed_url._replace(scheme=other.parsed_url.scheme)
376 origin.url = origin.parsed_url.geturl()
add_unresponsive_engine(self, str engine_name, str error_type, bool suspended=False)
dict[str, dict[str, str]] engine_data
extend(self, str|None engine_name, results)
list[MainResult|LegacyResult] _main_results_sorted
Set[UnresponsiveEngine] unresponsive_engines
_merge_main_result(self, MainResult|LegacyResult result, position)
_merge_infobox(self, LegacyResult new_infobox)
int number_of_results(self)
list[MainResult|LegacyResult] get_ordered_results(self)
add_timing(self, str engine_name, float engine_time, float page_load_time)
merge_two_infoboxes(LegacyResult origin, LegacyResult other)
merge_two_main_results(MainResult|LegacyResult origin, MainResult|LegacyResult other)
float calculate_score(result, priority)