5
votes

I am trying to understand queries in general in Hibernate Search. I have some trouble understanding the forEntity(...) method. This is what the documentation says:

Let's see how to use the API. You first need to create a query builder that is attached to a given indexed entity type. This QueryBuilder will know what analyzer to use and what field bridge to apply. You can create several QueryBuilders (one for each entity type involved in the root of your query). You get the QueryBuilder from the SearchFactory.

From section: 5.1.2. Building a Lucene query with the Hibernate Search query DSL

QueryBuilder mythQB = searchFactory.buildQueryBuilder().forEntity( Myth.class ).get();

From above you see that you have to name an entity. What are you supposed to to when you want to create an own querybuilder for creating a boolean query inside the "root" query? What should you bind that too?

Let say I want to have a boolean query that should match either "Apples" or "Pie". That is two different entity, so currently I have two different querybuilders for them. But I need a third one to create a boolean query. Should this be bound to Object class?

2

2 Answers

3
votes

If you want to be able to return multiple entities from a single query, you can use the QueryBuilder as you stated, but you'll want to specify multiple entities in the createFullTextQuery invocation. For example, if you have a Book entity and a Movie entity, and you want to find all books and movies whose titles start with d, you could use the following query:

QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(Book.class).get();
Query query = queryBuilder.keyword().wildcard().onField("title").matching("d*").createQuery();
org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery(query, Book.class, Movie.class);

Notice that the query builder is created with just the Book entity, but both Books and Movies are specified in the createFullTextQuery invocation.

1
votes

The boolean operators are called should instead of OR because of the names they have in the Lucene API and documentation, and because it is more appropriate: it is not only influencing a boolean decision, but it also affects scoring of the result.

For example if you search for cars "of brand Fiat" OR "blue", the cars branded Fiat AND blue will also be returned and having an higher score than those which are blue but not Fiat.

It might feel cumbersome because it's programmatic and provides many detailed options. A simpler alternative is to use a simple string for your query and use the QueryParser to create the query. Generally the parser is useful to parse user input, the programmatic one is easier to deal with well defined fields; for example if you have the collection you mentioned it's easy to build it in a for loop.

Collection<String> namesCollection = getNames(); // Contains "billy" and "bob", for example
StringBuilder names = new StringBuilder(100);
for(String name : namesCollection) {
    names.append(name).append(" "); // Never mind the space at the end of the resulting string.
}

QueryBuilder b = fts.getSearchFactory().buildQueryBuilder().forEntity(Person.class).get();
Query luceneQuery = b.bool()
    .should(
        // Searches for multiple possible values in the same field
        b.keyword().onField("firstName").matching( sb.toString() ).createQuery()
    )
    .must(b.keyword().onField("lastName").matching("thornton").createQuery())
    .createQuery();