Annotation-based Indexing

Instead of implementing and registering indexers by hand, GigaMap can derive them from annotations on your entity type. The core module ships the bitmap annotations; integration modules add their own.

Annotation Module Produces

@Index, @Unique, @Identity

gigamap

Bitmap indices (see Defining Indices)

@SpatialIndex

gigamap

Spatial (geo) index (see Spatial Index)

@FullText

gigamap-lucene

Lucene full-text index (see Lucene · Annotations)

@Vector

gigamap-jvector

JVector similarity index (see JVector · Annotations)

@Indexed

gigamap

Type-level marker flagging an annotated entity (optional)

Generating the indices

Bitmap indices alone:

GigaMap<Person> gigaMap = GigaMap.New();
IndexerGenerator.AnnotationBased(Person.class).generateIndices(gigaMap.index().bitmap());

To also generate indices contributed by integration modules, register their handlers and pass the GigaMap (the bitmap indices are generated first, then each handler runs in registration order):

IndexerGenerator.AnnotationBased(Article.class)
    .register(LuceneAnnotationHandler.New())   // gigamap-lucene
    .register(VectorAnnotationHandler.New())   // gigamap-jvector
    .generateIndices(gigaMap);

The convenience method does the same in one call:

IndexerGenerator.generate(Article.class, gigaMap,
    LuceneAnnotationHandler.New(),
    VectorAnnotationHandler.New());

Registration uses explicit handlers (no classpath scanning), so only the modules you depend on participate.

Compile-time metamodel (annotation processor)

IndexerGenerator builds the indexers reflectively at runtime. As an alternative, the optional gigamap-codegen module is an annotation processor that, at compile time, generates a sibling metamodel class <Entity>_ for each annotated entity — typed public static final indexer constants plus a registerIndices(GigaMap) helper. This gives you compile-time index names, IDE autocomplete and refactor-safety, and reads members through getters / accessible fields instead of reflection.

Add the processor to the maven-compiler-plugin (it is only needed at compile time, not at runtime):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <annotationProcessorPaths>
            <path>
                <groupId>org.eclipse.store</groupId>
                <artifactId>gigamap-codegen</artifactId>
                <version>4.2.0</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

For an annotated Person, the processor emits Person_ (into target/generated-sources/annotations) with a typed constant per bitmap / spatial index and an idempotent registration helper. Query through the constants just like hand-written ones:

GigaMap<Person> gigaMap = GigaMap.New();
Person_.registerIndices(gigaMap);   // ensures all indices, unique constraints, identity indices

gigaMap.query(Person_.firstName.startsWith("J"));
gigaMap.query(Person_.score.between(10, 20));

registerIndices(…​) is idempotent and reload-safe: call it on a freshly created map or re-run it on a map loaded from storage (re-registration is a no-op, like the runtime generator).

@FullText and @Vector are wired up too, but — unlike bitmap indices — without a typed query constant, since a LuceneIndex / VectorIndex is a per-map runtime object. For these the processor generates a reflection-free DocumentPopulator / Vectorizer and registers the index inside registerIndices(…​) (using in-graph / in-memory defaults); you still query through the runtime handle:

Article_.registerIndices(gigaMap);
gigaMap.index().get(LuceneIndex.class).query("title:gigamap");
gigaMap.index().get(VectorIndices.class).get("embedding").search(queryVector, 10);

The processor depends only on the GigaMap core and reads the @FullText / @Vector annotations by name, so bitmap-only projects do not pull the Lucene / JVector libraries onto the processor path; the generated code references those types, which are on your classpath whenever you use the annotations.

A custom @Index(creator = …​) is wired up too: the processor generates a constant initialized from the creator and registers it (a plain creator via new TheCreator().create(); a Creator.MemberAware is handed the index name and the member first). Because the creator decides the indexer at runtime, the constant is typed Indexer<Entity, K> (the key type K is taken from the creator’s Creator<E,K> binding when concrete, else a wildcard), which supports equality queries (Entity_.code.is(value)); flavor-specific methods are not exposed. The creator must be referenceable from the entity’s package (a public, or same-package, class with an accessible no-argument constructor); otherwise the processor emits a note and leaves that index to the runtime generator.

The metamodel name suffix (default , e.g. Person -> Person) is configurable via the processor option -Agigamap.metamodel.suffix=…​.

@Vector(onDisk = true) cannot be reproduced at compile time (it needs an index directory) and an inaccessible custom creator falls back to the runtime path; both are skipped with a compiler note — register those via the runtime IndexerGenerator / VectorAnnotationHandler.New(Path). The compile-time metamodel complements the runtime generator rather than replacing it — both resolve indices by name, so they interoperate.
The generated bitmap, spatial, full-text and vector reads use getters and package-accessible fields rather than reflection, so the compile-time path needs no JPMS opens. The one exception is a Creator.MemberAware creator, whose member is resolved with getDeclaredField / getDeclaredMethod — but that reflection targets the entity’s own module, which is allowed without opens.

Adding your own annotation handler

An index type outside the core module participates by implementing GigaIndexAnnotationHandler. The handler reflects over the entity type for the annotations it owns and registers the matching IndexCategory on the supplied GigaIndices; it must be a no-op when the type carries none of its annotations.

public final class MyAnnotationHandler<E> implements GigaIndexAnnotationHandler<E>
{
    @Override
    public void contribute(Class<E> entityType, GigaIndices<E> indices)
    {
        if(/* entityType has none of my annotations */)
        {
            return;
        }
        indices.register(MyIndex.Category(/* configuration derived from the annotations */));
    }
}

This is exactly how LuceneAnnotationHandler and VectorAnnotationHandler are built; use them as a reference.

The generator and handlers read annotated members reflectively. For entities in a named Java module, open the entity package to the modules that read it — org.eclipse.store.gigamap for bitmap/spatial annotations and the respective integration module (e.g. org.eclipse.store.gigamap.lucene) for its annotations.