OpenSearch-Java client, what is idiomatic?

Given the API shape in opensearch-java 1.0.0, what is the intended idiomatic use of the library? It seems there are too paths in the API when you start creating something such as a query, and they don’t overlap as well due to lacking overloads (accept function, accept Builder, accept Query, …).

Example:

        var response = osClient.search(
                new SearchRequest.Builder()
                        .index("fruits")
                        .query(QueryBuilders.matchAll().build()._toQuery()).build(), Fruit.class);

        var response2 = osClient.search(
                new SearchRequest.Builder()
                        .index("fruits")
                        .query(q -> q.matchAll(ma -> ma))
                        .build(), Fruit.class);

        var response3 = osClient.search(
                SearchRequest.of(b -> b.index("fruits").query(q -> q.matchAll(ma -> ma))), Fruit.class);

        var response4 =
                osClient.search(b -> b.index("fruits").query(q -> q.matchAll(ma -> ma)), Fruit.class);

        var response5 =
                osClient.search(b -> b.index("fruits").query(QueryBuilders.matchAll().build()._toQuery()), Fruit.class);

So either use the builder form of matchAll with the non optional configuration lambda, or use the builder which is longer because you have to build it and convert it. This seems clumsy.

To make this Kotlin pretty, I would need to add a function overload for each of these to turn them from foo(config: (builder)->result) to foo(config: builder.()->result) style lambdas to avoid the constant need to reference the builder, but rather make it the receiver and the this.

The API feels like it is a constant fight to find what is “smooth” and “natural” because the API itself doesn’t seem to know what it wants to be.