You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Expose Solr nested documents through the RODA UI in two complementary ways:
Advanced AIP Search — filter the AIP catalogue by nested child metadata (e.g. "show AIPs containing an email from X with subject Y")
Virtual Catalogues — a new dropdown context that queries nested children directly as first-class results (e.g. "show all emails across all mailbox AIPs")
The email archive is the reference use case but the implementation must be fully generic — any metadata type that produces nested Solr documents with a content_type field benefits automatically through configuration alone.
Note
Nested document support in Solr is already wired end-to-end: SolrXMLLoader parses <field name="X"><doc>...</doc></field> blocks into child SolrInputDocument entries, and indexDescriptiveMetadataFields propagates them to the parent AIP document. The only missing piece is the ingest XSLT crosswalk and the UI configuration to expose and search those children.
The API endpoint /api/v2/aips/find always deserialises results into IndexedAIP. For child documents, mandatory AIP fields (permissions, ancestors, ghost, etc.) come from the parent or are empty. Child-specific dynamic Solr fields (subject_txt, sender_s, …) appear in indexedAip.getFields(). ConfigurableAsyncTableCell already reads from this map for any non-default column — no new Java result type is needed.
Child document UUID pattern
Child UUIDs follow the format {parentAIPUUID}/{fieldName}#{index} (e.g. 5f28e162-.../emails#42). The parent UUID is extracted by splitting on / and taking the first segment — used for the row-click navigation to browse/{parentUUID}.
ui.lists.{listId}.catalogue.* config; extend SearchWrapper.Components for multiple IndexedAIP lists; virtual list builders with ChildOfFilterParameter.
Developer guide: how to add any nested metadata type (XSD + XSLT + config); virtual catalogue setup; advanced search nested groups; EmailArchive worked example.
Dependency order
Phase 0 (XSD + XSLT)
├── Phase 1 (nested filter groups) ← independent of Phase 2, can run in parallel
└── Phase 2 (virtual catalogue)
└── Phase 3 (history wiring)
└── Phase 4 (i18n + tests)
└── Phase 5 (documentation)
Key Design Decisions
Important
No new Java result type.ConfigurableAsyncTableCell<IndexedAIP> reads object.getFields().get(c.getField()) for custom columns. Virtual catalogue columns map directly to dynamic Solr field names — no IndexedGenericDocument or VirtualCatalogueList class required.
Important
Config follows ui.lists.{listId}.* convention. Virtual catalogues are defined under the same ui.lists namespace as Search_AIPs, with new catalogue.* sub-keys for dropdown label, icon, base filter, and click action. No new JSON config files.
Important
History reuses $prefilter scheme. Virtual catalogue selection maps to #search/$prefilter/title/{Label}/@{listId}. The @Search_emails token is parsed by the existing handlePreFilterSearch() — it ends up in classFilters keyed by "Search_emails" and the constructor routes it to the virtual list builder. No changes to Search.RESOLVER.
Warning
SearchWrapper.Components clash. The inner class is keyed by Class<? extends IsIndexed>. Multiple virtual catalogues using IndexedAIP would overwrite each other. Phase 2 adds parallel string-keyed maps (virtualSearchPanels, virtualLists) keyed by listId.
Feature Overview
Expose Solr nested documents through the RODA UI in two complementary ways:
The email archive is the reference use case but the implementation must be fully generic — any metadata type that produces nested Solr documents with a
content_typefield benefits automatically through configuration alone.Note
Nested document support in Solr is already wired end-to-end:
SolrXMLLoaderparses<field name="X"><doc>...</doc></field>blocks into childSolrInputDocumententries, andindexDescriptiveMetadataFieldspropagates them to the parent AIP document. The only missing piece is the ingest XSLT crosswalk and the UI configuration to expose and search those children.Architecture
Data flow diagram
Why children come back as
IndexedAIPThe API endpoint
/api/v2/aips/findalways deserialises results intoIndexedAIP. For child documents, mandatory AIP fields (permissions,ancestors,ghost, etc.) come from the parent or are empty. Child-specific dynamic Solr fields (subject_txt,sender_s, …) appear inindexedAip.getFields().ConfigurableAsyncTableCellalready reads from this map for any non-default column — no new Java result type is needed.Child document UUID pattern
Child UUIDs follow the format
{parentAIPUUID}/{fieldName}#{index}(e.g.5f28e162-.../emails#42). The parent UUID is extracted by splitting on/and taking the first segment — used for the row-click navigation tobrowse/{parentUUID}.Implementation Phases
roda-wui.propertiesregistration. Zero Java.nested_groupfield type inAdvancedSearchFieldsPanel; compiles toParentWhichFilterParameter.ui.lists.{listId}.catalogue.*config; extendSearchWrapper.Componentsfor multipleIndexedAIPlists; virtual list builders withChildOfFilterParameter.$prefilterscheme; fixhandlePreFilterSearchfor empty filter tokens.Dependency order
Key Design Decisions
Important
No new Java result type.
ConfigurableAsyncTableCell<IndexedAIP>readsobject.getFields().get(c.getField())for custom columns. Virtual catalogue columns map directly to dynamic Solr field names — noIndexedGenericDocumentorVirtualCatalogueListclass required.Important
Config follows
ui.lists.{listId}.*convention. Virtual catalogues are defined under the sameui.listsnamespace asSearch_AIPs, with newcatalogue.*sub-keys for dropdown label, icon, base filter, and click action. No new JSON config files.Important
History reuses
$prefilterscheme. Virtual catalogue selection maps to#search/$prefilter/title/{Label}/@{listId}. The@Search_emailstoken is parsed by the existinghandlePreFilterSearch()— it ends up inclassFilterskeyed by"Search_emails"and the constructor routes it to the virtual list builder. No changes toSearch.RESOLVER.Warning
SearchWrapper.Componentsclash. The inner class is keyed byClass<? extends IsIndexed>. Multiple virtual catalogues usingIndexedAIPwould overwrite each other. Phase 2 adds parallel string-keyed maps (virtualSearchPanels,virtualLists) keyed bylistId.Affected Files Summary
All files touched across all phases
New files:
roda-core/roda-core/src/main/resources/config/schemas/emailarchive.xsdroda-core/roda-core/src/main/resources/config/crosswalks/ingest/emailarchive.xsltroda-core/roda-core-tests/.../NestedDocumentsTest.javaModified files:
roda-core/roda-common-data/.../data/common/RodaConstants.javaroda-core/roda-common-data/.../data/v2/index/SearchField.javaroda-ui/roda-wui/src/main/resources/config/roda-wui.propertiesroda-ui/roda-wui/src/main/resources/config/i18n/ServerMessages.properties(+ all locale files)roda-ui/.../client/common/search/AdvancedSearchFieldsPanel.javaroda-ui/.../client/common/search/SearchWrapper.javaroda-ui/.../client/common/search/CatalogueSearch.java