/* * 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.query.dsl; import static org.fest.assertions.Assertions.assertThat; import java.util.Arrays; import java.util.List; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.lucene.search.SortField.Type; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.search.FullTextQuery; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; import org.hibernate.search.query.dsl.QueryBuilder; import org.hibernate.search.test.SearchTestBase; import org.hibernate.search.testsupport.TestForIssue; import org.junit.Before; import org.junit.Test; /** * @author Guillaume Smet */ public class SimpleQueryStringDSLTest extends SearchTestBase { private FullTextSession fullTextSession; @Before @Override public void setUp() throws Exception { super.setUp(); Session session = openSession(); fullTextSession = Search.getFullTextSession( session ); indexTestData(); } @Test @TestForIssue(jiraKey = "HSEARCH-2678") @SuppressWarnings({ "unchecked" }) public void testSimpleQueryString() { Transaction transaction = fullTextSession.beginTransaction(); try { QueryBuilder qb = getCoffeeQueryBuilder(); Query query = qb.simpleQueryString() .onFields( "name", "summary", "description" ) .withAndAsDefaultOperator() .matching( "balanced arabica" ) .createQuery(); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( query, Coffee.class ); fullTextQuery.setSort( new Sort( new SortField( Coffee.NAME_SORT, Type.STRING ) ) ); List<Coffee> results = fullTextQuery.getResultList(); compareCoffeeResultsAndExpected( Arrays.asList( "Dulsão do Brasil", "Kazaar", "Livanto" ), results ); query = qb.simpleQueryString() .onFields( "name", "summary", "description" ) .withAndAsDefaultOperator() .matching( "-balanced arabica" ) .createQuery(); fullTextQuery = fullTextSession.createFullTextQuery( query, Coffee.class ); fullTextQuery.setSort( new Sort( new SortField( Coffee.NAME_SORT, Type.STRING ) ) ); results = fullTextQuery.getResultList(); compareCoffeeResultsAndExpected( Arrays.asList( "Bukeela ka Ethiopia", "Linizio Lungo", "Volluto" ), results ); query = qb.simpleQueryString() .onFields( "name", "summary", "description" ) .withAndAsDefaultOperator() .matching( "powerful \"fruity note\"" ) .createQuery(); fullTextQuery = fullTextSession.createFullTextQuery( query, Coffee.class ); fullTextQuery.setSort( new Sort( new SortField( Coffee.NAME_SORT, Type.STRING ) ) ); results = fullTextQuery.getResultList(); compareCoffeeResultsAndExpected( Arrays.asList( "Ristretto" ), results ); query = qb.simpleQueryString() .onFields( "name", "summary", "description" ) .matching( "sweet robust" ) .createQuery(); fullTextQuery = fullTextSession.createFullTextQuery( query, Coffee.class ); fullTextQuery.setSort( new Sort( new SortField( Coffee.NAME_SORT, Type.STRING ) ) ); results = fullTextQuery.getResultList(); compareCoffeeResultsAndExpected( Arrays.asList( "Caramelito", "Dulsão do Brasil", "Roma", "Volluto" ), results ); } finally { transaction.commit(); } } @Test @TestForIssue(jiraKey = "HSEARCH-2678") @SuppressWarnings({ "unchecked" }) public void testBoost() { Transaction transaction = fullTextSession.beginTransaction(); try { QueryBuilder qb = getCoffeeQueryBuilder(); Query query = qb.simpleQueryString() .onFields( "name", "summary" ).boostedTo( 5f ) .andField( "description" ) .withAndAsDefaultOperator() .matching( "fruity arabicas" ) .createQuery(); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( query, Coffee.class ); fullTextQuery.setSort( new Sort( SortField.FIELD_SCORE ) ); List<Coffee> results = fullTextQuery.getResultList(); compareCoffeeResultsAndExpected( Arrays.asList( "Rosabaya de Colombia", "Decaffeinato", "Ristretto" ), results ); query = qb.simpleQueryString() .onFields( "name", "summary" ) .andField( "description" ).boostedTo( 10f ) .withAndAsDefaultOperator() .matching( "fruity arabicas" ) .createQuery(); fullTextQuery = fullTextSession.createFullTextQuery( query, Coffee.class ); fullTextQuery.setSort( new Sort( SortField.FIELD_SCORE ) ); results = fullTextQuery.getResultList(); compareCoffeeResultsAndExpected( Arrays.asList( "Ristretto", "Rosabaya de Colombia", "Decaffeinato" ), results ); } finally { transaction.commit(); } } @Test @TestForIssue(jiraKey = "HSEARCH-2678") @SuppressWarnings({ "unchecked" }) public void testFuzzy() { Transaction transaction = fullTextSession.beginTransaction(); try { QueryBuilder qb = getCoffeeQueryBuilder(); Query query = qb.simpleQueryString() .onFields( "name", "summary", "description" ) .withAndAsDefaultOperator() .matching( "fruity arabica~2" ) .createQuery(); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( query, Coffee.class ); fullTextQuery.setSort( new Sort( new SortField( Coffee.NAME_SORT, Type.STRING ) ) ); List<Coffee> results = fullTextQuery.getResultList(); compareCoffeeResultsAndExpected( Arrays.asList( "Decaffeinato", "Ristretto", "Rosabaya de Colombia", "Volluto" ), results ); } finally { transaction.commit(); } } @Test @TestForIssue(jiraKey = "HSEARCH-2678") @SuppressWarnings({ "unchecked" }) public void testAnalyzer() { Transaction transaction = fullTextSession.beginTransaction(); try { QueryBuilder qb = getBookQueryBuilder(); Query query = qb.simpleQueryString() .onFields( "title", "author" ) .withAndAsDefaultOperator() .matching( "Molière" ) .createQuery(); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( query, Book.class ); fullTextQuery.setSort( new Sort( new SortField( "title", SortField.Type.STRING ) ) ); List<Book> results = fullTextQuery.getResultList(); compareBookResultsAndExpected( Arrays.asList( "Le Grand Molière illustré", "Tartuffe" ), results ); query = qb.simpleQueryString() .onFields( "title", "author" ) .withAndAsDefaultOperator() .matching( "deplacait" ) .createQuery(); fullTextQuery = fullTextSession.createFullTextQuery( query, Book.class ); fullTextQuery.setSort( new Sort( new SortField( "title", SortField.Type.STRING ) ) ); results = fullTextQuery.getResultList(); compareBookResultsAndExpected( Arrays.asList( "Le chat qui déplaçait des montagnes" ), results ); qb = fullTextSession.getSearchFactory() .buildQueryBuilder() .forEntity( Book.class ) .overridesForField( "author", "titleAnalyzer" ) .get(); query = qb.simpleQueryString() .onFields( "title", "author" ) .withAndAsDefaultOperator() .matching( "Molière" ) .createQuery(); fullTextQuery = fullTextSession.createFullTextQuery( query, Book.class ); fullTextQuery.setSort( new Sort( new SortField( "title", SortField.Type.STRING ) ) ); results = fullTextQuery.getResultList(); compareBookResultsAndExpected( Arrays.asList( "Dom Garcie de Navarre", "Le Grand Molière illustré" ), results ); } finally { transaction.commit(); } } private void compareCoffeeResultsAndExpected(List<String> expected, List<Coffee> results) { assertThat( results ).onProperty( "name" ).isEqualTo( expected ); } private void compareBookResultsAndExpected(List<String> expected, List<Book> results) { assertThat( results ).onProperty( "title" ).isEqualTo( expected ); } @Override public Class<?>[] getAnnotatedClasses() { return new Class<?>[] { Coffee.class, CoffeeBrand.class, Book.class }; } private QueryBuilder getCoffeeQueryBuilder() { return fullTextSession.getSearchFactory() .buildQueryBuilder() .forEntity( Coffee.class ) .get(); } private QueryBuilder getBookQueryBuilder() { return fullTextSession.getSearchFactory() .buildQueryBuilder() .forEntity( Book.class ) .get(); } private void indexTestData() { Transaction tx = fullTextSession.beginTransaction(); CoffeeBrand brandPony = new CoffeeBrand(); brandPony.setName( "My little pony" ); brandPony.setDescription( "Sells goods for horseback riding and good coffee blends" ); fullTextSession.persist( brandPony ); CoffeeBrand brandMonkey = new CoffeeBrand(); brandMonkey.setName( "Monkey Monkey Do" ); brandPony.setDescription( "Offers mover services via monkeys instead of trucks for difficult terrains. Coffees from this brand make monkeys work much faster." ); fullTextSession.persist( brandMonkey ); 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, brandMonkey ); 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, brandPony ); 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, brandMonkey ); createCoffee( "Arpeggio", "INTENSE AND CREAMY", "A dark roast of pure South and Central American Arabicas, Arpeggio has a strong character and intense body, enhanced by cocoa notes.", 9, brandPony ); createCoffee( "Roma", "FULL AND BALANCED", "The balance of lightly roasted South and Central American Arabicas with Robusta, gives Roma sweet and woody notes and a full, lasting taste on the palate.", 8, brandMonkey ); createCoffee( "Livanto", "ROUND AND BALANCED", "A pure Arabica from South and Central America, Livanto is a well-balanced espresso characterised by a roasted caramelised note.", 6, brandMonkey ); createCoffee( "Capriccio", "RICH AND DISTINCTIVE", "Blending South American Arabicas with a touch of Robusta, Capriccio is an espresso with a rich aroma and a strong typical cereal note.", 5, brandMonkey ); createCoffee( "Volluto", "SWEET AND LIGHT", "A pure and lightly roasted Arabica from South America, Volluto reveals sweet and biscuity flavours, reinforced by a little acidity and a fruity note.", 4, brandMonkey ); createCoffee( "Cosi", "LIGHT AND LEMONY", "Pure, lightly roasted East African, Central and South American Arabicas make Cosi a light-bodied espresso with refreshing citrus notes.", 3, brandMonkey ); createCoffee( "Indriya from India", "POWERFUL AND SPICY", "Indriya from India is the noble marriage of Arabicas with a hint of Robusta from southern India. It is a full-bodied espresso, which has a distinct personality with notes of spices.", 10, brandMonkey ); createCoffee( "Rosabaya de Colombia", "FRUITY AND BALANCED", "This blend of fine, individually roasted Colombian Arabicas, develops a subtle acidity with typical red fruit and winey notes.", 6, brandMonkey ); createCoffee( "Dulsão do Brasil", "SWEET AND SATINY SMOOTH", "A pure Arabica coffee, Dulsão do Brasil is a delicate blend of red and yellow Bourbon beans from Brazil. Its satiny smooth, elegantly balanced flavor is enhanced with a note of delicately toasted grain.", 4, brandMonkey ); createCoffee( "Bukeela ka Ethiopia", "", "This delicate Lungo expresses a floral bouquet reminiscent of jasmine, white lily, bergamot and orange blossom together with notes of wood. A pure Arabica blend composed of two very different coffees coming from the birthplace of coffee, Ethiopia. The blend’s coffees are roasted separately: one portion short and dark to guarantee the body, the other light but longer to preserve the delicate notes.", 3, brandMonkey ); createCoffee( "Fortissio Lungo", "RICH AND INTENSE", "Made from Central and South American Arabicas with just a hint of Robusta, Fortissio Lungo is an intense full-bodied blend with bitterness, which develops notes of dark roasted beans.", 7, brandMonkey ); createCoffee( "Vivalto Lungo", "COMPLEX AND BALANCED", "Vivalto Lungo is a balanced coffee made from a complex blend of separately roasted South American and East African Arabicas, combining roasted and subtle floral notes.", 4, brandMonkey ); createCoffee( "Linizio Lungo", "ROUND AND SMOOTH", "Mild and well-rounded on the palate, Linizio Lungo is a blend of fine Arabica enhancing malt and cereal notes.", 4, brandMonkey ); createCoffee( "Decaffeinato Intenso", "DENSE AND POWERFUL", "Dark roasted South American Arabicas with a touch of Robusta bring out the subtle cocoa and roasted cereal notes of this full-bodied decaffeinated espresso.", 7, brandMonkey ); createCoffee( "Decaffeinato Lungo", "LIGHT AND FULL-FLAVOURED", "The slow roasting of this blend of South American Arabicas with a touch of Robusta gives Decaffeinato Lungo a smooth, creamy body and roasted cereal flavour.", 3, brandMonkey ); createCoffee( "Decaffeinato", "FRUITY AND DELICATE", "A blend of South American Arabicas reinforced with just a touch of Robusta is lightly roasted to reveal an aroma of red fruit.", 2, brandPony ); createCoffee( "Caramelito", "CARAMEL FLAVOURED", "The sweet flavour of caramel softens the roasted notes of the Livanto Grand Cru. This delicate gourmet marriage evokes the creaminess of soft toffee.", 6, brandMonkey ); createCoffee( "Ciocattino", "CHOCOLATE FLAVOURED", "Dark and bitter chocolate notes meet the caramelized roast of the Livanto Grand Cru. A rich combination reminiscent of a square of dark chocolate.", 6, brandMonkey ); createCoffee( "Vanilio", "VANILLA FLAVOURED", "A balanced harmony between the rich and the velvety aromas of vanilla and the mellow flavour of the Livanto Grand Cru. A blend distinguished by its full flavour, infinitely smooth and silky on the palate.", 6, brandMonkey ); fullTextSession.persist( new Book( 1L, "Le chat qui regardait les étoiles", "Lilian Jackson Braun" ) ); fullTextSession.persist( new Book( 2L, "Le chat qui déplaçait des montagnes", "Lilian Jackson Braun" ) ); fullTextSession.persist( new Book( 3L, "Le Grand Molière illustré", "Caroline Guillot" ) ); fullTextSession.persist( new Book( 4L, "Tartuffe", "Molière" ) ); fullTextSession.persist( new Book( 5L, "Dom Garcie de Navarre", "moliere" ) ); // Molière all lowercase and without an accent tx.commit(); fullTextSession.clear(); } private void createCoffee(String title, String summary, String description, int intensity, CoffeeBrand brand) { Coffee coffee = new Coffee(); coffee.setName( title ); coffee.setSummary( summary ); coffee.setDescription( description ); coffee.setIntensity( intensity ); coffee.setBrand( brand ); fullTextSession.persist( coffee ); } }