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()}}:
{code} 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 {code}
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:
{code} 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> {code}
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:
{code} 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> {code}
On a side note, a result of these changes is that we could potentially expose the query fetch methods directly on the last step of the query DSL and not force users to call {{toQuery}} for simple use cases anymore : => see HSEARCH-3572 {code} searchSession.search( MyEntity.class ).asEntity() .predicate( ... ) .fetch() // Returns the search result directly! {code} |
|