/* * Hibernate, Relational Persistence for Idiomatic Java * * JBoss, Home of Professional Open Source * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public License, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package org.hibernate.hql.lucene.test; import static org.fest.assertions.Assertions.assertThat; import java.util.HashMap; import java.util.Map; import org.hibernate.hql.ParsingException; import org.hibernate.hql.QueryParser; import org.hibernate.hql.lucene.LuceneProcessingChain; import org.hibernate.hql.lucene.LuceneQueryParsingResult; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * Provides common tests and infrastructure for Lucene query parsing tests. * * @author Sanne Grinovero <sanne@hibernate.org> (C) 2012 Red Hat Inc. * @author Gunnar Morling */ public abstract class LuceneQueryParsingTestBase { private static boolean USE_STDOUT = true; @Rule public ExpectedException expectedException = ExpectedException.none(); private QueryParser queryParser; @Before public void setupParser() { queryParser = new QueryParser(); } @Test public void shouldRaiseExceptionDueToUnknownAlias() { expectedException.expect( ParsingException.class ); expectedException.expectMessage( "HQL100004" ); parseQuery( "from IndexedEntity e where a.name = 'same'" ); } @Test public void shouldCreateUnrestrictedQuery() { assertLuceneQuery( "from IndexedEntity", "*:*" ); } @Test public void shouldCreateRestrictedQueryUsingSelect() { assertLuceneQuery( "select e from IndexedEntity e where e.name = 'same' and not e.id = 5", "+name:same -id:5" ); } @Test public void shouldCreateProjectionQuery() { LuceneQueryParsingResult parsingResult = parseQuery( "select e.id, e.name from IndexedEntity e" ); assertThat( parsingResult.getQuery().toString() ).isEqualTo( "*:*" ); assertThat( parsingResult.getProjections() ).containsExactly( "id", "name" ); } @Test public void shouldCreateEmbeddedProjectionQuery() { LuceneQueryParsingResult parsingResult = parseQuery( "select e.author.name from IndexedEntity e" ); assertThat( parsingResult.getQuery().toString() ).isEqualTo( "*:*" ); assertThat( parsingResult.getProjections() ).containsExactly( "author.name" ); } @Test public void shouldCreateNestedEmbeddedProjectionQuery() { LuceneQueryParsingResult parsingResult = parseQuery( "select e.author.address.street from IndexedEntity e" ); assertThat( parsingResult.getQuery().toString() ).isEqualTo( "*:*" ); assertThat( parsingResult.getProjections() ).containsExactly( "author.address.street" ); } @Test public void shouldCreateQueryWithUnqualifiedPropertyReferences() { assertLuceneQuery( "from IndexedEntity e where name = 'same' and not id = 5", "+name:same -id:5" ); } @Test public void shouldCreateNegatedQuery() { assertLuceneQuery( "from IndexedEntity e where NOT e.name = 'same'", "-name:same #*:*" ); // JPQL syntax assertLuceneQuery( "from IndexedEntity e where e.name <> 'same'", "-name:same #*:*" ); // HQL syntax assertLuceneQuery( "from IndexedEntity e where e.name != 'same'", "-name:same #*:*" ); } @Test public void shouldCreateNegatedQueryOnNumericProperty() { assertLuceneQuery( "from IndexedEntity e where e.position <> 3", "-position:[3 TO 3] #*:*" ); } @Test public void shouldCreateNegatedRangeQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name = 'Bob' and not e.position between 1 and 3", "+name:Bob -position:[1 TO 3]" ); } @Test public void shouldCreateQueryWithNamedParameter() { Map<String, Object> namedParameters = new HashMap<String, Object>(); namedParameters.put( "nameParameter", "Bob" ); assertLuceneQuery( "from IndexedEntity e where e.name = :nameParameter", namedParameters, "name:Bob" ); } @Test public void shouldCreateBooleanQuery() { assertLuceneQuery( "from IndexedEntity e where e.name = 'same' or ( e.id = 4 and e.name = 'booh')", "name:same (+id:4 +name:booh)" ); } @Test public void shouldCreateBooleanQueryUsingSelect() { assertLuceneQuery( "select e from IndexedEntity e where e.name = 'same' or ( e.id = 4 and e.name = 'booh')", "name:same (+id:4 +name:booh)" ); } @Test public void shouldCreateBetweenQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name between 'aaa' and 'zzz'", "name:[aaa TO zzz]" ); } @Test public void shouldCreateNotBetweenQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name not between 'aaa' and 'zzz'", "-name:[aaa TO zzz] #*:*" ); } @Test public void shouldCreateBetweenQueryForCharacterLiterals() { assertLuceneQuery( "select e from IndexedEntity e where e.name between 'a' and 'z'", "name:[a TO z]" ); } @Test public void shouldCreateBetweenQueryWithNamedParameters() { Map<String, Object> namedParameters = new HashMap<String, Object>(); namedParameters.put( "lower", "aaa" ); namedParameters.put( "upper", "zzz" ); assertLuceneQuery( "select e from IndexedEntity e where e.name between :lower and :upper", namedParameters, "name:[aaa TO zzz]" ); } @Test public void shouldCreateNumericBetweenQuery() { Map<String, Object> namedParameters = new HashMap<String, Object>(); namedParameters.put( "lower", 10L ); namedParameters.put( "upper", 20L ); assertLuceneQuery( "select e from IndexedEntity e where e.position between :lower and :upper", namedParameters, "position:[10 TO 20]" ); } @Test public void shouldCreateQueryWithEmbeddedPropertyInFromClause() { assertLuceneQuery( "from IndexedEntity e where e.author.name = 'Bob'", "author.name:Bob" ); } @Test public void shouldCreateLessThanQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.position < 100", "position:[* TO 100}" ); } @Test public void shouldCreateLessThanOrEqualsToQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.position <= 100", "position:[* TO 100]" ); } @Test public void shouldCreateGreaterThanOrEqualsToQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.position >= 100", "position:[100 TO *]" ); } @Test public void shouldCreateGreaterThanQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.position > 100", "position:{100 TO *]" ); } @Test public void shouldCreateInQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name in ('Bob', 'Alice')", "name:Bob name:Alice" ); assertLuceneQuery( "select e from IndexedEntity e where e.position in (10, 20, 30, 40)", "position:[10 TO 10] position:[20 TO 20] position:[30 TO 30] position:[40 TO 40]" ); } @Test public void shouldCreateInQueryWithNamedParameters() { Map<String, Object> namedParameters = new HashMap<String, Object>(); namedParameters.put( "name1", "Bob" ); namedParameters.put( "name2", "Alice" ); assertLuceneQuery( "select e from IndexedEntity e where e.name in (:name1, :name2)", namedParameters, "name:Bob name:Alice" ); namedParameters = new HashMap<String, Object>(); namedParameters.put( "pos1", 10 ); namedParameters.put( "pos2", 20 ); namedParameters.put( "pos3", 30 ); namedParameters.put( "pos4", 40 ); assertLuceneQuery( "select e from IndexedEntity e where e.position in (:pos1, :pos2, :pos3, :pos4)", namedParameters, "position:[10 TO 10] position:[20 TO 20] position:[30 TO 30] position:[40 TO 40]" ); } @Test public void shouldCreateNotInQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name not in ('Bob', 'Alice')", "-(name:Bob name:Alice) #*:*" ); assertLuceneQuery( "select e from IndexedEntity e where e.position not in (10, 20, 30, 40)", "-(position:[10 TO 10] position:[20 TO 20] position:[30 TO 30] position:[40 TO 40]) #*:*" ); } @Test public void shouldCreateLikeQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name LIKE 'Al_ce'", "name:Al?ce" ); assertLuceneQuery( "select e from IndexedEntity e where e.name LIKE 'Ali%'", "name:Ali*" ); assertLuceneQuery( "select e from IndexedEntity e where e.name LIKE 'Ali%%'", "name:Ali**" ); assertLuceneQuery( "select e from IndexedEntity e where e.name LIKE '_l_ce'", "name:?l?ce" ); assertLuceneQuery( "select e from IndexedEntity e where e.name LIKE '___ce'", "name:???ce" ); assertLuceneQuery( "select e from IndexedEntity e where e.name LIKE '___ce'", "name:???ce" ); assertLuceneQuery( "select e from IndexedEntity e where e.name LIKE 'Alice in wonderl%'", "name:Alice in wonderl*" ); } @Test public void shouldCreateNotLikeQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name NOT LIKE 'Al_ce'", "-name:Al?ce #*:*" ); assertLuceneQuery( "select e from IndexedEntity e where e.name NOT LIKE 'Ali%'", "-name:Ali* #*:*" ); assertLuceneQuery( "select e from IndexedEntity e where e.name NOT LIKE '_l_ce' and not (e.title LIKE '%goo' and e.position = '5' )", "-name:?l?ce -(+title:*goo +position:[5 TO 5]) #*:*" ); } @Test public void shouldCreateIsNullQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name IS null", "name:_null_" ); } @Test public void shouldCreateIsNullQueryForEmbeddedEntity() { assertLuceneQuery( "select e from IndexedEntity e where e.author IS null", "author:_null_" ); } @Test public void shouldCreateIsNotNullQuery() { assertLuceneQuery( "select e from IndexedEntity e where e.name IS NOT null", "-name:_null_ #*:*" ); } @Test public void shouldCreateCollectionOfEmbeddedableQuery() { assertLuceneQuery( "select e from IndexedEntity e JOIN e.contactDetails d WHERE d.email = 'mym@il.it' ", "contactDetails.email:mym@il.it" ); } @Test public void shouldCreateCollectionOfEmbeddedableInEmbeddedQuery() { assertLuceneQuery( "SELECT e FROM IndexedEntity e " + " JOIN e.contactDetails d" + " JOIN d.address.alternatives as a " + "WHERE a.postCode = '28921' ", "contactDetails.address.alternatives.postCode:28921" ); } private void assertLuceneQuery(String queryString, String expectedLuceneQuery) { assertLuceneQuery( queryString, null, expectedLuceneQuery ); } private void assertLuceneQuery(String queryString, Map<String, Object> namedParameters, String expectedLuceneQuery) { LuceneQueryParsingResult parsingResult = parseQuery( queryString, namedParameters ); assertThat( parsingResult.getQuery().toString() ).isEqualTo( expectedLuceneQuery ); if ( USE_STDOUT ) { System.out.println( expectedLuceneQuery ); System.out.println(); } } protected LuceneQueryParsingResult parseQuery(String queryString) { return parseQuery( queryString, null ); } private LuceneQueryParsingResult parseQuery(String queryString, Map<String, Object> namedParameters) { if ( USE_STDOUT ) { System.out.println( queryString ); } return queryParser.parseQuery( queryString, setUpLuceneProcessingChain( namedParameters ) ); } protected abstract LuceneProcessingChain setUpLuceneProcessingChain(Map<String, Object> namedParameters); }