+ sql = sql.where(t.c.centroid
+ .ST_Distance(NEAR_PARAM) < NEAR_RADIUS_PARAM)
+
+ if self.housenumbers:
+ sql = sql.where(t.c.address_rank.between(16, 30))
+ else:
+ if details.excluded:
+ sql = sql.where(_exclude_places(t))
+ if details.min_rank > 0:
+ sql = sql.where(sa.or_(t.c.address_rank >= MIN_RANK_PARAM,
+ t.c.search_rank >= MIN_RANK_PARAM))
+ if details.max_rank < 30:
+ sql = sql.where(sa.or_(t.c.address_rank <= MAX_RANK_PARAM,
+ t.c.search_rank <= MAX_RANK_PARAM))
+
+ inner = sql.limit(10000).order_by(sa.desc(sa.text('importance'))).subquery()
+
+ sql = sa.select(inner.c.place_id, inner.c.search_rank, inner.c.address_rank,
+ inner.c.country_code, inner.c.centroid, inner.c.importance,
+ inner.c.penalty)
+
+ # If the query is not an address search or has a geographic preference,
+ # preselect most important items to restrict the number of places
+ # that need to be looked up in placex.
+ if not self.housenumbers\
+ and (details.viewbox is None or details.bounded_viewbox)\
+ and (details.near is None or details.near_radius is not None)\
+ and not self.qualifiers:
+ sql = sql.add_columns(sa.func.first_value(inner.c.penalty - inner.c.importance)
+ .over(order_by=inner.c.penalty - inner.c.importance)
+ .label('min_penalty'))
+
+ inner = sql.subquery()
+
+ sql = sa.select(inner.c.place_id, inner.c.search_rank, inner.c.address_rank,
+ inner.c.country_code, inner.c.centroid, inner.c.importance,
+ inner.c.penalty)\
+ .where(inner.c.penalty - inner.c.importance < inner.c.min_penalty + 0.5)
+
+ return sql.cte('searches')
+
+
+ async def lookup(self, conn: SearchConnection,
+ details: SearchDetails) -> nres.SearchResults:
+ """ Find results for the search in the database.
+ """
+ t = conn.t.placex
+ tsearch = self._inner_search_name_cte(conn, details)
+
+ sql = _select_placex(t).join(tsearch, t.c.place_id == tsearch.c.place_id)
+
+ if details.geometry_output:
+ sql = _add_geometry_columns(sql, t.c.geometry, details)
+
+ penalty: SaExpression = tsearch.c.penalty
+
+ if self.postcodes:
+ tpc = conn.t.postcode
+ pcs = self.postcodes.values
+
+ pc_near = sa.select(sa.func.min(tpc.c.geometry.ST_Distance(t.c.centroid)))\
+ .where(tpc.c.postcode.in_(pcs))\
+ .scalar_subquery()
+ penalty += sa.case((t.c.postcode.in_(pcs), 0.0),
+ else_=sa.func.coalesce(pc_near, cast(SaColumn, 2.0)))
+
+ if details.viewbox is not None and not details.bounded_viewbox:
+ penalty += sa.case((t.c.geometry.intersects(VIEWBOX_PARAM, use_index=False), 0.0),
+ (t.c.geometry.intersects(VIEWBOX2_PARAM, use_index=False), 0.5),
+ else_=1.0)