Skip to content

Commit

Permalink
HSEARCH-2983 Take boosts into account for all queries built with the DSL
Browse files Browse the repository at this point in the history
Instead of setting the boost on a per query type basis, let's set it one level
up so that we're sure it's defined for all the query types.
  • Loading branch information
gsmet authored and Sanne committed Jan 3, 2018
1 parent 02dd905 commit 4de27cb
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 17 deletions.
Expand Up @@ -66,14 +66,21 @@ public Query createQuery() {
final int size = fieldsContext.size();
final ConversionContext conversionContext = new ContextualExceptionBridgeHelper();
if ( size == 1 ) {
return queryCustomizer.setWrappedQuery( createQuery( fieldsContext.getFirst(), conversionContext ) ).createQuery();
FieldContext fieldContext = fieldsContext.getFirst();
return queryCustomizer.setWrappedQuery(
fieldContext.getFieldCustomizer().setWrappedQuery(
createQuery( fieldContext, conversionContext )
).createQuery()
).createQuery();
}
else {
BooleanQuery.Builder aggregatedFieldsQueryBuilder = new BooleanQuery.Builder();
for ( FieldContext fieldContext : fieldsContext ) {
aggregatedFieldsQueryBuilder.add(
createQuery( fieldContext, conversionContext ),
BooleanClause.Occur.SHOULD
fieldContext.getFieldCustomizer().setWrappedQuery(
createQuery( fieldContext, conversionContext ) )
.createQuery(),
BooleanClause.Occur.SHOULD
);
}
BooleanQuery aggregatedFieldsQuery = aggregatedFieldsQueryBuilder.build();
Expand All @@ -82,7 +89,6 @@ public Query createQuery() {
}

private Query createQuery(FieldContext fieldContext, ConversionContext conversionContext) {
final Query perFieldQuery;
final DocumentBuilderIndexedEntity documentBuilder = queryContext.getDocumentBuilder();
final boolean applyTokenization;

Expand All @@ -107,19 +113,17 @@ private Query createQuery(FieldContext fieldContext, ConversionContext conversio
final String searchTerm = buildSearchTerm( fieldContext, documentBuilder, conversionContext );

if ( !applyTokenization || BridgeAdaptorUtils.unwrapAdaptorOnly( fieldBridge, IgnoreAnalyzerBridge.class ) != null ) {
perFieldQuery = createTermQuery( fieldContext, searchTerm );
return createTermQuery( fieldContext, searchTerm );
}

// we need to build differentiated queries depending on if the search terms should be analyzed
// locally or not
if ( queryContext.getQueryAnalyzerReference().is( RemoteAnalyzerReference.class ) ) {
return createRemoteQuery( fieldContext, searchTerm );
}
else {
// we need to build differentiated queries depending of if the search terms should be analyzed
// locally or not
if ( queryContext.getQueryAnalyzerReference().is( RemoteAnalyzerReference.class ) ) {
perFieldQuery = createRemoteQuery( fieldContext, searchTerm );
}
else {
perFieldQuery = createLuceneQuery( fieldContext, searchTerm );
}
return createLuceneQuery( fieldContext, searchTerm );
}
return fieldContext.getFieldCustomizer().setWrappedQuery( perFieldQuery ).createQuery();
}

private void validateNullValueIsSearchable(FieldContext fieldContext) {
Expand Down
112 changes: 112 additions & 0 deletions engine/src/test/java/org/hibernate/search/test/dsl/BoostDSLTest.java
@@ -0,0 +1,112 @@
/*
* 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.test.dsl;

import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.testsupport.TestForIssue;
import org.hibernate.search.testsupport.junit.SearchFactoryHolder;
import org.hibernate.search.testsupport.junit.SearchITHelper;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

/**
* @author Guillaume Smet
*/
@TestForIssue(jiraKey = "HSEARCH-2983")
public class BoostDSLTest {

@Rule
public final SearchFactoryHolder sfHolder = new SearchFactoryHolder( Coffee.class, CoffeeBrand.class );

private final SearchITHelper helper = new SearchITHelper( sfHolder );

@Before
public void setUp() throws Exception {
indexTestData();
}

@Test
public void testBoostOnTermQuery() {
QueryBuilder qb = helper.queryBuilder( Coffee.class );

Query query = qb.bool()
.should( qb.keyword().onField( "name" ).boostedTo( 40f ).matching( "Kazaar" ).createQuery() )
.should( qb.keyword().onField( "summary" ).boostedTo( 1f ).matching( "VELVETY" ).createQuery() )
.createQuery();

helper.assertThat( query ).from( Coffee.class )
.sort( new Sort( SortField.FIELD_SCORE ) )
.matchesExactlyIds( "Kazaar", "Dharkan" );

query = qb.bool()
.should( qb.keyword().onField( "name" ).boostedTo( 1f ).matching( "Kazaar" ).createQuery() )
.should( qb.keyword().onField( "summary" ).boostedTo( 40f ).matching( "VELVETY" ).createQuery() )
.createQuery();

helper.assertThat( query ).from( Coffee.class )
.sort( new Sort( SortField.FIELD_SCORE ) )
.matchesExactlyIds( "Dharkan", "Kazaar" );
}

@Test
public void testBoostOnNumericQuery() {
QueryBuilder qb = helper.queryBuilder( Coffee.class );

Query query = qb.bool()
.should( qb.keyword().onField( "name" ).boostedTo( 40f ).matching( "Kazaar" ).createQuery() )
.should( qb.keyword().onField( "intensity" ).boostedTo( 1f ).matching( 11 ).createQuery() )
.createQuery();

helper.assertThat( query ).from( Coffee.class )
.sort( new Sort( SortField.FIELD_SCORE ) )
.matchesExactlyIds( "Kazaar", "Dharkan" );

query = qb.bool()
.should( qb.keyword().onField( "name" ).boostedTo( 1f ).matching( "Kazaar" ).createQuery() )
.should( qb.keyword().onField( "intensity" ).boostedTo( 40f ).matching( 11 ).createQuery() )
.createQuery();

helper.assertThat( query ).from( Coffee.class )
.sort( new Sort( SortField.FIELD_SCORE ) )
.matchesExactlyIds( "Dharkan", "Kazaar" );
}

private void indexTestData() {
createCoffee(
"Kazaar",
"EXCEPTIONALLY INTENSE AND SYRUPY",
"A daring blend of two Robustas from Brazil and Guatemala, specially prepared for Nespresso, and a separately roasted Arabica from South America, Kazaar is a coffee of exceptional intensity. Its powerful bitterness and notes of pepper are balanced by a full and creamy texture.",
12
);
createCoffee(
"Dharkan",
"LONG ROASTED AND VELVETY",
"This blend of Arabicas from Latin America and Asia fully unveils its character thanks to the technique of long roasting at a low temperature. Its powerful personality reveals intense roasted notes together with hints of bitter cocoa powder and toasted cereals that express themselves in a silky and velvety txture.",
11
);
createCoffee(
"Ristretto",
"POWERFUL AND CONTRASTING",
"A blend of South American and East African Arabicas, with a touch of Robusta, roasted separately to create the subtle fruity note of this full-bodied, intense espresso.",
10
);
}

private void createCoffee(String name, String summary, String description, int intensity) {
Coffee coffee = new Coffee();
coffee.setId( name );
coffee.setName( name );
coffee.setSummary( summary );
coffee.setDescription( description );
coffee.setIntensity( intensity );
helper.add( coffee );
}
}
Expand Up @@ -48,6 +48,7 @@ public void setId(String id) {
public void setDescription(String description) { this.description = description; }
private String description;

@Field
public int getIntensity() { return intensity; }
public void setIntensity(int intensity) { this.intensity = intensity; }
private int intensity;
Expand Down
Expand Up @@ -406,10 +406,10 @@ private void indexTestData() {
);
}

private void createCoffee(String title, String summary, String description, int intensity, CoffeeBrand brand) {
private void createCoffee(String name, String summary, String description, int intensity, CoffeeBrand brand) {
Coffee coffee = new Coffee();
coffee.setId( title );
coffee.setName( title );
coffee.setId( name );
coffee.setName( name );
coffee.setSummary( summary );
coffee.setDescription( description );
coffee.setIntensity( intensity );
Expand Down

0 comments on commit 4de27cb

Please sign in to comment.