FacetDiscovery: a facet vanishes on pages where all its values are unlabelled (conditionsOfAccess worst, 2431 page-instances) #6

Open
opened 2026-07-31 21:31:56 +00:00 by laoc · 1 comment
Owner

Found by TestOER reviewing wp-plugin-amb-nostr#5; reproduced independently on a
different corpus slice. Not caused by that PR — it is the
$labelled === 0 rejection in FacetDiscovery::finalizeCore(), which predates
every variant of the label rule.

Symptom

FacetDiscovery treats "family carries :id and a :prefLabel:<lang>" as
the test for whether a tag family is a filterable facet. Some publishers emit
learningResourceType:id with no prefLabel at all. On an archive page drawn
from their events, learningResourceType — the single most useful facet — is
silently absent from the filter UI.

The facet is not missing from the data. It is present and filterable by tag
value. Only the display label is absent.

Measured

TestOER, 8482 events: 3 of 38 publishers, 359 events affected.

Reproduced on a separate 1999-event sample paged from
wss://amb-relay.edufeed.org — 5 publishers with learningResourceType:id and
zero labels:

pubkey     events   lrt:id   lrt:label
610df6d6     1823     2862        5724
5c090631       75      107           0   <- facet vanishes
5a12b41e       17       26           0   <- facet vanishes
0b3e3e2c        2        1           0   <- facet vanishes
be93b210        1        1           0   <- facet vanishes
2c8d3be4        1        2           0   <- facet vanishes

5c090631 alone is 75 events, so this is not a long-tail curiosity — it is a
whole publisher's worth of pages rendering without their main facet.

TestOER also measured the same for conditionsOfAccess on two of those
publishers.

Why it stayed hidden

The corpus-level census reads as an absolute separation — every entity family
exactly 0 prefLabels, every vocabulary family thousands. That number is
dominated by 610df6d6, which is 91% of the corpus and labels everything.
discover() runs per page, and per page the picture is different. A
corpus-level aggregate cannot show a per-page false negative.

Options

  1. Fall back to the value when a concept carries no label — render the URI,
    or its last path segment. Keeps the facet, ugly labels.
  2. Vocabulary lookup. SkosLabelResolver already exists and resolves w3id
    vocabularies; the unlabelled values here are real vocabulary URIs, so this
    would produce proper labels. Costs a lookup and a cache.
  3. Split the two questions. Label presence decides how to render, not
    whether it is a facet. Facet-hood would need a different signal — which is
    the deeper fix and the larger change.

Related: 4 concept values in the labelled families also carry no label and would
render as raw URIs today (1 about from hochschulfaechersystematik, 3
learningResourceType from vocabs.sodix.de) — a different vocabulary source
than the labelled ones. So the gap tracks the vocabulary, not the value, which
is evidence for option 2.

My recommendation is 2 with 1 as the fallback when the lookup misses, but this
is a rendering/product call as much as a code one.

Found by TestOER reviewing wp-plugin-amb-nostr#5; reproduced independently on a different corpus slice. **Not** caused by that PR — it is the `$labelled === 0` rejection in `FacetDiscovery::finalizeCore()`, which predates every variant of the label rule. ## Symptom `FacetDiscovery` treats "family carries `:id` **and** a `:prefLabel:<lang>`" as the test for whether a tag family is a filterable facet. Some publishers emit `learningResourceType:id` with **no prefLabel at all**. On an archive page drawn from their events, `learningResourceType` — the single most useful facet — is silently absent from the filter UI. The facet is not missing from the data. It is present and filterable by tag value. Only the *display label* is absent. ## Measured TestOER, 8482 events: 3 of 38 publishers, 359 events affected. Reproduced on a separate 1999-event sample paged from `wss://amb-relay.edufeed.org` — 5 publishers with `learningResourceType:id` and zero labels: ``` pubkey events lrt:id lrt:label 610df6d6 1823 2862 5724 5c090631 75 107 0 <- facet vanishes 5a12b41e 17 26 0 <- facet vanishes 0b3e3e2c 2 1 0 <- facet vanishes be93b210 1 1 0 <- facet vanishes 2c8d3be4 1 2 0 <- facet vanishes ``` `5c090631` alone is 75 events, so this is not a long-tail curiosity — it is a whole publisher's worth of pages rendering without their main facet. TestOER also measured the same for `conditionsOfAccess` on two of those publishers. ## Why it stayed hidden The corpus-level census reads as an absolute separation — every entity family exactly 0 prefLabels, every vocabulary family thousands. That number is dominated by `610df6d6`, which is 91% of the corpus and labels everything. `discover()` runs **per page**, and per page the picture is different. A corpus-level aggregate cannot show a per-page false negative. ## Options 1. **Fall back to the value** when a concept carries no label — render the URI, or its last path segment. Keeps the facet, ugly labels. 2. **Vocabulary lookup.** `SkosLabelResolver` already exists and resolves w3id vocabularies; the unlabelled values here are real vocabulary URIs, so this would produce proper labels. Costs a lookup and a cache. 3. **Split the two questions.** Label presence decides *how to render*, not *whether it is a facet*. Facet-hood would need a different signal — which is the deeper fix and the larger change. Related: 4 concept values in the labelled families also carry no label and would render as raw URIs today (1 `about` from `hochschulfaechersystematik`, 3 `learningResourceType` from `vocabs.sodix.de`) — a different vocabulary source than the labelled ones. So the gap tracks the vocabulary, not the value, which is evidence for option 2. My recommendation is 2 with 1 as the fallback when the lookup misses, but this is a rendering/product call as much as a code one.
Author
Owner

Reframing this on TestOER's measurement: conditionsOfAccess is the bigger victim, not learningResourceType. I originally framed it around lrt; that was the wrong unit.

Two units, and they rank the families differently

Across a 67,384-page every-offset sweep (TestOER, 8482 events), counting page-instances where the facet vanishes from the dropdown:

conditionsOfAccess   2431
learningResourceType 1967
educationalLevel       42
about                  10
                     ----
                     4450   all of them the `labelled == 0` path

Counting events that carry :id with zero prefLabels instead, on my own independent 4999-event slice:

learningResourceType   95 of 4913 events
conditionsOfAccess     75 of 4898
about                  17 of 4966
educationalLevel       17 of 3576
audience                0        teaches  0

These disagree, and both are correct. lrt has more unlabelled events; conditionsOfAccess loses more pages, because a page only loses the facet when every event on it that carries the facet is unlabelled — so clustering dominates, not raw counts.

The page unit is the user-visible one — a vanished dropdown is a page-level event. So the issue is now framed on it, and conditionsOfAccess leads.

Not a regression, and bounded

All 4450 drops come from the label test, none from the type test. The pre-de6dde2 rule dropped these same 4450 plus 56 more (real about/learningResourceType facets with exactly 1 labelled value), so the shipped discriminator is strictly better here — this issue is what remains, not something it introduced.

Recommendation unchanged

SkosLabelResolver lookup with value-fallback. The unlabelled values are real vocabulary URIs (w3id.org/kim/..., vocabs.sodix.de/...), so the label is resolvable — it is a rendering gap, not missing data. Falling back to the raw value would keep the facet filterable even when no label resolves.

**Reframing this on TestOER's measurement: `conditionsOfAccess` is the bigger victim, not `learningResourceType`.** I originally framed it around lrt; that was the wrong unit. ## Two units, and they rank the families differently Across a 67,384-page every-offset sweep (TestOER, 8482 events), counting **page-instances where the facet vanishes from the dropdown**: ``` conditionsOfAccess 2431 learningResourceType 1967 educationalLevel 42 about 10 ---- 4450 all of them the `labelled == 0` path ``` Counting **events that carry `:id` with zero prefLabels** instead, on my own independent 4999-event slice: ``` learningResourceType 95 of 4913 events conditionsOfAccess 75 of 4898 about 17 of 4966 educationalLevel 17 of 3576 audience 0 teaches 0 ``` **These disagree, and both are correct.** lrt has more unlabelled *events*; `conditionsOfAccess` loses more *pages*, because a page only loses the facet when **every** event on it that carries the facet is unlabelled — so clustering dominates, not raw counts. **The page unit is the user-visible one** — a vanished dropdown is a page-level event. So the issue is now framed on it, and `conditionsOfAccess` leads. ## Not a regression, and bounded All 4450 drops come from the label test, none from the type test. The pre-`de6dde2` rule dropped these same 4450 **plus 56 more** (real `about`/`learningResourceType` facets with exactly 1 labelled value), so the shipped discriminator is strictly better here — this issue is what remains, not something it introduced. ## Recommendation unchanged `SkosLabelResolver` lookup with value-fallback. The unlabelled values are real vocabulary URIs (`w3id.org/kim/...`, `vocabs.sodix.de/...`), so the label is resolvable — it is a rendering gap, not missing data. Falling back to the raw value would keep the facet filterable even when no label resolves.
laoc changed title from FacetDiscovery: a facet vanishes for publishers who emit :id with no prefLabel to FacetDiscovery: a facet vanishes on pages where all its values are unlabelled (conditionsOfAccess worst, 2431 page-instances) 2026-07-31 21:52:41 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
edufeed/wp-plugin-amb-nostr#6
No description provided.