Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.elasticsearch.search.predicate.impl;

import org.hibernate.search.backend.elasticsearch.gson.impl.JsonAccessor;
import org.hibernate.search.backend.elasticsearch.gson.impl.JsonObjectAccessor;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.spi.MatchNonePredicateBuilder;

import com.google.gson.JsonObject;


class ElasticsearchMatchNonePredicate extends AbstractElasticsearchPredicate {

private static final JsonObjectAccessor MATCH_NONE_ACCESSOR = JsonAccessor.root().property( "match_none" )
.asObject();

private ElasticsearchMatchNonePredicate(Builder builder) {
super( builder );
}

@Override
public void checkNestableWithin(String expectedParentNestedPath) {
// Nothing to do
}

@Override
protected JsonObject doToJsonQuery(PredicateRequestContext context,
JsonObject outerObject, JsonObject innerObject) {
MATCH_NONE_ACCESSOR.set( outerObject, innerObject );
return outerObject;
}

static class Builder extends AbstractBuilder implements MatchNonePredicateBuilder {
Builder(ElasticsearchSearchIndexScope<?> scope) {
super( scope );
}

@Override
public SearchPredicate build() {
return new ElasticsearchMatchNonePredicate( this );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.hibernate.search.engine.search.predicate.spi.BooleanPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchAllPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchIdPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchNonePredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.SearchPredicateBuilderFactory;
import org.hibernate.search.engine.search.predicate.spi.SimpleQueryStringPredicateBuilder;

Expand All @@ -28,6 +29,11 @@ public MatchAllPredicateBuilder matchAll() {
return new ElasticsearchMatchAllPredicate.Builder( scope );
}

@Override
public MatchNonePredicateBuilder matchNone() {
return new ElasticsearchMatchNonePredicate.Builder( scope );
}

@Override
public MatchIdPredicateBuilder id() {
return new ElasticsearchMatchIdPredicate.Builder( scope );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.lucene.search.predicate.impl;

import org.hibernate.search.backend.lucene.search.common.impl.LuceneSearchIndexScope;
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.spi.MatchNonePredicateBuilder;

import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;


class LuceneMatchNonePredicate extends AbstractLuceneSearchPredicate {

private LuceneMatchNonePredicate(Builder builder) {
super( builder );
}

@Override
public void checkNestableWithin(String expectedParentNestedPath) {
// Nothing to do
}

@Override
protected Query doToQuery(PredicateRequestContext context) {
return new MatchNoDocsQuery();
}

static class Builder extends AbstractBuilder implements MatchNonePredicateBuilder {
Builder(LuceneSearchIndexScope<?> scope) {
super( scope );
}

@Override
public SearchPredicate build() {
return new LuceneMatchNonePredicate( this );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.hibernate.search.engine.search.predicate.spi.BooleanPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchAllPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchIdPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchNonePredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.SearchPredicateBuilderFactory;
import org.hibernate.search.engine.search.predicate.spi.SimpleQueryStringPredicateBuilder;

Expand All @@ -29,6 +30,11 @@ public MatchAllPredicateBuilder matchAll() {
return new LuceneMatchAllPredicate.Builder( scope );
}

@Override
public MatchNonePredicateBuilder matchNone() {
return new LuceneMatchNonePredicate.Builder( scope );
}

@Override
public MatchIdPredicateBuilder id() {
return new LuceneMatchIdPredicate.Builder( scope );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ include::{sourcedir}/org/hibernate/search/documentation/search/predicate/Predica
* The score of a `matchAll` predicate is constant and equal to 1 by default,
but can be <<search-dsl-predicate-common-boost,boosted with `.boost(...)`>>.

[[search-dsl-predicate-match-none]]
== `matchNone`: match no documents

The `matchNone` predicate is the inverse of `matchAll` and matches no documents.

.Matching no documents
====
[source, JAVA, indent=0, subs="+callouts"]
----
include::{sourcedir}/org/hibernate/search/documentation/search/predicate/PredicateDslIT.java[tags=matchNone]
----
====

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I didn't include the original use case in the ticket; here it is: https://discourse.hibernate.org/t/fail-fast-predicate/6062?u=yrodiere

The best solution to avoid running a search query is simply to not call .fetch() at all, but in some cases people will have methods dedicated to the creation of inner predicates, and will not be able to prevent the query to run; in that case, they can just create a matchNone predicate and the end result will be the same (though it will unfortunately involve some pointless I/O).

So, as you can see, it's a very specific use case, for advanced users. I'm not sure we need further explanation than what you did here, but if you think you can add something clear and concise, feel free to give it a shot :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's an interesting case... Knew of match_all usage within dynamic filters but not the match_none. nice 😃
As for the text, I gave it a try ... aaaand the text becomes too cumbersome 😄 but as you've said - it's for advanced users. So those who need it would know its meaning, and others are probably safer without using it 😃

[[search-dsl-predicate-id]]
== `id`: match a document identifier

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,20 @@ public void matchAll() {
} );
}

@Test
public void matchNone() {
withinSearchSession( searchSession -> {
// tag::matchNone[]
List<Book> hits = searchSession.search( Book.class )
.where( f -> f.matchNone() )
.fetchHits( 20 );
// end::matchNone[]
assertThat( hits )
.extracting( Book::getId )
.isEmpty();
} );
}

@Test
public void id() {
// DO NOT USE THE BOOKX_ID CONSTANTS INSIDE TAGS BELOW:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.engine.search.predicate.dsl;

/**
* The initial and final step in "match none" predicate definition.
*/
public interface MatchNonePredicateFinalStep extends PredicateFinalStep {
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public interface SearchPredicateFactory {
*/
MatchAllPredicateOptionsStep<?> matchAll();

/**
* Match none of the documents.
*
* @return The initial step of a DSL where the "match none" predicate can be defined.
* @see MatchNonePredicateFinalStep
*/
MatchNonePredicateFinalStep matchNone();

/**
* Match documents where the identifier is among the given values.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.engine.search.predicate.dsl.impl;

import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.MatchNonePredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.spi.AbstractPredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.spi.SearchPredicateDslContext;
import org.hibernate.search.engine.search.predicate.spi.MatchNonePredicateBuilder;


public final class MatchNonePredicateFinalStepImpl extends AbstractPredicateFinalStep
implements MatchNonePredicateFinalStep {

private final MatchNonePredicateBuilder matchNoneBuilder;

public MatchNonePredicateFinalStepImpl(SearchPredicateDslContext<?> dslContext) {
super( dslContext );
this.matchNoneBuilder = dslContext.scope().predicateBuilders().matchNone();
}

@Override
protected SearchPredicate build() {
return matchNoneBuilder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.search.engine.search.predicate.dsl.ExtendedSearchPredicateFactory;
import org.hibernate.search.engine.search.predicate.dsl.MatchAllPredicateOptionsStep;
import org.hibernate.search.engine.search.predicate.dsl.MatchIdPredicateMatchingStep;
import org.hibernate.search.engine.search.predicate.dsl.MatchNonePredicateFinalStep;
import org.hibernate.search.engine.search.predicate.dsl.MatchPredicateFieldStep;
import org.hibernate.search.engine.search.predicate.dsl.NamedPredicateOptionsStep;
import org.hibernate.search.engine.search.predicate.dsl.NestedPredicateFieldStep;
Expand All @@ -32,6 +33,7 @@
import org.hibernate.search.engine.search.predicate.dsl.impl.ExistsPredicateFieldStepImpl;
import org.hibernate.search.engine.search.predicate.dsl.impl.MatchAllPredicateOptionsStepImpl;
import org.hibernate.search.engine.search.predicate.dsl.impl.MatchIdPredicateMatchingStepImpl;
import org.hibernate.search.engine.search.predicate.dsl.impl.MatchNonePredicateFinalStepImpl;
import org.hibernate.search.engine.search.predicate.dsl.impl.MatchPredicateFieldStepImpl;
import org.hibernate.search.engine.search.predicate.dsl.impl.NamedPredicateOptionsStepImpl;
import org.hibernate.search.engine.search.predicate.dsl.impl.NestedPredicateFieldStepImpl;
Expand Down Expand Up @@ -64,6 +66,11 @@ public MatchAllPredicateOptionsStep<?> matchAll() {
return new MatchAllPredicateOptionsStepImpl( dslContext, this );
}

@Override
public MatchNonePredicateFinalStep matchNone() {
return new MatchNonePredicateFinalStepImpl( dslContext );
}

@Override
public MatchIdPredicateMatchingStep<?> id() {
return new MatchIdPredicateMatchingStepImpl( dslContext );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.engine.search.predicate.spi;

public interface MatchNonePredicateBuilder extends SearchPredicateBuilder {

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public interface SearchPredicateBuilderFactory {

MatchAllPredicateBuilder matchAll();

MatchNonePredicateBuilder matchNone();

MatchIdPredicateBuilder id();

BooleanPredicateBuilder bool();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.integrationtest.backend.tck.search.predicate;

import static org.hibernate.search.util.impl.integrationtest.common.assertion.SearchResultAssert.assertThatQuery;

import org.hibernate.search.engine.backend.document.IndexFieldReference;
import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaElement;
import org.hibernate.search.engine.backend.types.dsl.IndexFieldTypeFactory;
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
import org.hibernate.search.integrationtest.backend.tck.testsupport.util.rule.SearchSetupHelper;
import org.hibernate.search.util.impl.integrationtest.mapper.stub.SimpleMappedIndex;

import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;

public class MatchNonePredicateSpecificsIT {

private static final String DOCUMENT_1 = "1";
private static final String STRING_1 = "aaa";

private static final String DOCUMENT_2 = "2";
private static final String STRING_2 = "bbb";

private static final String DOCUMENT_3 = "3";
private static final String STRING_3 = "ccc";

@ClassRule
public static final SearchSetupHelper setupHelper = new SearchSetupHelper();

private static final SimpleMappedIndex<IndexBinding> index = SimpleMappedIndex.of( IndexBinding::new );

@BeforeClass
public static void setup() {
setupHelper.start().withIndex( index ).setup();

initData();
}

@Test
public void matchNone() {
assertThatQuery( index.query()
.where( SearchPredicateFactory::matchNone ) )
.hasNoHits();
}

Comment on lines +45 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just add another test with a bool predicate containing one predicate that does match a document, and also the matchNone predicate, to check that the resulting bool predicate won't match anything?

@Test
public void matchNoneWithinBoolPredicate() {
//check that we will find something with a single match predicate
assertThatQuery( index.query()
.where(
f -> f.bool()
.must( f.match().field( "string" ).matching( STRING_1 ).toPredicate() )
)
).hasTotalHitCount( 1 );

// make sure that matchNone will "override" the other matching predicate
assertThatQuery( index.query()
.where(
f -> f.bool()
.must( f.match().field( "string" ).matching( STRING_1 ).toPredicate() )
.must( f.matchNone() )
)
).hasNoHits();
}

private static void initData() {
index.bulkIndexer()
.add( DOCUMENT_1, document -> document.addValue( index.binding().string, STRING_1 ) )
.add( DOCUMENT_2, document -> document.addValue( index.binding().string, STRING_2 ) )
.add( DOCUMENT_3, document -> document.addValue( index.binding().string, STRING_3 ) )
.join();
}

private static class IndexBinding {
final IndexFieldReference<String> string;

IndexBinding(IndexSchemaElement root) {
string = root.field( "string", IndexFieldTypeFactory::asString ).toReference();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.hibernate.search.engine.search.predicate.spi.ExistsPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchAllPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchIdPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchNonePredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.MatchPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.NamedPredicateBuilder;
import org.hibernate.search.engine.search.predicate.spi.NestedPredicateBuilder;
Expand Down Expand Up @@ -101,6 +102,7 @@ public void flags(Set<SimpleQueryFlag> flags) {
}

public static class Builder implements MatchAllPredicateBuilder,
MatchNonePredicateBuilder,
BooleanPredicateBuilder,
MatchIdPredicateBuilder,
MatchPredicateBuilder,
Expand Down
Loading