1 """Entities queries for logbook."""
3 from __future__
import annotations
5 from collections.abc
import Collection, Iterable
8 from sqlalchemy
import lambda_stmt, select, union_all
9 from sqlalchemy.sql.elements
import ColumnElement
10 from sqlalchemy.sql.lambdas
import StatementLambdaElement
11 from sqlalchemy.sql.selectable
import CTE, CompoundSelect, Select
15 METADATA_ID_LAST_UPDATED_INDEX_TS,
16 OLD_ENTITY_ID_IN_EVENT,
25 apply_events_context_hints,
26 apply_states_context_hints,
28 select_events_context_id_subquery,
29 select_events_context_only,
30 select_events_without_states,
32 select_states_context_only,
39 event_type_ids: tuple[int, ...],
40 states_metadata_ids: Collection[int],
41 json_quoted_entity_ids: list[str],
43 """Generate a subquery to find context ids for multiple entities."""
50 (States.last_updated_ts > start_day) & (States.last_updated_ts < end_day)
52 .where(States.metadata_id.in_(states_metadata_ids)),
54 return select(union.c.context_id_bin).group_by(union.c.context_id_bin)
61 event_type_ids: tuple[int, ...],
62 states_metadata_ids: Collection[int],
63 json_quoted_entity_ids: list[str],
65 """Generate a CTE to find the entity and device context ids and a query to find linked row."""
71 json_quoted_entity_ids,
82 .select_from(entities_cte)
83 .outerjoin(Events, entities_cte.c.context_id_bin == Events.context_id_bin)
84 .outerjoin(EventTypes, (Events.event_type_id == EventTypes.event_type_id))
85 .outerjoin(EventData, (Events.data_id == EventData.data_id))
89 .select_from(entities_cte)
90 .outerjoin(States, entities_cte.c.context_id_bin == States.context_id_bin)
91 .outerjoin(StatesMeta, (States.metadata_id == StatesMeta.metadata_id))
99 event_type_ids: tuple[int, ...],
100 states_metadata_ids: Collection[int],
101 json_quoted_entity_ids: list[str],
102 ) -> StatementLambdaElement:
103 """Generate a logbook query for multiple entities."""
113 json_quoted_entity_ids,
114 ).order_by(Events.time_fired_ts)
119 start_day: float, end_day: float, states_metadata_ids: Collection[int]
121 """Generate a select for states from the States table for specific entities."""
124 ).where(States.metadata_id.in_(states_metadata_ids))
128 json_quoted_entity_ids: Iterable[str],
129 ) -> ColumnElement[bool]:
130 """Create matchers for the entity_id in the event_data."""
131 return sqlalchemy.or_(
132 ENTITY_ID_IN_EVENT.is_not(
None)
133 & sqlalchemy.cast(ENTITY_ID_IN_EVENT, sqlalchemy.Text()).in_(
134 json_quoted_entity_ids
136 OLD_ENTITY_ID_IN_EVENT.is_not(
None)
137 & sqlalchemy.cast(OLD_ENTITY_ID_IN_EVENT, sqlalchemy.Text()).in_(
138 json_quoted_entity_ids
144 """Force mysql to use the right index on large selects."""
145 return sel.with_hint(
147 f
"FORCE INDEX ({METADATA_ID_LAST_UPDATED_INDEX_TS})",
148 dialect_name=
"mysql",
151 f
"FORCE INDEX ({METADATA_ID_LAST_UPDATED_INDEX_TS})",
152 dialect_name=
"mariadb",
Select apply_states_filters(Select sel, float start_day, float end_day)
Select select_states_context_only()
Select select_events_without_states(float start_day, float end_day, tuple[int,...] event_type_ids)
Select select_events_context_id_subquery(float start_day, float end_day, tuple[int,...] event_type_ids)
Select apply_states_context_hints(Select sel)
Select apply_events_context_hints(Select sel)
Select select_events_context_only()
Select _select_entities_context_ids_sub_query(float start_day, float end_day, tuple[int,...] event_type_ids, Collection[int] states_metadata_ids, list[str] json_quoted_entity_ids)
ColumnElement[bool] apply_event_entity_id_matchers(Iterable[str] json_quoted_entity_ids)
CompoundSelect _apply_entities_context_union(Select sel, float start_day, float end_day, tuple[int,...] event_type_ids, Collection[int] states_metadata_ids, list[str] json_quoted_entity_ids)
Select apply_entities_hints(Select sel)
Select states_select_for_entity_ids(float start_day, float end_day, Collection[int] states_metadata_ids)
StatementLambdaElement entities_stmt(float start_day, float end_day, tuple[int,...] event_type_ids, Collection[int] states_metadata_ids, list[str] json_quoted_entity_ids)