The problem is two-fold:
When creating a query, we need to expose APIs that are specific to Elasticsearch or Lucene at the top level, i.e. on the context returned by .predicate( ... ) or .sort(...).
The query itself should expose APIs that are specific to Elasticsearch or Lucene, to retrieve results that are specific to that backend.
This is necessary to implement major features that might only be supported by one backend, such as aggregations (HSEARCH-3003) that will only be supported by the Elasticsearch backend, at least at first.
The first problem should be solved fairly easily by adding some .extension() call before .predicate():
The second problem will require to change our approach a little bit. Currently toQuery() returns a query type that is defined by the mapper; e.g. for the ORM mapper it returns org.hibernate.search.mapper.orm.search.query.SearchQuery. If we want to return a different type of query for each backend, we can no longer return a different type of query for each mapper.
One solution would be to expose a toQuery method that allows to pass an argument to determine the type of query:
Note we have to use extensions as arguments, not just Class<Q>, because the returned query has a generic type parameter.
I can only see two problems with this approach:
The syntax is a bit ugly. But then so are the other extension points.
We cannot easily document that the resulting query is flawed, like we currently do in org.hibernate.search.mapper.orm.search.query.SearchQuery#toOrmQuery.
Maybe we should make .toQuery( HibernateOrmExtension.get() ) return an adapter that itself offers a toOrmQuery method:
On a side note, a result of these changes is that we could potentially expose the fetch methods directly on the last step of the query DSL and not force users to call toQuery for simple use cases anymore => see