diff --git a/synapse/config/experimental.py b/synapse/config/experimental.py
index e4719d19b8..f05a803a71 100644
--- a/synapse/config/experimental.py
+++ b/synapse/config/experimental.py
@@ -26,6 +26,8 @@ class ExperimentalConfig(Config):
# MSC3440 (thread relation)
self.msc3440_enabled: bool = experimental.get("msc3440_enabled", False)
+ # MSC3666: including bundled relations in /search.
+ self.msc3666_enabled: bool = experimental.get("msc3666_enabled", False)
# MSC3026 (busy presence state)
self.msc3026_enabled: bool = experimental.get("msc3026_enabled", False)
diff --git a/synapse/handlers/search.py b/synapse/handlers/search.py
index 02bb5ae72f..41cb809078 100644
--- a/synapse/handlers/search.py
+++ b/synapse/handlers/search.py
@@ -43,6 +43,8 @@ class SearchHandler:
self.state_store = self.storage.state
self.auth = hs.get_auth()
+ self._msc3666_enabled = hs.config.experimental.msc3666_enabled
+
async def get_old_rooms_from_upgraded_room(self, room_id: str) -> Iterable[str]:
"""Retrieves room IDs of old rooms in the history of an upgraded room.
@@ -238,8 +240,6 @@ class SearchHandler:
results = search_result["results"]
- results_map = {r["event"].event_id: r for r in results}
-
rank_map.update({r["event"].event_id: r["rank"] for r in results})
filtered_events = await search_filter.filter([r["event"] for r in results])
@@ -420,12 +420,29 @@ class SearchHandler:
time_now = self.clock.time_msec()
+ aggregations = None
+ if self._msc3666_enabled:
+ aggregations = await self.store.get_bundled_aggregations(
+ # Generate an iterable of EventBase for all the events that will be
+ # returned, including contextual events.
+ itertools.chain(
+ # The events_before and events_after for each context.
+ itertools.chain.from_iterable(
+ itertools.chain(context["events_before"], context["events_after"]) # type: ignore[arg-type]
+ for context in contexts.values()
+ ),
+ # The returned events.
+ allowed_events,
+ ),
+ user.to_string(),
+ )
+
for context in contexts.values():
context["events_before"] = self._event_serializer.serialize_events(
- context["events_before"], time_now # type: ignore[arg-type]
+ context["events_before"], time_now, bundle_aggregations=aggregations # type: ignore[arg-type]
)
context["events_after"] = self._event_serializer.serialize_events(
- context["events_after"], time_now # type: ignore[arg-type]
+ context["events_after"], time_now, bundle_aggregations=aggregations # type: ignore[arg-type]
)
state_results = {}
@@ -442,7 +459,9 @@ class SearchHandler:
results.append(
{
"rank": rank_map[e.event_id],
- "result": self._event_serializer.serialize_event(e, time_now),
+ "result": self._event_serializer.serialize_event(
+ e, time_now, bundle_aggregations=aggregations
+ ),
"context": contexts.get(e.event_id, {}),
}
)
diff --git a/synapse/storage/databases/main/relations.py b/synapse/storage/databases/main/relations.py
index 6180b17296..7718acbf1c 100644
--- a/synapse/storage/databases/main/relations.py
+++ b/synapse/storage/databases/main/relations.py
@@ -715,6 +715,9 @@ class RelationsWorkerStore(SQLBaseStore):
A map of event ID to the bundled aggregation for the event. Not all
events may have bundled aggregations in the results.
"""
+ # The already processed event IDs. Tracked separately from the result
+ # since the result omits events which do not have bundled aggregations.
+ seen_event_ids = set()
# State events and redacted events do not get bundled aggregations.
events = [
@@ -728,13 +731,19 @@ class RelationsWorkerStore(SQLBaseStore):
# Fetch other relations per event.
for event in events:
+ # De-duplicate events by ID to handle the same event requested multiple
+ # times. The caches that _get_bundled_aggregation_for_event use should
+ # capture this, but best to reduce work.
+ if event.event_id in seen_event_ids:
+ continue
+ seen_event_ids.add(event.event_id)
+
event_result = await self._get_bundled_aggregation_for_event(event, user_id)
if event_result:
results[event.event_id] = event_result
# Fetch any edits.
- event_ids = [event.event_id for event in events]
- edits = await self._get_applicable_edits(event_ids)
+ edits = await self._get_applicable_edits(seen_event_ids)
for event_id, edit in edits.items():
results.setdefault(event_id, BundledAggregations()).replace = edit
|