| /* |
| * Copyright 2008 Alin Dreghiciu. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. |
| * |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.qi4j.runtime.query; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.qi4j.api.activation.ActivationException; |
| import org.qi4j.api.query.Query; |
| import org.qi4j.api.query.QueryBuilder; |
| import org.qi4j.api.query.QueryBuilderFactory; |
| import org.qi4j.api.query.QueryExpressions; |
| import org.qi4j.api.query.grammar.OrderBy; |
| import org.qi4j.api.unitofwork.UnitOfWork; |
| import org.qi4j.api.unitofwork.UnitOfWorkCompletionException; |
| import org.qi4j.bootstrap.AssemblyException; |
| import org.qi4j.bootstrap.ClassScanner; |
| import org.qi4j.bootstrap.ModuleAssembly; |
| import org.qi4j.bootstrap.SingletonAssembler; |
| import org.qi4j.runtime.query.model.City; |
| import org.qi4j.runtime.query.model.Describable; |
| import org.qi4j.runtime.query.model.Domain; |
| import org.qi4j.runtime.query.model.Female; |
| import org.qi4j.runtime.query.model.Male; |
| import org.qi4j.runtime.query.model.Nameable; |
| import org.qi4j.runtime.query.model.Person; |
| import org.qi4j.runtime.query.model.Pet; |
| import org.qi4j.runtime.query.model.entities.DomainEntity; |
| import org.qi4j.runtime.query.model.entities.PetEntity; |
| import org.qi4j.runtime.query.model.values.ContactValue; |
| import org.qi4j.runtime.query.model.values.ContactsValue; |
| import org.qi4j.spi.query.EntityFinderException; |
| import org.qi4j.test.EntityTestAssembler; |
| |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.qi4j.api.query.QueryExpressions.eq; |
| import static org.qi4j.api.query.QueryExpressions.ge; |
| import static org.qi4j.api.query.QueryExpressions.gt; |
| import static org.qi4j.api.query.QueryExpressions.isNotNull; |
| import static org.qi4j.api.query.QueryExpressions.isNull; |
| import static org.qi4j.api.query.QueryExpressions.lt; |
| import static org.qi4j.api.query.QueryExpressions.matches; |
| import static org.qi4j.api.query.QueryExpressions.not; |
| import static org.qi4j.api.query.QueryExpressions.or; |
| import static org.qi4j.api.query.QueryExpressions.orderBy; |
| import static org.qi4j.api.query.QueryExpressions.property; |
| import static org.qi4j.api.query.QueryExpressions.templateFor; |
| |
| public class IterableQuerySourceTest |
| { |
| |
| private UnitOfWork uow; |
| private QueryBuilderFactory qbf; |
| |
| @Before |
| public void setUp() |
| throws UnitOfWorkCompletionException, ActivationException, AssemblyException |
| { |
| SingletonAssembler assembler = new SingletonAssembler() |
| { |
| public void assemble( ModuleAssembly module ) |
| throws AssemblyException |
| { |
| Iterable<Class<?>> entities = ClassScanner.findClasses( DomainEntity.class ); |
| |
| for( Class entity : entities ) |
| { |
| module.entities( entity ); |
| } |
| |
| module.values( ContactsValue.class, ContactValue.class ); |
| new EntityTestAssembler().assemble( module ); |
| } |
| }; |
| uow = assembler.module().newUnitOfWork(); |
| Network.populate( uow, assembler.module() ); |
| uow.complete(); |
| uow = assembler.module().newUnitOfWork(); |
| Network.refresh( uow ); |
| qbf = assembler.module(); |
| } |
| |
| @After |
| public void tearDown() |
| { |
| if( uow != null ) |
| { |
| uow.discard(); |
| } |
| } |
| |
| private static void verifyUnorderedResults( final Iterable<? extends Nameable> results, |
| final String... names |
| ) |
| { |
| final List<String> expected = new ArrayList<String>( Arrays.asList( names ) ); |
| |
| for( Nameable entity : results ) |
| { |
| String name = entity.name().get(); |
| assertTrue( name + " returned but not expected", expected.remove( name ) ); |
| } |
| |
| for( String notReturned : expected ) |
| { |
| fail( notReturned + " was expected but not returned" ); |
| } |
| } |
| |
| private static void verifyOrderedResults( final Iterable<? extends Nameable> results, |
| final String... names |
| ) |
| { |
| final List<String> expected = new ArrayList<String>( Arrays.asList( names ) ); |
| |
| for( Nameable entity : results ) |
| { |
| String firstExpected = null; |
| if( expected.size() > 0 ) |
| { |
| firstExpected = expected.get( 0 ); |
| } |
| if( firstExpected == null ) |
| { |
| fail( entity.name().get() + " returned but not expected" ); |
| } |
| else if( !firstExpected.equals( entity.name().get() ) ) |
| { |
| fail( entity.name().get() + " is not in the expected order" ); |
| } |
| expected.remove( 0 ); |
| } |
| for( String notReturned : expected ) |
| { |
| fail( notReturned + " was expected but not returned" ); |
| } |
| } |
| |
| @Test |
| public void givenQueryWhenExecutedReturnAll() |
| throws EntityFinderException |
| { |
| final QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| final Query<Person> query = qb.newQuery( Network.persons() ); |
| System.out.println( query ); |
| verifyUnorderedResults( query, "Joe Doe", "Ann Doe", "Jack Doe", "Vivian Smith" ); |
| } |
| |
| @Test |
| public void givenEqQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Domain> qb = qbf.newQueryBuilder( Domain.class ); |
| final Nameable nameable = templateFor( Nameable.class ); |
| final Query<Domain> query = qb.where( |
| eq( nameable.name(), "Gaming" ) |
| ).newQuery( Network.domains() ); |
| verifyUnorderedResults( query, "Gaming" ); |
| } |
| |
| @Test |
| public void givenMixinTypeQueryWhenExecutedReturnAll() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| Query<Nameable> query = qb.newQuery( Network.nameables() ); |
| verifyUnorderedResults( |
| query, |
| "Joe Doe", "Ann Doe", "Jack Doe", "Vivian Smith", |
| "Penang", "Kuala Lumpur", |
| "Cooking", "Gaming", "Programming", "Cars" |
| ); |
| } |
| |
| @Test |
| public void givenEqQueryOnValueWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person personTemplate = templateFor( Person.class ); |
| City placeOfBirth = personTemplate.placeOfBirth().get(); |
| Query<Person> query = qb.where( |
| eq( placeOfBirth.name(), "Kuala Lumpur" ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Joe Doe", "Ann Doe", "Vivian Smith" ); |
| } |
| |
| @Test |
| public void givenEqQueryOnAssociationAndPropertyWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| eq( person.mother().get().placeOfBirth().get().name(), "Kuala Lumpur" ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Joe Doe" ); |
| } |
| |
| @Test |
| public void givenEqQueryOnAssociationWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| City kl = uow.get( City.class, "kualalumpur" ); |
| Query<Person> query = qb.where( |
| eq( person.mother().get().placeOfBirth(), kl ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Joe Doe" ); |
| } |
| |
| @Test |
| public void givenGeQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| ge( person.yearOfBirth(), 1973 ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Joe Doe", "Ann Doe", "Vivian Smith" ); |
| } |
| |
| @Test |
| public void givenAndQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| Person person = templateFor( Person.class ); |
| Query<Nameable> query = qb.where( |
| ge( person.yearOfBirth(), 1900 ).and( eq( person.placeOfBirth().get().name(), "Penang" ) ) |
| ).newQuery( Network.nameables() ); |
| verifyUnorderedResults( query, "Jack Doe" ); |
| } |
| |
| @Test |
| public void givenMultipleAndQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| Person person = templateFor( Person.class ); |
| Query<Nameable> query = qb.where( |
| ge( person.yearOfBirth(), 1900 ). |
| and( lt( person.yearOfBirth(), 2000 ) ). |
| and( eq( person.placeOfBirth().get().name(), "Penang" ) ) |
| ).newQuery( Network.nameables() ); |
| verifyUnorderedResults( query, "Jack Doe" ); |
| } |
| |
| @Test |
| public void givenOrQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| or( |
| eq( person.yearOfBirth(), 1970 ), |
| eq( person.yearOfBirth(), 1975 ) |
| ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Jack Doe", "Ann Doe" ); |
| } |
| |
| @Test |
| public void givenMultipleOrQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| or( |
| eq( person.yearOfBirth(), 1970 ), |
| eq( person.yearOfBirth(), 1975 ), |
| eq( person.yearOfBirth(), 1990 ) |
| ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Jack Doe", "Ann Doe", "Joe Doe" ); |
| } |
| |
| @Test |
| public void givenOrQueryOnFemalesWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Female> qb = qbf.newQueryBuilder( Female.class ); |
| Person person = templateFor( Person.class ); |
| Query<Female> query = qb.where( |
| or( |
| eq( person.yearOfBirth(), 1970 ), |
| eq( person.yearOfBirth(), 1975 ) |
| ) |
| ).newQuery( Network.females() ); |
| verifyUnorderedResults( query, "Ann Doe" ); |
| } |
| |
| @Test |
| public void givenNotQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| not( |
| eq( person.yearOfBirth(), 1975 ) |
| ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Jack Doe", "Joe Doe", "Vivian Smith" ); |
| } |
| |
| @Test |
| public void givenIsNotNullQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| isNotNull( person.email() ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Joe Doe", "Vivian Smith" ); |
| } |
| |
| @Test |
| public void givenIsNullQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| isNull( person.email() ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Ann Doe", "Jack Doe" ); |
| } |
| |
| @Test |
| public void givenIsNotNullOnMixinTypeWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Male person = templateFor( Male.class ); |
| Query<Person> query = qb.where( |
| isNotNull( person.wife() ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Jack Doe" ); |
| } |
| |
| @Test |
| public void givenIsNullOnMixinTypeWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Male> qb = qbf.newQueryBuilder( Male.class ); |
| Male person = templateFor( Male.class ); |
| Query<Male> query = qb.where( |
| isNull( person.wife() ) |
| ).newQuery( Network.males() ); |
| verifyUnorderedResults( query, "Joe Doe" ); |
| } |
| |
| @Test |
| public void givenIsNullOnAssociationWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Male person = templateFor( Male.class ); |
| Query<Person> query = qb.where( |
| isNull( person.wife() ) |
| ).newQuery( Network.persons() ); |
| verifyUnorderedResults( query, "Joe Doe", "Ann Doe", "Vivian Smith" ); |
| } |
| |
| @Test |
| public void givenOrderAndMaxResultsQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| // should return only 2 entities |
| Nameable nameable = templateFor( Nameable.class ); |
| Query<Nameable> query = qb.newQuery( Network.nameables() ); |
| query.orderBy( orderBy( nameable.name() ) ); |
| query.maxResults( 2 ); |
| verifyOrderedResults( |
| query, |
| "Ann Doe", "Cars" |
| ); |
| } |
| |
| @Test |
| public void givenOrderAndFirstAndMaxResultsQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| // should return only 3 entities starting with forth one |
| Nameable nameable = templateFor( Nameable.class ); |
| Query<Nameable> query = qb.newQuery( Network.nameables() ); |
| query.orderBy( orderBy( nameable.name() ) ); |
| query.firstResult( 3 ); |
| query.maxResults( 3 ); |
| verifyOrderedResults( |
| query, |
| "Gaming", "Jack Doe", "Joe Doe" |
| ); |
| } |
| |
| @Test |
| public void givenOrderByOnMixinTypeQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| // should return all Nameable entities sorted by name |
| Nameable nameable = templateFor( Nameable.class ); |
| Query<Nameable> query = qb.newQuery( Network.nameables() ); |
| query.orderBy( orderBy( nameable.name() ) ); |
| verifyOrderedResults( |
| query, |
| "Ann Doe", "Cars", "Cooking", "Gaming", "Jack Doe", "Joe Doe", "Kuala Lumpur", "Penang", "Programming", "Vivian Smith" |
| ); |
| } |
| |
| @Test |
| public void givenGtAndOrderByQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| // should return all Nameable entities with a name > "D" sorted by name |
| Nameable nameable = templateFor( Nameable.class ); |
| Query<Nameable> query = qb.where( |
| gt( nameable.name(), "D" ) |
| ).newQuery( Network.nameables() ); |
| query.orderBy( orderBy( nameable.name() ) ); |
| verifyOrderedResults( |
| query, |
| "Gaming", "Jack Doe", "Joe Doe", "Kuala Lumpur", "Penang", "Programming", "Vivian Smith" |
| ); |
| } |
| |
| @Test |
| public void givenGtAndOrderByDescendingQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| // should return all Persons born after 1973 (Ann and Joe Doe) sorted descending by name |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.where( |
| gt( person.yearOfBirth(), 1973 ) |
| ).newQuery( Network.persons() ); |
| query.orderBy( orderBy( person.name(), OrderBy.Order.DESCENDING ) ); |
| verifyOrderedResults( query, "Vivian Smith", "Joe Doe", "Ann Doe" ); |
| } |
| |
| @Test |
| public void givenOrderByMultipleQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| // should return all Persons sorted by name of the city they were born, and then by year they were born |
| Person person = templateFor( Person.class ); |
| Query<Person> query = qb.newQuery( Network.persons() ); |
| query.orderBy( orderBy( person.placeOfBirth().get().name() ), |
| orderBy( person.yearOfBirth() ) ); |
| verifyOrderedResults( query, "Ann Doe", "Joe Doe", "Vivian Smith", "Jack Doe" ); |
| } |
| |
| @Test |
| public void givenMatchesQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Nameable> qb = qbf.newQueryBuilder( Nameable.class ); |
| Nameable nameable = templateFor( Nameable.class ); |
| // should return Jack and Joe Doe |
| Query<Nameable> query = qb.where( |
| matches( nameable.name(), "J.*Doe" ) |
| ).newQuery( Network.nameables() ); |
| verifyUnorderedResults( |
| query, |
| "Jack Doe", "Joe Doe" |
| ); |
| } |
| |
| // TODO solve ManyAssociation filtering for iterables |
| // @Test |
| public void givenOneOfQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Domain interests = person.interests().get( 0 ); |
| Query<Person> query = qb.where( eq( interests.name(), "Cars" ) ).newQuery( Network.persons() ); |
| verifyOrderedResults( query, "Jack Doe" ); |
| } |
| |
| @Test |
| public void givenManyAssociationContainsQueryWhenExecutedThenReturnCorrect() |
| throws EntityFinderException |
| { |
| QueryBuilder<Person> qb = qbf.newQueryBuilder( Person.class ); |
| Person person = templateFor( Person.class ); |
| Domain value = Network.domains().iterator().next(); |
| Query<Person> query = qb.where( QueryExpressions.contains( person.interests(), value ) ) |
| .newQuery( Network.persons() ); |
| for( Person person1 : query ) |
| { |
| System.out.println( person1.name() ); |
| } |
| verifyOrderedResults( query, "Joe Doe", "Vivian Smith" ); |
| } |
| |
| @Test |
| public void givenEntitiesWithInternalStateWhenQueriedThenReturnCorrect() |
| { |
| QueryBuilder<PetEntity> qb = qbf.newQueryBuilder( PetEntity.class ); |
| Pet.PetState pet = templateFor( Pet.PetState.class ); |
| Nameable petOwner = templateFor( Nameable.class, pet.owner() ); |
| Query<PetEntity> query = qb.where( eq( petOwner.name(), "Jack Doe" ) ).newQuery( Network.pets() ); |
| verifyOrderedResults( query, "Rex" ); |
| } |
| |
| @Test |
| public void givenEntitiesWithFieldPropertyByNameWhenQueriedThenReturnCorrect() |
| { |
| QueryBuilder<PetEntity> qb = qbf.newQueryBuilder( PetEntity.class ); |
| Query<PetEntity> query = qb.where( eq( property( Describable.Mixin.class, "description" ), "Rex is a great dog" ) ) |
| .newQuery( Network.pets() ); |
| verifyOrderedResults( query, "Rex" ); |
| } |
| |
| @Test |
| public void givenEntitiesWithFieldPropertyWhenQueriedThenReturnCorrect() |
| { |
| QueryBuilder<PetEntity> qb = qbf.newQueryBuilder( PetEntity.class ); |
| Query<PetEntity> query = qb.where( eq( templateFor( Describable.Mixin.class ).description, "Rex is a great dog" ) ) |
| .newQuery( Network.pets() ); |
| verifyOrderedResults( query, "Rex" ); |
| } |
| } |