-
Notifications
You must be signed in to change notification settings - Fork 1
Material Dropdown Grouping
How the Material filter dropdown fetches its options and organizes them into high-level categories.
See PR #24.
The Material dropdown is a multi-select rendered by FlatSelect in SampleFilters.tsx. Its options and counts are fetched live via SPARQL — not hardcoded. Each material type is grouped under one of four high-level buckets, with an Other catch-all:
- Water
- Biota
- Solid Material
- Air
- Other
| Layer | File | Role |
|---|---|---|
| UI | src/components/QueryEditor/SampleFilters.tsx |
Renders FlatSelect, maps material types → options |
| Render | src/components/QueryEditor/FlatSelect/FlatSelect.tsx |
Grouped render with sticky section headers |
| Hook |
src/hooks/useDiscoveryQueries.ts (useMaterialTypes) |
Runs the query, maps bucketPrio → group |
| Query |
src/engine/templates/regions.ts (buildDiscoverMaterialTypesQuery) |
Builds the SPARQL |
| Client | src/engine/sparqlClient.ts |
POSTs the query |
| Constants | src/constants/materialTypes.ts |
MaterialType.group, MATERIAL_GROUP_BY_PRIO, fallback list |
Endpoints (src/constants/endpoints.ts):
-
https://frink.apps.renci.org/sawgraph/sparql— default (no region filter) -
https://frink.apps.renci.org/federation/sparql— when a region/state is selected
Each material type is bucketed graph-side. A priority VALUES block binds every bucket a type matches; MIN(?prio) picks the winner. Unmatched types have no ?bucketPrio and fall to Other.
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX coso: <http://w3id.org/coso/v1/contaminoso#>
SELECT ?matType (SAMPLE(?_label) AS ?label)
(COUNT(DISTINCT ?observation) AS ?num)
(MIN(?prio) AS ?bucketPrio) WHERE {
?observation rdf:type coso:ContaminantObservation ;
coso:analyzedSample ?sample .
?sample coso:sampleOfMaterialType ?matType .
OPTIONAL {
?sample rdf:type ?bucketClass .
VALUES (?bucketClass ?prio) {
(coso:BiotaSample 1)
(coso:SolidMaterialSample 2)
(coso:WaterSample 3)
(coso:AirSample 4)
}
}
OPTIONAL { ?matType rdfs:label ?_label . }
FILTER(STRSTARTS(STR(?matType), "http://w3id.org/"))
} GROUP BY ?matType
ORDER BY DESC(?num) ?labelThe region-filtered branch adds a SamplePoint region pattern before the observation triples but keeps the same VALUES block, so grouping works with or without a region selected.
They are the direct children of coso:MaterialSample in the ontology, so they partition the space at the top tier:
sosa:Sample
└─ coso:MaterialSample (catch-all root — matches all types, NOT a bucket)
├─ coso:AirSample
├─ coso:BiotaSample → Animal* (Blood, Milk, Organ, Tissue), Plant*
├─ coso:SolidMaterialSample → Sediment, Soil, SolidWaste
└─ coso:WaterSample → BySource (Drinking/Ground/Surface/Waste), ByTreatment (Filtered/Raw/Treated)
MaterialSample itself is the catch-all root (matches all 151 types) and is intentionally not used as a bucket.
Data isn't perfectly clean — ~11 material types are sampled under more than one bucket (e.g. SEDIMENT, SOIL, MILK, BEEF, MANURE).
MIN(?bucketPrio) resolves these deterministically. A single global order can't satisfy every case — e.g. BEEF and MANURE are both Biota-vs-Solid ties but arguably want opposite buckets — so one or two assignments are debatable rather than wrong.
Bucketing follows whatever samples are in scope:
-
Unfiltered — MANURE's observation-samples include
BiotaSample, so it groups under Biota. -
Region filter applied — its in-scope samples are only
SolidMaterialSample, so it groups under Solid Material.
Both are correct for their data slice. No override is hardcoded.
Some types fall to Other because their observation-samples carry none of the four subclasses (only the MaterialSample root or a WQP media type): STORM WATER RUNOFF, PROCESS WATER, FEED, COMPOST, FRUIT, GRASS, etc. This is the honest graph-driven result. Pulling them in would mean extending the VALUES set (e.g. WQP SampleMedia) or adding a label-keyword fallback — deferred.
The ontology nests deeper (Water › BySource/ByTreatment). FlatSelect stays flat with one tier of sticky headers; sub-sections can be added later if needed.
If the query fails or returns nothing, the hook uses FALLBACK_MATERIAL_TYPES from src/constants/materialTypes.ts (ungrouped).
Dropdowns
In the repo