We're updating the issue view to help you get more done. 

Allow extensions at the top level of the query DSL

Description

The problem is two-fold:

  1. 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(...).

  2. 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():

1 2 3 4 5 searchSession.search( MyEntity.class ).asEntity() // returns an interface defined in the engine .extension( ElasticsearchExtension.get() ) // Returns a specialization of SearchQueryResultContext<Q> .predicate( ... ) // Returns a specialization of SearchQueryContext<Q> .aggregation( ... ) // Returns a specialization of SearchQueryContext<Q> .toQuery() // Returns Q

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:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 searchSession.search( MyEntity.class ).asEntity() .predicate( ... ) .toQuery() // Returns an engine-defined interface: SearchQuery<T> searchSession.search( MyEntity.class ).asEntity() .extension( ElasticsearchExtension.get() ) .predicate( ... ) .someEsSpecificMethod( ... ) .toQuery() // Returns an Elasticsearch-specific interface: ElasticsearchSearchQuery<T> searchSession.search( MyEntity.class ).asEntity() .predicate( ... ) .toQuery( HibernateOrmExtension.get() ) // Returns a org.hibernate.query.Query<T> searchSession.search( MyEntity.class ).asEntity() .extension( ElasticsearchExtension.get() ) .predicate( ... ) .someEsSpecificMethod( ... ) .toQuery( HibernateOrmExtension.get() ) // Returns a org.hibernate.query.Query<T>

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:

1 2 3 4 5 6 7 HibernateOrmSearchQuery<T> query = searchSession.search( MyEntity.class ).asEntity() .extension( ElasticsearchExtension.get() ) .predicate( ... ) .someEsSpecificMethod( ... ) .toQuery( HibernateOrmExtension.get() ); query.fetch() // can be called directly query.toOrmQuery() // returns a org.hibernate.query.Query<T>

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

Environment

None

Status

Assignee

Yoann Rodière

Reporter

Yoann Rodière

Labels

None

Suitable for new contributors

None

Feedback Requested

None

Fix versions

Priority

Major