Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from stitch.api.observability.context import named_query
from stitch.api.entities import (
FILTER_OPTION_FIELDS,
OGFieldFilterParams,
OGFieldQueryParams,
)
from stitch.api.db.og_field_source_actions import (
Expand Down Expand Up @@ -93,16 +94,17 @@ async def query(

async def filter_options(
session: AsyncSession,
params: OGFieldFilterParams,
licensed_sources: Collection[OGSISrcKey] | None = None,
) -> dict[str, list[str]]:
"""Distinct coalesced values for every filterable field, in one query."""
"""Fetch availible values for each attribute after filtering the db results."""
options: dict[str, list[str]] = {field: [] for field in FILTER_OPTION_FIELDS}
with named_query("resources.filter_options"):
for colname, value in await session.execute(
filter_option_rows(licensed_sources)
filter_option_rows(params, licensed_sources)
):
options[colname].append(value)
return options
return {field: sorted(values) for field, values in options.items()}


async def get(
Expand Down
57 changes: 45 additions & 12 deletions deployments/api/src/stitch/api/db/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@
from sqlalchemy import (
CTE,
ColumnElement,
CompoundSelect,
Select,
and_,
asc,
case,
desc,
func,
literal,
or_,
select,
union_all,
)

from stitch.api.db.model import (
Expand All @@ -38,7 +41,11 @@
ResourceModel,
)
from stitch.api.db.model.oil_gas_field_source_value import value_attr_for
from stitch.api.entities import FILTER_OPTION_FIELDS, OGFieldQueryParams
from stitch.api.entities import (
FILTER_OPTION_FIELDS,
OGFieldFilterParams,
OGFieldQueryParams,
)
from stitch.ogsi.model.types import OGSISrcKey

# Single source of truth for the source-list field metadata. This is a shared
Expand Down Expand Up @@ -66,7 +73,7 @@
_HEADER_SORT_FIELDS: Final[frozenset[str]] = frozenset({"id", "source", "resource_id"})


def _filter_values(params: OGFieldQueryParams, field_name: str) -> list[Any]:
def _filter_values(params: OGFieldFilterParams, field_name: str) -> list[Any]:
"""The values an exact-match filter is set to, as a list.

Multi-select fields arrive as lists; the rest are scalars, returned as a
Expand Down Expand Up @@ -264,14 +271,24 @@ def coalesced_winner_rows(


def filter_option_rows(
params: OGFieldFilterParams,
licensed_sources: Collection[OGSISrcKey] | None = None,
) -> Select[tuple[str, str]]:
"""Distinct winning ``(colname, value)`` pairs for every filterable field."""
) -> CompoundSelect:
"""Fetch distinct ``(colname, value)`` pairs still present after filtering by ``params``."""
m = MembershipModel
v = OilGasFieldSourceValueModel
r = ResourceModel
p = OGFieldSourcePriority
o = OGFieldResourceSourcePriority

involved = list(FILTER_OPTION_FIELDS)
for field_name in EXACT_MATCH_FIELDS:
if _filter_values(params, field_name):
involved.append(field_name)
if params.q:
involved += Q_FIELDS
involved = list(dict.fromkeys(involved))

base = (
select(
m.resource_id.label("resource_id"),
Expand All @@ -283,25 +300,41 @@ def filter_option_rows(
v.value_text,
)
.select_from(m)
.join(r, r.id == m.resource_id)
.join(v, v.source_pk == m.source_pk)
.join(p, p.source == m.source)
.outerjoin(o, _override_join(m.resource_id))
.where(
r.repointed_id.is_(None),
m.status == MembershipStatus.ACTIVE,
v.colname.in_(FILTER_OPTION_FIELDS),
v.colname.in_(involved),
)
)
if licensed_sources is not None:
base = base.where(m.source.in_(list(dict.fromkeys(licensed_sources))))

ranked = add_ranking(base.cte("filter_option_base")).cte("filter_option_ranked")
c = ranked.c
return (
select(c.colname, c.value_text)
.where(c.value_text.is_not(None)) # rn == 1 already applied by add_ranking
.distinct()
.order_by(c.colname, c.value_text)

pivot = _add_pivot_columns(
select(ranked.c.resource_id.label("resource_id")),
involved,
ranked.c.colname,
lambda field_name: getattr(ranked.c, value_attr_for(field_name)),
)
pivot_cte = pivot.group_by(ranked.c.resource_id).cte("filter_option_pivot")

branches: list[Select[tuple[str, str]]] = []
for field_name in FILTER_OPTION_FIELDS:
column = _require_column(pivot_cte, field_name)
branch = select(
literal(field_name).label("colname"), column.label("value")
).where(column.is_not(None))
for cond in _build_field_conditions(
pivot_cte, params.model_copy(update={field_name: None})
):
branch = branch.where(cond)
branches.append(branch.distinct())
return union_all(*branches)


def field_source_candidates(
Expand Down Expand Up @@ -489,7 +522,7 @@ def _require_column(cte: CTE | Select, field_name: str) -> ColumnElement[Any]:

def _build_field_conditions(
cte: CTE | Select,
params: OGFieldQueryParams,
params: OGFieldFilterParams,
) -> list[ColumnElement[bool]]:
"""Path-agnostic q-ILIKE + exact-match filters over the pivoted columns.

Expand Down
3 changes: 3 additions & 0 deletions deployments/api/src/stitch/api/routers/oil_gas_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
MergeCandidateDetailView,
MergeCandidateReviewRequest,
MergeCandidateView,
OGFieldFilterParams,
OGFieldQueryParams,
PaginatedResponse,
SetFieldPriorityRequest,
Expand Down Expand Up @@ -93,9 +94,11 @@ async def get_resource_filter_options(
uow: UnitOfWorkDep,
_user: CurrentUser,
claims: Claims,
params: Annotated[OGFieldFilterParams, Query()],
) -> OGFieldFilterOptionsResponse:
opts = await resource_actions.filter_options(
session=uow.session,
params=params,
licensed_sources=licensed_sources(claims),
)
return OGFieldFilterOptionsResponse(**opts)
Expand Down
Loading
Loading