Context
`services/catalogue-service/catalogue-db-queries.go:51`:
```cypher
OPTIONAL MATCH (itm)-[pv:HAS_CATALOGUE_PROPERTY]->(:CatalogueProperty)
```
The actual node label is `CatalogueCategoryProperty` (confirmed in PR #416 root-cause). `:CatalogueProperty` matches nothing, so `pv` is always null, `pval` is always empty string, and the search-by-property-value branch in the items list query is dead code — searching for "DN 100" in catalogue items never matches on property values, only on item name/description/catalogueNumber/supplier name.
Why not fixed in PR #416
Fixing the label would make the OPTIONAL MATCH actually match — but it returns one row per item-property pair, which then participates in `WITH DISTINCT itm, cat, q, iname, idesc, icn, sname, pval`. `pval` differs per property, so DISTINCT no longer collapses items to one row each → pagination over duplicate rows.
Proposed fix
Restructure the search-by-property-value as a subquery:
```cypher
WITH itm, cat, q, iname, idesc, icn, sname
OPTIONAL MATCH (itm)-[pv:HAS_CATALOGUE_PROPERTY]->(:CatalogueCategoryProperty)
WITH itm, cat, q, iname, idesc, icn, sname,
any(v IN collect(toLower(toString(pv.value))) WHERE v CONTAINS q) AS hasMatchingPropVal
WHERE q = '' OR iname CONTAINS q OR ... OR hasMatchingPropVal
```
Or use a CALL{} subquery returning a boolean.
Impact if fixed
- Free-text search will find items by property value (e.g. searching "DN 100" finds all items with that flange size).
- Likely surfaces UX expectations users have been silently working around.
Related
Context
`services/catalogue-service/catalogue-db-queries.go:51`:
```cypher
OPTIONAL MATCH (itm)-[pv:HAS_CATALOGUE_PROPERTY]->(:CatalogueProperty)
```
The actual node label is `CatalogueCategoryProperty` (confirmed in PR #416 root-cause). `:CatalogueProperty` matches nothing, so `pv` is always null, `pval` is always empty string, and the search-by-property-value branch in the items list query is dead code — searching for "DN 100" in catalogue items never matches on property values, only on item name/description/catalogueNumber/supplier name.
Why not fixed in PR #416
Fixing the label would make the OPTIONAL MATCH actually match — but it returns one row per item-property pair, which then participates in `WITH DISTINCT itm, cat, q, iname, idesc, icn, sname, pval`. `pval` differs per property, so DISTINCT no longer collapses items to one row each → pagination over duplicate rows.
Proposed fix
Restructure the search-by-property-value as a subquery:
```cypher
WITH itm, cat, q, iname, idesc, icn, sname
OPTIONAL MATCH (itm)-[pv:HAS_CATALOGUE_PROPERTY]->(:CatalogueCategoryProperty)
WITH itm, cat, q, iname, idesc, icn, sname,
any(v IN collect(toLower(toString(pv.value))) WHERE v CONTAINS q) AS hasMatchingPropVal
WHERE q = '' OR iname CONTAINS q OR ... OR hasMatchingPropVal
```
Or use a CALL{} subquery returning a boolean.
Impact if fixed
Related