=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/InMemoryQueryEngine.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/InMemoryQueryEngine.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/InMemoryQueryEngine.java 2015-11-09 05:05:13 +0000 @@ -0,0 +1,107 @@ +package org.hisp.dhis.query; + +/* + * Copyright (c) 2004-2015, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.common.PagerUtils; +import org.hisp.dhis.schema.Property; +import org.hisp.dhis.schema.Schema; +import org.hisp.dhis.system.util.ReflectionUtils; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Morten Olav Hansen + */ +public class InMemoryQueryEngine implements QueryEngine +{ + @Override + public List query( Query query ) + { + validateQuery( query ); + List list = runQuery( query ); + + return PagerUtils.pageCollection( list, query.getFirstResult(), query.getMaxResults() ); + } + + @Override + public int count( Query query ) + { + validateQuery( query ); + List list = runQuery( query ); + + return list.size(); + } + + private void validateQuery( Query query ) + { + if ( query.getSchema() == null ) + { + throw new QueryException( "Invalid Query object, does not contain Schema" ); + } + + if ( query.getObjects() == null ) + { + throw new QueryException( "InMemoryQueryEngine requires an existing object list to work on." ); + } + } + + @SuppressWarnings( "unchecked" ) + private List runQuery( Query query ) + { + return query.getObjects().stream() + .filter( object -> test( query.getSchema(), (T) object, query.getCriterions() ) ) + .map( object -> (T) object ) + .collect( Collectors.toList() ); + } + + private boolean test( Schema schema, T object, List criterions ) + { + for ( Criterion criterion : criterions ) + { + Restriction restriction = (Restriction) criterion; + Object value = getValue( schema, object, restriction.getPath() ); + + if ( !restriction.getOperator().test( value ) ) + { + return false; + } + } + + return true; + } + + private Object getValue( Schema schema, Object object, String path ) + { + Property property = schema.getProperty( path ); + + return ReflectionUtils.invokeMethod( object, property.getGetterMethod() ); + } +} === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Query.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Query.java 2015-11-05 03:31:11 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/Query.java 2015-11-09 05:05:13 +0000 @@ -44,9 +44,11 @@ { private List orders = new ArrayList<>(); - private Integer firstResult; - - private Integer maxResults; + private Integer firstResult = 0; + + private Integer maxResults = Integer.MAX_VALUE; + + private Collection objects; public static Query from( Schema schema ) { @@ -90,6 +92,16 @@ return this; } + public Collection getObjects() + { + return objects; + } + + public void setObjects( Collection objects ) + { + this.objects = objects; + } + public Query addOrder( Order... orders ) { for ( Order order : orders ) === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml' --- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2015-11-05 08:40:46 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2015-11-09 05:05:13 +0000 @@ -25,6 +25,8 @@ + + === added file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/InMemoryQueryEngineTest.java' --- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/InMemoryQueryEngineTest.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/InMemoryQueryEngineTest.java 2015-11-09 05:05:13 +0000 @@ -0,0 +1,464 @@ +package org.hisp.dhis.query; + +/* + * Copyright (c) 2004-2015, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.google.common.collect.Lists; +import org.hisp.dhis.DhisSpringTest; +import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.common.ValueType; +import org.hisp.dhis.dataelement.DataElement; +import org.hisp.dhis.query.operators.MatchMode; +import org.hisp.dhis.schema.Schema; +import org.hisp.dhis.schema.SchemaService; +import org.jfree.data.time.Year; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @author Morten Olav Hansen + */ +public class InMemoryQueryEngineTest + extends DhisSpringTest +{ + @Autowired + private SchemaService schemaService; + + @Autowired + private InMemoryQueryEngine inMemoryQueryEngine; + + private Collection dataElements = new ArrayList<>(); + + @Before + public void createDataElements() + { + DataElement dataElementA = createDataElement( 'A' ); + dataElementA.setValueType( ValueType.NUMBER ); + + DataElement dataElementB = createDataElement( 'B' ); + dataElementB.setValueType( ValueType.BOOLEAN ); + + DataElement dataElementC = createDataElement( 'C' ); + dataElementC.setValueType( ValueType.INTEGER ); + + DataElement dataElementD = createDataElement( 'D' ); + dataElementD.setValueType( ValueType.NUMBER ); + + DataElement dataElementE = createDataElement( 'E' ); + dataElementE.setValueType( ValueType.BOOLEAN ); + + DataElement dataElementF = createDataElement( 'F' ); + dataElementF.setValueType( ValueType.INTEGER ); + + dataElementA.setCreated( Year.parseYear( "2001" ).getStart() ); + dataElementB.setCreated( Year.parseYear( "2002" ).getStart() ); + dataElementC.setCreated( Year.parseYear( "2003" ).getStart() ); + dataElementD.setCreated( Year.parseYear( "2004" ).getStart() ); + dataElementE.setCreated( Year.parseYear( "2005" ).getStart() ); + dataElementF.setCreated( Year.parseYear( "2006" ).getStart() ); + + dataElements.clear(); + dataElements.add( dataElementA ); + dataElements.add( dataElementB ); + dataElements.add( dataElementC ); + dataElements.add( dataElementD ); + dataElements.add( dataElementE ); + dataElements.add( dataElementF ); + } + + private boolean collectionContainsUid( Collection collection, String uid ) + { + for ( IdentifiableObject identifiableObject : collection ) + { + if ( identifiableObject.getUid().equals( uid ) ) + { + return true; + } + } + + return false; + } + + @Test + public void getAllQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + assertEquals( 6, inMemoryQueryEngine.query( query ).size() ); + } + + @Test + public void getMinMaxQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.setFirstResult( 2 ); + query.setMaxResults( 10 ); + + assertEquals( 4, inMemoryQueryEngine.query( query ).size() ); + + query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.setFirstResult( 2 ); + query.setMaxResults( 2 ); + + assertEquals( 2, inMemoryQueryEngine.query( query ).size() ); + } + + @Test + public void getEqQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.eq( "id", "deabcdefghA" ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 1, objects.size() ); + assertEquals( "deabcdefghA", objects.get( 0 ).getUid() ); + } + + @Test + public void getNeQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.ne( "id", "deabcdefghA" ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 5, objects.size() ); + + assertFalse( collectionContainsUid( objects, "deabcdefghA" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghB" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghC" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghE" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghF" ) ); + } + + @Test + @Ignore + public void getLikeQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.like( "name", "F", MatchMode.ANYWHERE ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 1, objects.size() ); + assertEquals( "deabcdefghF", objects.get( 0 ).getUid() ); + } + + @Test + public void getGtQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.gt( "created", Year.parseYear( "2003" ).getStart() ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 3, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghE" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghF" ) ); + } + + @Test + public void getLtQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.lt( "created", Year.parseYear( "2003" ).getStart() ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 2, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghA" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghB" ) ); + } + + @Test + public void getGeQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.ge( "created", Year.parseYear( "2003" ).getStart() ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 4, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghC" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghE" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghF" ) ); + } + + @Test + public void getLeQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.le( "created", Year.parseYear( "2003" ).getStart() ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 3, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghA" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghB" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghC" ) ); + } + + @Test + @Ignore + public void getBetweenQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.between( "created", Year.parseYear( "2003" ).getStart(), Year.parseYear( "2005" ).getStart() ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 3, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghC" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghE" ) ); + } + + @Test + public void getInQuery() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.in( "id", Lists.newArrayList( "deabcdefghD", "deabcdefghF" ) ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 2, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghF" ) ); + } + + @Test + @Ignore + public void sortNameDesc() + { + Schema schema = schemaService.getDynamicSchema( DataElement.class ); + + Query query = Query.from( schema ); + query.setObjects( dataElements ); + query.addOrder( new Order( schema.getProperty( "name" ), false ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 6, objects.size() ); + + assertEquals( "deabcdefghF", objects.get( 0 ).getUid() ); + assertEquals( "deabcdefghE", objects.get( 1 ).getUid() ); + assertEquals( "deabcdefghD", objects.get( 2 ).getUid() ); + assertEquals( "deabcdefghC", objects.get( 3 ).getUid() ); + assertEquals( "deabcdefghB", objects.get( 4 ).getUid() ); + assertEquals( "deabcdefghA", objects.get( 5 ).getUid() ); + } + + @Test + @Ignore + public void sortNameAsc() + { + Schema schema = schemaService.getDynamicSchema( DataElement.class ); + + Query query = Query.from( schema ); + query.setObjects( dataElements ); + query.addOrder( new Order( schema.getProperty( "name" ), true ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 6, objects.size() ); + + assertEquals( "deabcdefghA", objects.get( 0 ).getUid() ); + assertEquals( "deabcdefghB", objects.get( 1 ).getUid() ); + assertEquals( "deabcdefghC", objects.get( 2 ).getUid() ); + assertEquals( "deabcdefghD", objects.get( 3 ).getUid() ); + assertEquals( "deabcdefghE", objects.get( 4 ).getUid() ); + assertEquals( "deabcdefghF", objects.get( 5 ).getUid() ); + } + + @Test + @Ignore + public void sortCreatedDesc() + { + Schema schema = schemaService.getDynamicSchema( DataElement.class ); + + Query query = Query.from( schema ); + query.setObjects( dataElements ); + query.addOrder( new Order( schema.getProperty( "created" ), false ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 6, objects.size() ); + + assertEquals( "deabcdefghF", objects.get( 0 ).getUid() ); + assertEquals( "deabcdefghE", objects.get( 1 ).getUid() ); + assertEquals( "deabcdefghD", objects.get( 2 ).getUid() ); + assertEquals( "deabcdefghC", objects.get( 3 ).getUid() ); + assertEquals( "deabcdefghB", objects.get( 4 ).getUid() ); + assertEquals( "deabcdefghA", objects.get( 5 ).getUid() ); + } + + @Test + @Ignore + public void sortCreatedAsc() + { + Schema schema = schemaService.getDynamicSchema( DataElement.class ); + + Query query = Query.from( schema ); + query.setObjects( dataElements ); + query.addOrder( new Order( schema.getProperty( "created" ), true ) ); + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 6, objects.size() ); + + assertEquals( "deabcdefghA", objects.get( 0 ).getUid() ); + assertEquals( "deabcdefghB", objects.get( 1 ).getUid() ); + assertEquals( "deabcdefghC", objects.get( 2 ).getUid() ); + assertEquals( "deabcdefghD", objects.get( 3 ).getUid() ); + assertEquals( "deabcdefghE", objects.get( 4 ).getUid() ); + assertEquals( "deabcdefghF", objects.get( 5 ).getUid() ); + } + + @Test + @Ignore + public void testDoubleEqConjunction() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + + Conjunction conjunction = query.conjunction(); + conjunction.add( Restrictions.eq( "id", "deabcdefghD" ) ); + conjunction.add( Restrictions.eq( "id", "deabcdefghF" ) ); + query.add( conjunction ); + + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 0, objects.size() ); + } + + @Test + @Ignore + public void testDoubleEqDisjunction() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + + Disjunction disjunction = query.disjunction(); + disjunction.add( Restrictions.eq( "id", "deabcdefghD" ) ); + disjunction.add( Restrictions.eq( "id", "deabcdefghF" ) ); + query.add( disjunction ); + + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 2, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghF" ) ); + } + + @Test + public void testDateRange() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + + query.add( Restrictions.ge( "created", Year.parseYear( "2002" ).getStart() ) ); + query.add( Restrictions.le( "created", Year.parseYear( "2004" ).getStart() ) ); + + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 3, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghB" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghC" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + } + + @Test + @Ignore + public void testDateRangeWithConjunction() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + + Conjunction conjunction = query.conjunction(); + conjunction.add( Restrictions.ge( "created", Year.parseYear( "2002" ).getStart() ) ); + conjunction.add( Restrictions.le( "created", Year.parseYear( "2004" ).getStart() ) ); + query.add( conjunction ); + + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 3, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghB" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghC" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + } + + @Test + public void testIsNull() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.isNull( "categoryCombo" ) ); + + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 6, objects.size() ); + + assertTrue( collectionContainsUid( objects, "deabcdefghA" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghB" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghC" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghD" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghE" ) ); + assertTrue( collectionContainsUid( objects, "deabcdefghF" ) ); + } + + @Test + public void testIsNotNull() + { + Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) ); + query.setObjects( dataElements ); + query.add( Restrictions.isNotNull( "categoryCombo" ) ); + + List objects = inMemoryQueryEngine.query( query ); + + assertEquals( 0, objects.size() ); + } +}