Search code examples
javahibernatelucenehibernate-search

Is it possible to create something similar to WHERE NOT IN ( :values ) in Hibernate Search?


I am actively searching through the Internet for solution to something that I know can be accomplished but I am frustrated why does it not work with Hibernate Search. What I am looking for is something similar to SQL clause where not in (1,2,3)

I have two entities, A and B.

@Indexed
public class A
{
   @Field
   public String field1;
   @FieldBridge(impl = LongBridge.class)
   public Long field2;
   @IndexEmbedded
   public Set<B> names;
}

public class B {
   @Field
   public String field3;
   @ContainedIn
   public Set<A> names2;
}

I am positively running search using querybuilder which looks like this:

queryBuilder.keyword()
                .fuzzy()
                .onFields("field1", "names.field3", "field2")
                .matching(matching+"-field2:(1 2 3)")
                .createQuery();

But matching does not read this as full Lucene matching pattern I think. It returns values that have field2 set to 1 2 3 instead of filtering those documents out.

EDIT1: I have found boolean query builder to be helpfull, I have created query with call to not() expecting it to filter the data. The underlying lucene query is what I expected, the same as I wrote in the first approach, but the lucene still returns wrong values. On the other hand Luke tool for lucene indexes queries with this expression returns what I am expecting.

EDIT2: What I was doing wrong is that I used single query and concatinated values, while each value of the field2 should have been carried by another boolean junction. It wokrs great now!

What do I do wrong? Should I use another mechanism? Is it even supported by Hibernate Search?


Solution

  • The argument to .keyword()...matching( ... ) is not a query that accepts special characters. This method expects to be passed plain text. You cannot use - or : in there.

    If all this content is passed directly by users and you do need something that accepts operators, have a look at simpleQueryString().

    If instead the filter is provided by yourself, the developer, just use the bool query.

    In your case:

    Query includedQuery = queryBuilder.keyword()
                    .fuzzy()
                    .onFields("field1", "names.field3", "field2")
                    .matching(matching)
                    .createQuery();
    
    BooleanJunction<?> combinedQueryBuilder = querybuilder.bool();
    combinedQueryBuilder.must(includedQuery);
    for (String excluded : Arrays.asList("1", "2", "3")) {
        combinedQueryBuilder.mustNot(queryBuilder.keyword()
                    .fuzzy()
                    .onField("field2")
                    .matching(excluded)
                    .createQuery());
    }
    
    Query combinedQuery = combinedQueryBuilder.createQuery();