Search: ArtifactDocumentBuilder assemble() nested objects (assets, updates, publications)

Part of #2616, stacked on the flat-half MR (!1236). It targets that branch, so this diff is only the nested stages.

The flat-half MR built assemble()'s scalars and the parity harness. This adds the nested half: the four record arrays (external_resource, asset, update with created/modified, publication with primary_publication_designation), as pure transforms over the same fetchData() bundle. No DB, no fetch changes. After this, the only unbuilt field is the ATF block.

Most of the diff is captured fixtures, not code: about 830 lines of code and 2,900 of input/ + expected/ JSON.

Parity, not an exact match. The comparator gains a nested-record rule: each record array compares as an order-insensitive multiset, every sub-field reconciled by type (the ''/null and null/[] divergences from the flat fields carry over, plus an empty-reference rule for the makeExactReference-of-empties case). Intended divergences pass, a regression in any covered sub-field still fails. The orders a multiset can't see (author/editor sequence, the creator-first update combine, asset order) are pinned by the unit tests.

Two places the builder is more correct than the current index. Both are Logstash bugs the builder fixes, so they read as builder-vs-oracle differences, not parity passes:

  • a cross-path update event Logstash counts twice (UNION ALL) is de-duplicated (1 public doc, 547619);
  • publications with an all-NULL publication_type, which Logstash drops entirely, are indexed (574 public docs). The builder is right and the current index is wrong, so these stay out of the strict-parity fixtures. They do change _source for those docs at cutover, so worth a look before the switch.

Fixtures. Five new ids cover the nested edge cases the flat-half set didn't reach: asset annotations (473247), asset authors (8303), publication editors with ascii folding (14), the empty-reference case (285571), and an external_resource with null key/url (282417). The existing fixtures are reused as the covered set grows.

Not here. The ATF parser: atf/jtf/annotation and the derived atf_* block. That's the last stage.

Tests. 22 new unit tests drive the zips on synthetic rows (the casts, the creator-first combine, min/max timestamps, ascii alignment, primary detection), the parity test compares the four records field-by-field against captured ES. Both DB-free, full suite green in dev_cake_composer.

Merge request reports

Loading