Files
Subhash Polisetti 620993807f Azure AI Search: Expose bring-your-own index fields as metadata (#6022)
## Issue
Closes #2973. Also covers the metadata-mapping gap reported in #1098.

## Change
`AzureAiSearchContentRetriever` and `AzureAiSearchEmbeddingStore` mapped
segment metadata
only from the nested complex `metadata` field this store writes itself.
Against a
pre-existing index whose fields live at the document root
(`createOrUpdateIndex(false)`),
those fields were unreachable, so results came back without their
metadata.

Add `metadataFieldNames` to both builders: an explicit allowlist of
top-level index fields
to expose as metadata.

```java
ContentRetriever retriever = AzureAiSearchContentRetriever.builder()
        .endpoint(endpoint)
        .tokenCredential(tokenCredential)
        .indexName(indexName)
        .createOrUpdateIndex(false)
        .queryType(AzureAiSearchQueryType.FULL_TEXT)
        .metadataFieldNames(List.of("sourcepage", "weburl", "topic", "role"))
        .build();
```

The listed fields are added to each result's `Metadata`, alongside the
complex `metadata`
field when present. Both classes share `getEmbeddingMatches`, so vector,
full-text, hybrid
and hybrid+reranking searches are all covered.

A field is copied only when its value is a type `Metadata` supports
(`String`, `Integer`,
`Long`, `Float`, `Double`, or `UUID`), preserving the type; absent,
`null` and other-typed
values (such as the embedding vector) are skipped. An explicit allowlist
keeps field
selection predictable for bring-your-own indexes with arbitrary schemas.
The default is an
empty list, so existing behaviour is unchanged and the API change is
additive only: the
existing `metadataFrom(Object)` helper is kept as-is and the new
behaviour is a delegating
overload, so no revapi entries and no changes to the existing tests. The
reformatting in
`AzureAiSearchEmbeddingStore` is applied by Spotless (`ratchetFrom
origin/main`).

This PR configures metadata extraction only; the index's core ID,
content, vector and
semantic-search field names are unchanged (the broader field-name
configuration in #1098
is out of scope here).

## Tests
`AzureAiSearchEmbeddingStoreTest`, offline. The existing nested-metadata
tests are kept as-is
(the `metadataFrom(Object)` helper is unchanged); new tests cover the
added overload:
- allowlisted fields copied; `Integer` / `Double` / `UUID` values
preserved with their type
- absent / `null` / unsupported-typed / blank-name fields skipped
- complex metadata and allowlisted fields merged; top-level field wins
on key collision
- the builder wires `metadataFieldNames` onto the store; `null` is
treated as none

```
mvn -pl langchain4j-azure-ai-search verify
Tests run: 34, Failures: 0, Errors: 0, Skipped: 0   (revapi + spotless green)
```

Core and main modules are green as well:

```
mvn -pl langchain4j-core,langchain4j test
langchain4j-core: Tests run: 1247, Failures: 0, Errors: 0, Skipped: 5
langchain4j:      Tests run: 1327, Failures: 0, Errors: 0, Skipped: 0
```

## General checklist
- [X] There are no breaking changes (API, behaviour)
- [X] I have added unit and/or integration tests for my change
- [X] The tests cover both positive and negative cases
- [X] I have manually run all the unit and integration tests in the
module I have added/changed, and they are all green
- [X] I have manually run all the unit and integration tests in the core
and main modules, and they are all green
- [X] I have added/updated the documentation
- [ ] I have added an example in the examples repo (only for "big"
features)
- [ ] I have added/updated Spring Boot starter(s) (if applicable)
2026-08-21 10:26:31 +02:00
..