The purge ran one correlated ``NOT EXISTS`` scan of ``ir_ui_view`` per
candidate attachment (``ILIKE '%web/image/<id>%'`` on ``arch_db::text``),
taking hours on large databases. It now scans ``ir_ui_view`` once with
``regexp_matches``, builds a deduplicated set of referenced ids, and
deletes orphans via hash lookups.
This also fixes substring false positives: ``/web/image/12`` was kept
because it matched ``/web/image/123``. Integer comparison (``ref_id =
a.id``) now requires an exact match.