package org.infinispan.query.dsl.embedded.impl;
import static org.testng.AssertJUnit.assertEquals;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.TimeZone;
import org.hibernate.search.exception.SearchException;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.Index;
import org.infinispan.objectfilter.ParsingException;
import org.infinispan.objectfilter.impl.syntax.parser.IckleParsingResult;
import org.infinispan.objectfilter.impl.syntax.parser.IckleParser;
import org.infinispan.query.CacheQuery;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.embedded.impl.model.TheEntity;
import org.infinispan.query.dsl.embedded.testdomain.Account;
import org.infinispan.query.dsl.embedded.testdomain.Address;
import org.infinispan.query.dsl.embedded.testdomain.Author;
import org.infinispan.query.dsl.embedded.testdomain.Book;
import org.infinispan.query.dsl.embedded.testdomain.NotIndexed;
import org.infinispan.query.dsl.embedded.testdomain.Transaction;
import org.infinispan.query.dsl.embedded.testdomain.User;
import org.infinispan.query.dsl.embedded.testdomain.hsearch.AccountHS;
import org.infinispan.query.dsl.embedded.testdomain.hsearch.AddressHS;
import org.infinispan.query.dsl.embedded.testdomain.hsearch.TransactionHS;
import org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.fwk.CleanupAfterTest;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.TransactionMode;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* @author anistor@redhat.com
* @since 8.0
*/
@Test(groups = "functional", testName = "query.dsl.embedded.impl.EmbeddedQueryEngineTest")
@CleanupAfterTest
public class EmbeddedQueryEngineTest extends MultipleCacheManagersTest {
private final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
private QueryEngine<Class<?>> qe;
public EmbeddedQueryEngineTest() {
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
}
private Date makeDate(String dateStr) throws ParseException {
return DATE_FORMAT.parse(dateStr);
}
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder cfg = TestCacheManagerFactory.getDefaultCacheConfiguration(true);
cfg.transaction()
.transactionMode(TransactionMode.TRANSACTIONAL)
.indexing().index(Index.ALL)
.addIndexedEntity(UserHS.class)
.addIndexedEntity(AccountHS.class)
.addIndexedEntity(TransactionHS.class)
.addIndexedEntity(TheEntity.class)
.addIndexedEntity(Book.class)
.addProperty("default.directory_provider", "ram")
.addProperty("lucene_version", "LUCENE_CURRENT");
createClusteredCaches(1, cfg);
}
@BeforeClass(alwaysRun = true)
protected void init() throws Exception {
qe = new EmbeddedQueryEngine(cache(0).getAdvancedCache(), true);
// create the test objects
User user1 = new UserHS();
user1.setId(1);
user1.setName("John");
user1.setSurname("Doe");
user1.setGender(User.Gender.MALE);
user1.setAge(22);
user1.setAccountIds(new HashSet<>(Arrays.asList(1, 2)));
user1.setNotes("Lorem ipsum dolor sit amet");
Address address1 = new AddressHS();
address1.setStreet("Main Street");
address1.setPostCode("X1234");
user1.setAddresses(Collections.singletonList(address1));
User user2 = new UserHS();
user2.setId(2);
user2.setName("Spider");
user2.setSurname("Man");
user2.setGender(User.Gender.MALE);
user2.setAge(44);
user2.setAccountIds(Collections.singleton(3));
Address address2 = new AddressHS();
address2.setStreet("Old Street");
address2.setPostCode("Y12");
Address address3 = new AddressHS();
address3.setStreet("Bond Street");
address3.setPostCode("ZZ");
user2.setAddresses(Arrays.asList(address2, address3));
User user3 = new UserHS();
user3.setId(3);
user3.setName("Spider");
user3.setSurname("Woman");
user3.setGender(User.Gender.FEMALE);
user3.setAccountIds(Collections.emptySet());
Account account1 = new AccountHS();
account1.setId(1);
account1.setDescription("John Doe's first bank account");
account1.setCreationDate(makeDate("2013-01-03"));
Account account2 = new AccountHS();
account2.setId(2);
account2.setDescription("John Doe's second bank account");
account2.setCreationDate(makeDate("2013-01-04"));
Account account3 = new AccountHS();
account3.setId(3);
account3.setCreationDate(makeDate("2013-01-20"));
Transaction transaction0 = new TransactionHS();
transaction0.setId(0);
transaction0.setDescription("Birthday present");
transaction0.setAccountId(1);
transaction0.setAmount(1800);
transaction0.setDate(makeDate("2012-09-07"));
transaction0.setDebit(false);
transaction0.setValid(true);
Transaction transaction1 = new TransactionHS();
transaction1.setId(1);
transaction1.setDescription("Feb. rent payment");
transaction1.setAccountId(1);
transaction1.setAmount(1500);
transaction1.setDate(makeDate("2013-01-05"));
transaction1.setDebit(true);
transaction1.setValid(true);
Transaction transaction2 = new TransactionHS();
transaction2.setId(2);
transaction2.setDescription("Starbucks");
transaction2.setAccountId(1);
transaction2.setAmount(23);
transaction2.setDate(makeDate("2013-01-09"));
transaction2.setDebit(true);
transaction2.setValid(true);
Transaction transaction3 = new TransactionHS();
transaction3.setId(3);
transaction3.setDescription("Hotel");
transaction3.setAccountId(2);
transaction3.setAmount(45);
transaction3.setDate(makeDate("2013-02-27"));
transaction3.setDebit(true);
transaction3.setValid(true);
Transaction transaction4 = new TransactionHS();
transaction4.setId(4);
transaction4.setDescription("Last january");
transaction4.setAccountId(2);
transaction4.setAmount(95);
transaction4.setDate(makeDate("2013-01-31"));
transaction4.setDebit(true);
transaction4.setValid(true);
Transaction transaction5 = new TransactionHS();
transaction5.setId(5);
transaction5.setDescription("Popcorn");
transaction5.setAccountId(2);
transaction5.setAmount(5);
transaction5.setDate(makeDate("2013-01-01"));
transaction5.setDebit(true);
transaction5.setValid(true);
// persist and index the test objects
// we put all of them in the same cache for the sake of simplicity
cache(0).put("user_" + user1.getId(), user1);
cache(0).put("user_" + user2.getId(), user2);
cache(0).put("user_" + user3.getId(), user3);
cache(0).put("account_" + account1.getId(), account1);
cache(0).put("account_" + account2.getId(), account2);
cache(0).put("account_" + account3.getId(), account3);
cache(0).put("transaction_" + transaction0.getId(), transaction0);
cache(0).put("transaction_" + transaction1.getId(), transaction1);
cache(0).put("transaction_" + transaction2.getId(), transaction2);
cache(0).put("transaction_" + transaction3.getId(), transaction3);
cache(0).put("transaction_" + transaction4.getId(), transaction4);
cache(0).put("transaction_" + transaction5.getId(), transaction5);
for (int i = 0; i < 50; i++) {
Transaction transaction = new TransactionHS();
transaction.setId(50 + i);
transaction.setDescription("Expensive shoes " + i);
transaction.setAccountId(2);
transaction.setAmount(100 + i);
transaction.setDate(makeDate("2013-08-20"));
transaction.setDebit(true);
transaction.setValid(true);
cache(0).put("transaction_" + transaction.getId(), transaction);
}
// this value should be ignored gracefully
cache(0).put("dummy", "a primitive value cannot be queried");
cache(0).put("notIndexed1", new NotIndexed("testing 123"));
cache(0).put("notIndexed2", new NotIndexed("xyz"));
cache(0).put("entity1", new TheEntity("test value 1", new TheEntity.TheEmbeddedEntity("test embedded value 1")));
cache(0).put("entity2", new TheEntity("test value 2", new TheEntity.TheEmbeddedEntity("test embedded value 2")));
cache(0).put("book1", new Book("Java Performance: The Definitive Guide", "O'Reilly Media", new Author("Scott", "Oaks"),
"Still, it turns out that every day, I think about GC performance, or the\n" +
"performance of the JVM compiler, or how to get the best performance from Java Enterprise Edition APIs."));
cache(0).put("book2", new Book("Functional Programming for Java Developers", "O'Reilly Media", new Author("Dean", "Wampler"),
"Why should a Java developer learn about functional programming (FP)? After all, hasn’t\n" +
"functional programming been safely hidden in academia for decades? Isn’t object-\n" +
"oriented programming (OOP) all we really need?"));
cache(0).put("book3", new Book("The Java ® Virtual Machine Specification Java SE 8 Edition", "Oracle", new Author("Tim", "Lindholm"),
"The Java SE 8 Edition of The Java Virtual Machine Specification incorporates all the changes that have " +
"been made to the Java Virtual Machine since the Java SE 7 Edition in 2011. In addition, numerous " +
"corrections and clarifications have been made to align with popular implementations of the Java Virtual Machine."));
}
@Override
protected void clearContent() {
// Don't clear, this is destroying the index
}
private Query buildQuery(String queryString) {
return qe.buildQuery(null, qe.parse(queryString), null, -1, -1);
}
public void testGrouping() {
Query q = buildQuery("select name from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS " +
"where surname is not null group by name having name >= 'A'");
List<User> list = q.list();
assertEquals(2, list.size());
}
public void testNoGroupingOrAggregation() {
Query q = buildQuery("from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<User> list = q.list();
assertEquals(3, list.size());
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN028516: Cannot have aggregate functions in the GROUP BY clause : SUM.")
public void testDisallowAggregationInGroupBy() {
Query q = buildQuery("select sum(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by sum(age) ");
q.list();
}
public void testDuplicatesAcceptedInGroupBy() {
Query q = buildQuery("select name from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by name, name");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals(1, list.get(1).length);
}
public void testDuplicatesAcceptedInSelect1() {
Query q = buildQuery("select name, name from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by name");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(2, list.get(0).length);
assertEquals(2, list.get(1).length);
}
public void testDuplicatesAcceptedInSelect2() {
Query q = buildQuery("select max(name), max(name) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<Object[]> list = q.list();
assertEquals(1, list.size());
assertEquals(2, list.get(0).length);
}
public void testDuplicatesAcceptedInSelect3() {
Query q = buildQuery("select min(name), max(name) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<Object[]> list = q.list();
assertEquals(1, list.size());
assertEquals(2, list.get(0).length);
}
public void testDuplicatesAcceptedInOrderBy1() {
Query q = buildQuery("from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS order by age, age");
List<User> list = q.list();
assertEquals(3, list.size());
}
public void testDuplicatesAcceptedInOrderBy2() {
Query q = buildQuery("from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS order by age, name, age");
List<User> list = q.list();
assertEquals(3, list.size());
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN014024: The property path 'addresses.postCode' cannot be used in the ORDER BY clause because it is multi-valued")
public void testRejectMultivaluedOrderBy() {
Query q = buildQuery("from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS u order by u.addresses.postCode");
q.list();
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN014023: Using the multi-valued property path 'addresses.postCode' in the GROUP BY clause is not currently supported")
public void testRejectMultivaluedGroupBy() {
Query q = buildQuery("select u.addresses.postCode from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS u group by u.addresses.postCode");
q.list();
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN014026: The expression 'age' must be part of an aggregate function or it should be included in the GROUP BY clause")
public void testMissingAggregateInSelect() {
Query q = buildQuery("select age from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by name");
q.list();
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN014026: The expression 'age' must be part of an aggregate function or it should be included in the GROUP BY clause")
public void testMissingAggregateInOrderBy() {
Query q = buildQuery("select name, sum(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by name order by age");
q.list();
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN028515: Cannot have aggregate functions in the WHERE clause : SUM.")
public void testDisallowAggregatesInWhereClause() {
Query q = buildQuery("select name from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS where sum(age) > 33 group by name");
q.list();
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN014026: The expression 'age' must be part of an aggregate function or it should be included in the GROUP BY clause")
public void testHavingClauseAllowsAggregationsAndGroupByColumnsOnly() {
Query q = buildQuery("select name from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by name having age >= 18");
q.list();
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN014026: The expression 'name' must be part of an aggregate function or it should be included in the GROUP BY clause")
public void testDisallowNonAggregatedProjectionWithGlobalAggregation() {
Query q = buildQuery("select name, count(name) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
q.list();
}
public void testBuildLuceneQuery() {
IckleParsingResult<Class<?>> parsingResult = IckleParser.parse("select name from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS", qe.propertyHelper);
CacheQuery<UserHS> q = qe.buildLuceneQuery(parsingResult, null, -1, -1);
List<?> list = q.list();
assertEquals(3, list.size());
}
@Test(expectedExceptions = SearchException.class, expectedExceptionsMessageRegExp = "Unable to find field notes in org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS")
public void testBuildLuceneQueryOnNonIndexedField() {
IckleParsingResult<Class<?>> parsingResult = IckleParser.parse("select notes from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS where notes like 'TBD%'", qe.propertyHelper);
CacheQuery<?> q = qe.buildLuceneQuery(parsingResult, null, -1, -1);
}
public void testGlobalCount() {
Query q = buildQuery("select count(name), count(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<Object[]> list = q.list();
assertEquals(1, list.size());
assertEquals(2, list.get(0).length);
assertEquals(3L, list.get(0)[0]);
assertEquals(2L, list.get(0)[1]);
}
public void testGlobalAvg() {
Query q = buildQuery("select avg(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<Object[]> list = q.list();
assertEquals(1, list.size());
assertEquals(1, list.get(0).length);
assertEquals(33.0d, list.get(0)[0]);
}
public void testGlobalSum() {
Query q = buildQuery("select sum(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<Object[]> list = q.list();
assertEquals(1, list.size());
assertEquals(1, list.get(0).length);
assertEquals(66L, list.get(0)[0]);
}
public void testGlobalMin() {
Query q = buildQuery("select min(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<Object[]> list = q.list();
assertEquals(1, list.size());
assertEquals(1, list.get(0).length);
assertEquals(22, list.get(0)[0]);
}
public void testGlobalMax() {
Query q = buildQuery("select max(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
List<Object[]> list = q.list();
assertEquals(1, list.size());
assertEquals(1, list.get(0).length);
assertEquals(44, list.get(0)[0]);
}
public void testAggregateGroupingField() {
Query q = buildQuery("select count(name) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by name order by count(name)");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals(1L, list.get(0)[0]);
assertEquals(1, list.get(1).length);
assertEquals(2L, list.get(1)[0]);
}
public void testAggregateEmbedded1() {
Query q = buildQuery("select max(accountIds) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS group by name order by name");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals(2, list.get(0)[0]);
assertEquals(1, list.get(1).length);
assertEquals(3, list.get(1)[0]);
}
public void testAggregateEmbedded2() {
Query q = buildQuery("select max(u.addresses.postCode) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS u group by u.name order by u.name");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals("X1234", list.get(0)[0]);
assertEquals(1, list.get(1).length);
assertEquals("ZZ", list.get(1)[0]);
}
@Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "Aggregation SUM cannot be applied to property of type java.lang.String")
public void testIncompatibleAggregator() {
Query q = buildQuery("select sum(name) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS");
q.list();
}
public void testAggregateNulls() {
Query q = buildQuery("select name, sum(age), avg(age) from org.infinispan.query.dsl.embedded.testdomain.hsearch.UserHS " +
"where surname is not null " +
"group by name " +
"having name >= 'A' and count(age) >= 1");
List<User> list = q.list();
assertEquals(2, list.size());
}
public void testRenamedFields1() {
Query q = buildQuery("select theField from org.infinispan.query.dsl.embedded.impl.model.TheEntity where theField >= 'a' order by theField");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals("test value 1", list.get(0)[0]);
assertEquals("test value 2", list.get(1)[0]);
}
public void testRenamedFields2() {
Query q = buildQuery("select theField from org.infinispan.query.dsl.embedded.impl.model.TheEntity order by theField");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals("test value 1", list.get(0)[0]);
assertEquals("test value 2", list.get(1)[0]);
}
public void testRenamedFields3() {
Query q = buildQuery("select e.embeddedEntity.anotherField from org.infinispan.query.dsl.embedded.impl.model.TheEntity e where e.embeddedEntity.anotherField >= 'a' order by e.theField");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals("test embedded value 1", list.get(0)[0]);
assertEquals("test embedded value 2", list.get(1)[0]);
}
public void testRenamedFields4() {
Query q = buildQuery("select e.embeddedEntity.anotherField from org.infinispan.query.dsl.embedded.impl.model.TheEntity e order by e.theField");
List<Object[]> list = q.list();
assertEquals(2, list.size());
assertEquals(1, list.get(0).length);
assertEquals("test embedded value 1", list.get(0)[0]);
assertEquals("test embedded value 2", list.get(1)[0]);
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN028507: Invalid boolean literal '90'")
public void testInvalidNotIndexedBooleanComparison() {
buildQuery("from org.infinispan.query.dsl.embedded.testdomain.hsearch.TransactionHS where isValid = 90");
}
@Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "ISPN014037: Invalid boolean literal '90'")
public void testInvalidIndexedBooleanComparison() {
buildQuery("from org.infinispan.query.dsl.embedded.testdomain.hsearch.TransactionHS where isDebit = 90");
}
public void testBooleanComparison() {
IckleParsingResult<Class<?>> parsingResult = IckleParser.parse("from org.infinispan.query.dsl.embedded.testdomain.hsearch.TransactionHS " +
"WHERE isDebit = false", qe.propertyHelper);
CacheQuery q = qe.buildLuceneQuery(parsingResult, null, -1, -1);
List<?> list = q.list();
assertEquals(1, list.size());
}
public void testConstantBooleanExpression() {
IckleParsingResult<Class<?>> parsingResult = IckleParser.parse("from org.infinispan.query.dsl.embedded.testdomain.hsearch.TransactionHS " +
"WHERE true", qe.propertyHelper);
CacheQuery q = qe.buildLuceneQuery(parsingResult, null, -1, -1);
List<?> list = q.list();
assertEquals(56, list.size());
parsingResult = IckleParser.parse("from org.infinispan.query.dsl.embedded.testdomain.hsearch.TransactionHS " +
"WHERE false", qe.propertyHelper);
q = qe.buildLuceneQuery(parsingResult, null, -1, -1);
list = q.list();
assertEquals(0, list.size());
}
public void testFullTextKeyword() {
IckleParsingResult<Class<?>> parsingResult = IckleParser.parse("from org.infinispan.query.dsl.embedded.testdomain.Book b " +
"where b.preface:('java se'^7 -('bicycle' 'ski')) and b.publisher:'Oracel'~2", qe.propertyHelper);
CacheQuery q = qe.buildLuceneQuery(parsingResult, null, -1, -1);
List<?> list = q.list();
assertEquals(1, list.size());
}
}