[hibernate-dev] Feature Proposal for Hibernate Search

Emmanuel Bernard emmanuel at hibernate.org
Tue Feb 4 07:38:02 EST 2014


On Tue 2014-02-04  4:24, Martin Braun wrote:
> 	/**
> 	 * @return a custom sort object or null
> 	 */
> 	public Sort getSort();
> 
> 
> or how to facet, etc...
> 
> 
> I want for this stuff to move to annotations (maybe) because I don't really like
> the fact that I have to implement an Interface for that (and the current approach doesn't
> allow profiles).

Here is an half formed alternative idea that reverts your model.

    class SomeQuery {
    
        // some params as getter / setters
    
        @Query(profile="a", forEntity=Customer.class)
        public Query query(QueryBuilder qb) {
            return qb.bool()
                .must(
                    qb.keyword()
                        .onField("name")
                            .boostedTo(2f)
                        .matchingParameter("name")
                        .createQuery();
                ).createQuery(); //some other boolean query should be here
        }
    
        @Sort(profile="a")
        Sort getSort() { ... }
    
        ...
    }

    session.createFullTextQuery(someQuery).list();

What this achieved is:

- all query info in one class
- support for multiple profiles
- reuse existing programmatic API

Probably some kind of interface would make more sense to propagate the
return type.

> > But annotations in my opinion don't scale very well for tree structures. Composition, lack of polymorphism are also not easy / elegant in annotations. 
> > You can see that you had to break the sub queries with string references. 
> 
> 
> I think they don't scale well in the general case but in this scenario I think it's ok.
> 
> 
> Look at this:
> 
> 
> 
> @Queries(@Query(profile = "notName", must = @Must(subQuery = "someNameForTheQuery")))
> @SubQueries(@SubQuery(id = "someNameForTheQuery", query = @Query(must = @Must(not = true, value = @SearchField(fieldName = "name", propertyName = "name")))))
> 
> 
> 
> I look at it this way: I lose the easy way to do tree structures, but I gain a way to document my query by having to give names to my sub-queries :).

I can document my subqueries by name using Java local variables :)

    Query filterByName = qb.keyword().onField("name").matchingParameter("name").createQuery();
    return qb.all().except(filterByName).createQuery();

BTW, I wonder why in your annotation approach all @Query are junction
queries. There is a small redundancy as the subquery defines a must not
in your example and the top query defines a must. What would happen if
the top query defined a @Should? Bootstrap error?
This is one of the things that throw off Hardy (I think) and me when we
look at the current annotations.

> 
> 
> > And I am not sure how you will be able to express other query types that we have like range, spatial etc. I might be wrong, so it's worth trying. But even then, if we had one new query type, we
> > have to also add an equivalent annotation or add new fields to an existing one. 
> 
> 
> 
> Uhm. I totally forgot to add this functionality in my rewrite (I have rewritten most of the code last weekend)
> and I implemented a way to pass parameters into QueryTypes. You can even pass parameters that are dynamically
> determined by stating the property to get its value from. With that you can easily do RangeQueryTypes.
> If you want me to elaborate on that, I can provide you with an example.

Yes :)

> 
> 
> > What do you mean by query nesting by hand?
> 
> 
> Consider you have a query like this (again with boolean syntax):
> 
> 
> (queryA || queryD) && (queryB || (queryC) || queryE,...
> 
> 
> Now you would have to build a BooleanJunction and several subJunctions
> and then put them together. I have written that type of code before and
> I made some mistakes because I messed up the variable names in my head.
> 
> 
> > And how does the annotation approach differs?
> 
> 
> In the annotation Version it would look something like this (example from above):
> 
> 
> 
> @Queries(@Query(must = {
> 	@Must(subQuery = "queryAOrQueryD"),
> 	@Must(subQuery = "queryBOrQueryC"),
> 	@Must(subQuery = "queryE")}))
> @SubQueries({
> 	@SubQuery(id = "queryAOrQueryD", 
> 		query = @Query(should = {@Should(subQuery = "queryA"), @Should(subQuery = "queryD")})
> 	@SubQuery(id = "queryBOrQueryC",
> 		query = @Query(should = {@Should(subQuery = "queryB"), @Should(subQuery = "queryC")})
> 	...
> 	//define query[A-E] here.
> })
> 
> 
> 
> Maybe it's because I don't like to work with the Junctions and put them together myself,
> and maybe it's only me that thinks this would be easier :P.

OK. BTW this is how I would write such query decomposing it. I would not
rely on the intermediary BooleanJunction object.

Query queryA = ...
Query queryB = ...
Query queryC = ...
Query queryD = ...
Query queryE = ...

qb.bool()
    .must(
        qb.bool().should(queryA).should(queryD).createQuery()
    )
    .must(
        qb.bool().should(queryB).should(queryC).query(E).createQuery()
    )
    .must(
        queryE
    )
    .createQuery();


More information about the hibernate-dev mailing list