Operations

Spring Data Meilisearch exposes its synchronous, template-style APIs through MeilisearchOperations. The interface combines document access and search APIs, and provides entry points to instance-level and index-level operations. Use the MeilisearchOperations interface in application code; see Index and Instance Operations for instance and index management, Settings for settings, and Object Mapping for entity conversion.

Entry Point

MeilisearchConfiguration registers one MeilisearchOperations bean with the names meilisearchOperations and meilisearchTemplate. Inject the interface when you need direct access to Meilisearch-specific operations instead of the repository abstraction.

getMeilisearchConverter() exposes the configured converter; see Object Mapping for its mapping and conversion behavior.

@RestController
class MovieController {

    private final MeilisearchOperations operations;

    MovieController(MeilisearchOperations operations) {
        this.operations = operations;
    }

    @PostMapping("/movies")
    Movie save(@RequestBody Movie movie) {
        return operations.save(movie);
    }
}

Document Operations

DocumentOperations works with one mapped entity type:

  • save(T) and save(List<T>)

  • get(String, Class<T>) and exists(String, Class<?>)

  • multiGet(Class<T>, List<String>) for ID-list retrieval

  • findAll(Class<T>) and findAll(Class<T>, Sort) for document listing

  • delete(String, Class<?>), delete(T), delete(Class<?>, List<String>), delete(List<T>), and deleteAll(Class<?>)

save(…​) writes through Meilisearch’s add-or-replace documents API. The API creates a missing index; replacing an existing document removes fields omitted from the new entity rather than applying a partial update. Document saves do not automatically apply @Setting metadata.

Both unsorted and sorted findAll(…​) use POST /documents/fetch without requesting specific fields. Meilisearch ignores displayedAttributes for document reads (see its regression test). Document listings can therefore return fields excluded from search results; grant documents.get only to callers allowed to read them.

get(…​) returns null when the document id is not present. findAll(Class<T>) retrieves all documents in one document request and holds the result in memory; it is not a Pageable search. A sorted listing also holds the full result in memory. It counts documents first and then fetches up to that count in one request, so concurrent additions between the requests may be absent. Sort properties must be configured as Meilisearch sortable attributes, and sorted document retrieval requires Meilisearch 1.16 or later. Neither document-list method is bounded by the search-only maxTotalHits setting.

Use multiGet(…​) when the ids are already known rather than listing or searching an index:

List<Movie> requested = operations.multiGet(Movie.class, List.of("movie-3", "movie-1", "missing"));

Missing documents are omitted; the remaining results preserve requested order and duplicate ids. The implementation requests document ids in internal batches; callers supply only the ids they want to retrieve. This uses Meilisearch’s native ID-list documents endpoint, which requires Meilisearch 1.14 or later; there is no individual-document lookup fallback for older servers.

Search Operations

SearchOperations provides:

  • long count(Class<?>) for the number of documents in the mapped index. This is a document count, not a query-filtered search count.

  • <T, Q extends BaseQuery> SearchHits<T> search(Q, Class<T>) for a single index.

  • <T, Q extends BaseQuery> SearchHits<T> multiSearch(List<Q>, Class<T>) for several requests in one non-federated multi-search.

  • <T, Q extends BaseQuery> SearchHits<T> multiSearch(List<Q>, MultiSearchFederation, Class<T>) for federated multi-search.

  • SearchHits<FacetHit> facetSearch(FacetQuery, Class<?>) for facet search.

  • <T> SearchHits<T> similarSearch(SimilarQuery, Class<T>) for similar-document search.

BasicQuery accepts Spring Data Pageable and Sort. The default query page is page 0 with 10 hits; a supplied Pageable is translated to Meilisearch’s one-based page and hitsPerPage values. Sort properties must be configured as sortable attributes. Filter expressions likewise require the referenced attributes to be filterable.

BasicQuery query = BasicQuery.builder()
        .withQ("Wonder Woman")
        .withFilter("genres = Action")
        .withPageable(PageRequest.of(0, 20, Sort.by("title")))
        .build();

SearchHits<Movie> result = operations.search(query, Movie.class);
List<Movie> movies = result.getSearchHits().stream()
        .map(SearchHit::getContent)
        .toList();

Use IndexQuery to override the mapped index uid for an individual request. All results are mapped to the entity type passed to multiSearch, so the target indexes must return documents compatible with that type.

List<IndexQuery> queries = List.of(
        IndexQuery.builder().withQ("Wonder Woman").withIndexUid("movies").build(),
        IndexQuery.builder().withQ("Wonder Woman").withIndexUid("comics").build());

SearchHits<Movie> combined = operations.multiSearch(queries, Movie.class);

Non-federated multi-search flattens per-query hits into one SearchHits list in query order; it does not return a separate SearchHits object for each query or an aggregate total.

Non-federated multi-search supports each query’s Pageable. Federated multi-search instead takes the SDK’s MultiSearchFederation options for the combined result window. Per-query Pageable settings are not sent in federated mode; set federation limit and offset instead.

MultiSearchFederation federation = new MultiSearchFederation();
federation.setOffset(0);
federation.setLimit(10);

SearchHits<Movie> federated = operations.multiSearch(queries, federation, Movie.class);

Query Types

Spring Data Meilisearch provides these query value types:

  • BasicQuery for standard search.

  • IndexQuery for per-index multi-search requests, including federated requests.

  • FacetQuery for facet search.

  • SimilarQuery for similar-document search.

SimilarQuery requires a source document id and the name of an embedder configured for the target index:

SimilarQuery similarQuery = SimilarQuery.builder()
        .withDocumentId("movie-1")
        .withEmbedder("default")
        .build();

SearchHits<Movie> similar = operations.similarSearch(similarQuery, Movie.class);

Search Result Types

Search methods return SearchHits<T>, which exposes the hit list, request execution duration, and total-hit metadata when the response provides it. Each SearchHit<T> contains the mapped content, Meilisearch processing time and query, optional facet statistics/distribution, and optional federation metadata.

SearchHits#getTotalHitsRelation() identifies whether getTotalHits() contains a total reported by the search response:

  • EQUAL_TO means Meilisearch reported the total matching-hit count.

  • OFF means total-hit metadata is unavailable; getTotalHits() then reflects the number of loaded hits and must not be treated as a total for the full result set.

Facet search, similar search, and multi-search results do not provide an aggregate total through this API. For a single-index search, use total-hit metadata only when its relation is EQUAL_TO.

The maxTotalHits pagination setting limits how many matching hits can be retrieved through paginated search; it does not necessarily cap the reported total. For example, the current implementation can return 10 hits while reporting 11 total matches when maxTotalHits is 10. Repository findAll(Pageable) uses search results and their reported total, while an unpaged repository lookup uses document retrieval. See Pagination Limits for the pagination setting.

Kotlin Extensions

Spring Data Meilisearch provides Kotlin extension functions for blocking operations that otherwise require an entity Class argument. Reified type parameters let Kotlin callers omit explicit ::class.java arguments.

import io.vanslog.spring.data.meilisearch.core.*

val movie = operations.get<Movie>("movie-1")
val hits = operations.search<Movie>(query)
val count = operations.count<Movie>()
val index = operations.indexOps<Movie>()

operations.applySettings<Movie>()

These extensions are thin wrappers over the Java contracts. They do not change blocking execution behavior or add reactive or coroutine repository support.

Repository Results and Search Metadata

Repository methods return mapped entities (or a Page<T> for paged findAll) rather than SearchHit<T> wrappers. Use MeilisearchOperations directly when you need per-hit metadata such as facet statistics or federation details from a manually issued search.