Skip to content
Navigation

Structured parcel search with a fast LIMIT fetch and a capped count

Address lookup: send ONE address per call as {field: 'address', operator: 'like', value: '<street> <number>'} with maxResults 10 - values are trimmed, and street + house number matches the stored 'Bahnhofstrasse 1' form. Walk address lists in small sequential batches, not one wide parallel burst. Backs the choose app's parcel search (and the navbar EGRID search). Replaces the GeoServer WFS path, whose exact total count over broad filters took 7–30 s and blew the Vercel Edge 25 s wall. This endpoint splits the work: a LIMIT maxResults row fetch with NO ORDER BY (the client sorts; a stable order would force a full sort and defeat the early exit) and a separate count that stops at 10,001 rows, reported as numberMatched: 10000, capped: true (the count is skipped when the fetch returns fewer rows than maxResults - the total is then exact). like on address/streetname is a trigram-indexed contains match (fast); like on any other text column still scans.

Reads the configured parcel snapshot table (config.parcelTableName). Both statements run in one transaction under SET LOCAL statement_timeout = 20000; a filter that cannot use an index (notably like on fso_name_2021) trips it and answers 500 {"error":"parcel search failed"}.

Filtering rules live in ParcelSearchCondition: unknown fields and empty values are silently dropped rather than rejected, so a malformed condition list yields a broader search, never a 400. There is no request validation error path at all - the only non-200 answer is the 500.

No authentication (mirrors the WFS it replaced; the whole surface is public parcel data). Not cached.

POST
https://res.aireon.ch/res_api/parcel_search

Request body JSON required

  • conditions
    optional
    array of
    object

    Filter clauses, ANDed together. Omit, or send an empty array, to match every parcel (the count is then simply capped).

    • field
      parcel_id
      or
      cz_canton_name
      or
      cityname
      or
      districtname
      or
      zip
      or
      streetname
      +34 more
      required
    • operator
      eq
      or
      neq
      or
      gt
      or
      gte
      or
      lt
      or
      lte
      +3 more
      required
    • value
      optional

      Comparison value. Numbers may be sent as numbers or numeric strings; in takes ONE comma-separated string ("8000,8001"), not an array.

      One of the following:

      • string
      • number
      • boolean
    • valueTo
      optional

      Upper bound for between on numeric fields. Ignored for every other operator.

      One of the following:

      • string
      • number
  • maxResults
    optional
    integer

    Row ceiling. A missing or non-numeric value, or anything below 1, becomes 50; anything above 1000 is clamped to 1000.

    maximum: 1000 · minimum: 1 · default: 50

Example requests

{
  "conditions": [
    {
      "field": "fso_name_2021",
      "operator": "eq",
      "value": "Zürich"
    },
    {
      "field": "cz_harmonized",
      "operator": "like",
      "value": "Wohn"
    },
    {
      "field": "parcel_area",
      "operator": "between",
      "value": 1000,
      "valueTo": 5000
    }
  ],
  "maxResults": 100
}

Returns

200

GeoJSON-shaped FeatureCollection with geometry: null on every feature, plus the capped count.

JSON
  • type
    string
    required
  • features
    array of
    object
    required
    • type
      optional
      string
    • id
      optional
      string

      <parcel table>.<parcel_id>, mirroring the WFS feature id this endpoint replaced.

    • geometry
      optional
      null

      Always null - geometry is intentionally not selected.

    • properties
      optional
      object

      The whitelisted result columns (RESULT_COLS), in this exact key order - the choose app builds its CSV header from Object.keys(features[0].properties), so the list is append-only. Values come straight from the parcel snapshot table: any column can be null, and int8/numeric columns arrive as strings.

      • parcel_id
        optional
        string | null

        EGRID (CH + 12 digits).

      • parcel_local_id
        optional
        string | null
      • address
        optional
        string | null
      • streetname
        optional
        string | null
      • cityname
        optional
        string | null
      • districtname
        optional
        string | null
      • cz_canton
        optional
        string | null
      • cz_canton_name
        optional
        string | null
      • zip
        optional
        string | integer | null

        Postal code. The column is int8, which pg-promise serialises as a string.

      • parcel_area
        optional
        number | null
      • plot_type
        optional
        string | null
      • owner
        optional
        string | null
      • is_sell
        optional
        boolean | null
      • bldg_constr_year
        optional
        number | null
      • bldg_size
        optional
        number | null
      • bldg_vol_gwr
        optional
        number | null
      • bldg_height_mean
        optional
        number | null
      • bldg_floors
        optional
        number | null
      • bldg_flats
        optional
        number | null
      • bldg_count
        optional
        number | null
      • gfa_now
        optional
        number | null
      • gfa_max
        optional
        number | null
      • vol_max
        optional
        number | null
      • estimated_price_m2
        optional
        number | null
      • ratio_v_free
        optional
        number | null
      • ratio_s_free
        optional
        number | null
      • tax_sum_chf
        optional
        number | null
      • lat
        optional
        number | null

        WGS84 latitude of the parcel point (choose renders markers from lat/lng; geometry is never returned).

      • lng
        optional
        number | null

        WGS84 longitude.

      • cz_harmonized
        optional
        string | null
      • cz_local
        optional
        string | null
      • cz_util_now
        optional
        number | null
      • fso_name_2021
        optional
        string | null

        FSO municipality name - never null, unlike cityname (address/GWR-derived, null for ~48 % of parcels: every one without a building).

      • fso_num_2021
        optional
        number | null

        BFS/FSO municipality number.

  • totalFeatures
    integer
    required

    Same value as numberMatched (kept for WFS-shaped clients).

  • numberMatched
    integer
    required

    Matching rows, counted with a cap: the count query stops at 10,001 rows, so this is at most 10000 and capped says whether the true total is larger.

  • numberReturned
    integer
    required

    Rows in features (≤ maxResults).

  • capped
    boolean
    required

    True when more than 10,000 rows match; render as "10,000+".

500

Query failed or exceeded the 20 s statement timeout - {"error":"parcel search failed"}.
JSON
  • error
    string
    required

    Human-readable error description.

  • message
    optional
    string

    Optional underlying error message.

Type to search…

↑↓ navigate↵ selectEsc close