Skip to content

Search: contributed index definitions and non-JPA entity support - #5702

Open
knstvk wants to merge 1 commit into
masterfrom
5701-dynmod-search
Open

knstvk wants to merge 1 commit into
masterfrom
5701-dynmod-search

Conversation

@knstvk

@knstvk knstvk commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Main issue: #5701

Summary

Lets a module contribute Search index definitions derived from runtime metadata, and makes Search's indexing, enqueueing and change-tracking paths work for entities and properties the JPA store does not know. This is the open-source half of the Dynamic Model integration; Search itself stays independent of Dynamic Model.

What was done

  • Added the IndexDefinitionContributor extension point (module search, package io.jmix.search.index.mapping) with the ContributedIndexDefinition record. Contributor beans are consulted every time the index configurations of a metadata generation are built. A contribution for an entity without a Java definition creates a configuration; one for an entity with a Java definition appends its fields, and fields the Java definition already maps win.
  • Indexing and enqueueing load a non-JPA entity through DataManager conditions instead of JPQL: the indexer's reload of instances, enqueueIndexAll, and the paged enqueueing session (new NonJpaEntityIdsLoader).
  • DependentEntitiesLoader no longer builds JPQL for a back-reference path that is not JPA-mapped. It loads the owners through a PropertyCondition on the reference instead. Before, the failed JPQL inside the save transaction marked it rollback-only, so saving an entity referenced through such a property failed. A path through a non-JPA collection is skipped with a warning.
  • Runtime state that used to be built once at startup became per entity name and lazy: IndexStateRegistry reports entities of later generations as unavailable instead of unknown and ignores state changes for retired ones, and the Elasticsearch and OpenSearch index settings providers cache by entity name rather than by Java class, which changes for a dynamic entity on every generation.
  • StartupIndexSynchronizer runs on ApplicationStartedEvent at JmixOrder.LOWEST_PRECEDENCE instead of @PostConstruct, so add-ons that publish metadata on startup run first. Without that, the default create-or-recreate strategy compared indexes against a mapping built from incomplete metadata and dropped them on each restart.
  • search-flowui: fullTextFilter works on a list view of a non-JPA entity by restricting the primary key to the found ids with a PropertyCondition; a search hit whose entity has no detail view shows a warning notification; SearchResultsView passes the identifier value rather than the Id wrapper when reloading, which only the JPA store unwrapped.

How it works

IndexConfigurationManager parses the annotated Java definitions, then merges the contributions of every IndexDefinitionContributor bean into the same map, keyed by entity name, and registers the result for the current or pinned metadata generation. Because configurations are stored per generation, a new generation published at runtime rebuilds the merged set lazily on first use, and refreshIndexDefinitions() includes contributions. Everywhere Search used to run JPQL against an entity or property, it first asks MetadataTools whether the target is JPA; if not, it goes through DataManager with conditions, which the entity's own store translates.

How to use

Implement IndexDefinitionContributor as a Spring bean and return one ContributedIndexDefinition per entity, with the entity name, an optional index name and a MappingDefinition built with the existing builders. Return an empty collection when there is nothing to contribute. The definitions must describe the metadata generation that is pinned or current for the calling thread.

Breaking changes

  • FullTextFilter.getQueryCondition() no longer overrides the base method with a JpqlCondition return type; the inherited Condition is returned and may be a PropertyCondition. Code that relied on the covariant return type stops compiling.
  • Index synchronization at startup now happens on ApplicationStartedEvent instead of during bean initialization. Application code that expected indexes to be synchronized when the context is refreshed is affected.
  • IndexStateRegistry.markIndexAsAvailable / markIndexAsUnavailable no longer throw for an entity that is not indexed; they log at debug level.

Compatibility

Applications without a contributor and without non-JPA entities see the same index configurations as before. The startup order change applies to every application using Search.

Test plan

  • The non-JPA paths and the dependents loading by condition are exercised from the Dynamic Model test suite in jmix-premium (:dynmodel:test), because the search module's own test context has no non-JPA store.

@knstvk knstvk linked an issue Sep 18, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dynamic Model: integrate with the Search add-on

1 participant