← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 18311: wip, Implement QueryService for optimizing Schema based (validated) queries against DB, adds inte...

 

------------------------------------------------------------
revno: 18311
committer: Morten Olav Hansen <mortenoh@xxxxxxxxx>
branch nick: dhis2
timestamp: Wed 2015-02-18 20:08:37 +0700
message:
  wip, Implement QueryService for optimizing Schema based (validated) queries against DB, adds interface QueryEngine with CriteriaQueryEngine implementation, uses sharing criteria as base criteria, support property alias (id => uid) etc. Also adds ResulTransformer, used to further customize query result (will be used for Java filtering and field filtering)
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/MutableResult.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Operator.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Query.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryEngine.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryException.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryService.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restriction.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/RestrictionException.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restrictions.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Result.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/ResultTransformer.java
  dhis-2/dhis-api/src/test/java/org/hisp/dhis/query/
  dhis-2/dhis-api/src/test/java/org/hisp/dhis/query/QueryTest.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/CriteriaQueryEngine.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/
  dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/QueryServiceTest.java
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/Schema.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/MutableResult.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/MutableResult.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/MutableResult.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,49 @@
+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 java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class MutableResult extends Result
+{
+    public MutableResult( List<? extends IdentifiableObject> items )
+    {
+        super( items );
+    }
+
+    public void setItems( List<? extends IdentifiableObject> items )
+    {
+        this.items = items;
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Operator.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Operator.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Operator.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,78 @@
+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.
+ */
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public enum Operator
+{
+    // TODO should probably introduce typed parameters here, but for now we just use a simple integer
+    EQ( 1 ),
+    NE( 1 ),
+    GT( 1 ),
+    LT( 1 ),
+    GE( 1 ),
+    LE( 1 ),
+    BETWEEN( 2 ),
+    LIKE( 1 ),
+    IN( 1, Integer.MAX_VALUE );
+
+    Integer min;
+
+    Integer max;
+
+    Operator()
+    {
+        this.min = null;
+        this.max = null;
+    }
+
+    Operator( int value )
+    {
+        this.min = value;
+        this.max = value;
+    }
+
+    Operator( int min, int max )
+    {
+        this.min = min;
+        this.max = max;
+    }
+
+    public Integer getMin()
+    {
+        return min;
+    }
+
+    public Integer getMax()
+    {
+        return max;
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Query.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Query.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Query.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,159 @@
+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.base.MoreObjects;
+import org.hisp.dhis.schema.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class Query
+{
+    private final Schema schema;
+
+    private List<Order> orders = new ArrayList<>();
+
+    private List<Restriction> restrictions = new ArrayList<>();
+
+    private Integer firstResult;
+
+    private Integer maxResults;
+
+    public static Query from( Schema schema )
+    {
+        return new Query( schema );
+    }
+
+    private Query( Schema schema )
+    {
+        this.schema = schema;
+    }
+
+    public Schema getSchema()
+    {
+        return schema;
+    }
+
+    public List<Order> getOrders()
+    {
+        return orders;
+    }
+
+    public void setOrders( List<Order> orders )
+    {
+        this.orders = orders;
+    }
+
+    public List<Restriction> getRestrictions()
+    {
+        return restrictions;
+    }
+
+    public void setRestrictions( List<Restriction> restrictions )
+    {
+        this.restrictions = restrictions;
+    }
+
+    public Integer getFirstResult()
+    {
+        return firstResult;
+    }
+
+    public void setFirstResult( Integer firstResult )
+    {
+        this.firstResult = firstResult;
+    }
+
+    public Integer getMaxResults()
+    {
+        return maxResults;
+    }
+
+    public void setMaxResults( Integer maxResults )
+    {
+        this.maxResults = maxResults;
+    }
+
+    // Builder
+    public Query add( Restriction... restrictions )
+    {
+        for ( Restriction restriction : restrictions )
+        {
+            if ( restriction == null || !schema.haveProperty( restriction.getPath() ) )
+            {
+                continue;
+            }
+
+            if ( restriction.getParameters().size() > restriction.getOperator().getMax()
+                || restriction.getParameters().size() < restriction.getOperator().getMin() )
+            {
+                continue;
+            }
+
+            this.restrictions.add( restriction );
+        }
+
+        return this;
+    }
+
+    public Query addOrder( Order... orders )
+    {
+        for ( Order order : orders )
+        {
+            if ( order != null )
+            {
+                this.orders.add( order );
+            }
+        }
+
+        return this;
+    }
+
+    public Query addOrders( List<Order> orders )
+    {
+        this.orders.addAll( orders );
+        return this;
+    }
+
+
+    @Override
+    public String toString()
+    {
+        return MoreObjects.toStringHelper( this )
+            .add( "firstResult", firstResult )
+            .add( "maxResults", maxResults )
+            .add( "orders", orders )
+            .add( "restrictions", restrictions )
+            .toString();
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryEngine.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryEngine.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryEngine.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,41 @@
+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 java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public interface QueryEngine
+{
+    List<? extends IdentifiableObject> query( Query query );
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryException.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryException.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryException.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,40 @@
+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.
+ */
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class QueryException extends RuntimeException
+{
+    public QueryException( String message )
+    {
+        super( message );
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryService.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/QueryService.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,39 @@
+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.
+ */
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public interface QueryService
+{
+    Result query( Query query );
+
+    Result query( Query query, ResultTransformer transformer );
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restriction.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restriction.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restriction.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,123 @@
+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.base.MoreObjects;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class Restriction
+{
+    /**
+     * Path to property you want to restrict only, one first-level properties are currently supported.
+     */
+    private String path;
+
+    /**
+     * Operator for restriction.
+     */
+    private Operator operator;
+
+    /**
+     * 0..* parameters as required by operator.
+     */
+    private List<Object> parameters = new ArrayList<>();
+
+    public Restriction( String path, Operator operator, Object... parameters )
+    {
+        this.path = path;
+        this.operator = operator;
+
+        for ( Object object : parameters )
+        {
+            addParameter( object );
+        }
+    }
+
+    public String getPath()
+    {
+        return path;
+    }
+
+    public void setPath( String path )
+    {
+        this.path = path;
+    }
+
+    public Operator getOperator()
+    {
+        return operator;
+    }
+
+    public void setOperator( Operator operator )
+    {
+        this.operator = operator;
+    }
+
+    public List<Object> getParameters()
+    {
+        return parameters;
+    }
+
+    public Object getParameter( int idx )
+    {
+        if ( parameters.size() < idx )
+        {
+            return null;
+        }
+
+        return parameters.get( idx );
+    }
+
+    public void setParameters( List<Object> parameters )
+    {
+        this.parameters = parameters;
+    }
+
+    // Builder
+    public Restriction addParameter( Object parameter )
+    {
+        this.parameters.add( parameter );
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return MoreObjects.toStringHelper( this )
+            .add( "path", path )
+            .add( "operator", operator )
+            .add( "parameters", parameters )
+            .toString();
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/RestrictionException.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/RestrictionException.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/RestrictionException.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,40 @@
+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.
+ */
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class RestrictionException extends RuntimeException
+{
+    public RestrictionException( String message )
+    {
+        super( message );
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restrictions.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restrictions.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Restrictions.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,89 @@
+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.
+ */
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public final class Restrictions
+{
+    public static Restriction eq( String path, Object value )
+    {
+        return new Restriction( path, Operator.EQ, value );
+    }
+
+    public static Restriction ne( String path, Object value )
+    {
+        return new Restriction( path, Operator.NE, value );
+    }
+
+    public static Restriction gt( String path, Object value )
+    {
+        return new Restriction( path, Operator.GT, value );
+    }
+
+    public static Restriction lt( String path, Object value )
+    {
+        return new Restriction( path, Operator.LT, value );
+    }
+
+    public static Restriction ge( String path, Object value )
+    {
+        return new Restriction( path, Operator.GE, value );
+    }
+
+    public static Restriction le( String path, Object value )
+    {
+        return new Restriction( path, Operator.LE, value );
+    }
+
+    public static Restriction between( String path, Object lside, Object rside )
+    {
+        return new Restriction( path, Operator.BETWEEN, lside, rside );
+    }
+
+    public static Restriction like( String path, Object value )
+    {
+        if ( !String.class.isInstance( value ) )
+        {
+            throw new RestrictionException( "Invalid type for LIKE operator, only String is allowed." );
+        }
+
+        return new Restriction( path, Operator.LIKE, value );
+    }
+
+    public static Restriction in( String path, Object... values )
+    {
+        return new Restriction( path, Operator.IN, values );
+    }
+
+    private Restrictions()
+    {
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Result.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Result.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/Result.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,71 @@
+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.base.MoreObjects;
+import org.hisp.dhis.common.IdentifiableObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class Result
+{
+    protected List<? extends IdentifiableObject> items = new ArrayList<>();
+
+    public Result()
+    {
+    }
+
+    public Result( List<? extends IdentifiableObject> items )
+    {
+        this.items = items;
+    }
+
+    public List<? extends IdentifiableObject> getItems()
+    {
+        return items;
+    }
+
+    public int size()
+    {
+        return items == null ? 0 : items.size();
+    }
+
+
+    @Override
+    public String toString()
+    {
+        return MoreObjects.toStringHelper( this )
+            .add( "items", items )
+            .toString();
+    }
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/ResultTransformer.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/ResultTransformer.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/query/ResultTransformer.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,37 @@
+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.
+ */
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public interface ResultTransformer
+{
+    Result transform( MutableResult result );
+}

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/Schema.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/Schema.java	2015-02-17 07:22:47 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/Schema.java	2015-02-18 13:08:37 +0000
@@ -338,6 +338,16 @@
         this.propertyMap = propertyMap;
     }
 
+    public void addProperty( Property property )
+    {
+        if ( property == null || property.getName() == null || propertyMap.containsKey( property.getName() ) )
+        {
+            return;
+        }
+
+        propertyMap.put( property.getName(), property );
+    }
+
     @JsonIgnore
     public Property getProperty( String name )
     {

=== added directory 'dhis-2/dhis-api/src/test/java/org/hisp/dhis/query'
=== added file 'dhis-2/dhis-api/src/test/java/org/hisp/dhis/query/QueryTest.java'
--- dhis-2/dhis-api/src/test/java/org/hisp/dhis/query/QueryTest.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/test/java/org/hisp/dhis/query/QueryTest.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,93 @@
+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.dataelement.DataElement;
+import org.hisp.dhis.schema.Property;
+import org.hisp.dhis.schema.Schema;
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class QueryTest
+{
+    private Property createProperty( Class<?> klazz, String name, boolean simple, boolean persisted )
+    {
+        Property property = new Property( klazz );
+        property.setName( name );
+        property.setFieldName( name );
+        property.setSimple( simple );
+        property.setPersisted( persisted );
+
+        return property;
+    }
+
+    private Schema createSchema()
+    {
+        Schema schema = new Schema( DataElement.class, "dataElement", "dataElements" );
+        schema.addProperty( createProperty( String.class, "id", true, true ) );
+        schema.addProperty( createProperty( String.class, "name", true, true ) );
+        schema.addProperty( createProperty( String.class, "code", true, true ) );
+        schema.addProperty( createProperty( Date.class, "lastUpdated", true, true ) );
+
+        return schema;
+    }
+
+    @Test
+    public void validRestrictionParameters()
+    {
+        Query query = Query.from( createSchema() );
+        query.add( Restrictions.eq( "id", "anc" ) );
+        query.add( Restrictions.like( "name", "anc" ) );
+        query.add( Restrictions.eq( "code", "anc" ) );
+
+        assertEquals( 3, query.getRestrictions().size() );
+    }
+
+    @Test
+    public void invalidRestrictionParameters()
+    {
+        Query query = Query.from( createSchema() );
+        query.add( new Restriction( "id", Operator.EQ ) );
+        query.add( new Restriction( "id", Operator.EQ, 1, 2 ) );
+        query.add( new Restriction( "name", Operator.EQ ) );
+        query.add( new Restriction( "name", Operator.EQ, 1, 2 ) );
+        query.add( new Restriction( "code", Operator.EQ ) );
+        query.add( new Restriction( "code", Operator.EQ, 1, 2, 3, 4, 5 ) );
+        query.add( new Restriction( "lastUpdated", Operator.BETWEEN, new Date() ) );
+        query.add( new Restriction( "lastUpdated", Operator.BETWEEN, new Date(), 1, 2, 3, 4 ) );
+
+        assertEquals( 0, query.getRestrictions().size() );
+    }
+}

=== added directory 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query'
=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/CriteriaQueryEngine.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/CriteriaQueryEngine.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/CriteriaQueryEngine.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,195 @@
+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.hibernate.Criteria;
+import org.hibernate.criterion.Criterion;
+import org.hibernate.criterion.Restrictions;
+import org.hisp.dhis.common.IdentifiableObject;
+import org.hisp.dhis.hibernate.HibernateGenericStore;
+import org.hisp.dhis.schema.Property;
+import org.hisp.dhis.schema.Schema;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class CriteriaQueryEngine implements QueryEngine
+{
+    @Autowired
+    private final List<HibernateGenericStore> hibernateGenericStores = new ArrayList<>();
+
+    private final Map<Class<?>, HibernateGenericStore> stores = new HashMap<>();
+
+    @PostConstruct
+    public void init()
+    {
+        for ( HibernateGenericStore store : hibernateGenericStores )
+        {
+            stores.put( store.getClazz(), store );
+        }
+    }
+
+    @Override
+    @SuppressWarnings( "unchecked" )
+    public List<? extends IdentifiableObject> query( Query query )
+    {
+        Schema schema = query.getSchema();
+
+        if ( schema == null )
+        {
+            return new ArrayList<>();
+        }
+
+        HibernateGenericStore store = stores.get( schema.getKlass() );
+
+        if ( store == null )
+        {
+            return new ArrayList<>();
+        }
+
+        Criteria criteria = buildCriteria( store.getSharingCriteria(), query );
+
+        if ( criteria == null )
+        {
+            return new ArrayList<>();
+        }
+
+        return criteria.list();
+    }
+
+    private Criteria buildCriteria( Criteria criteria, Query query )
+    {
+        if ( query.getFirstResult() != null )
+        {
+            criteria.setFirstResult( query.getFirstResult() );
+        }
+
+        if ( query.getMaxResults() != null )
+        {
+            criteria.setMaxResults( query.getMaxResults() );
+        }
+
+        for ( Restriction restriction : query.getRestrictions() )
+        {
+            criteria.add( getHibernateCriterion( query.getSchema(), restriction ) );
+        }
+
+        for ( Order order : query.getOrders() )
+        {
+            criteria.addOrder( getHibernateOrder( order ) );
+        }
+
+        return criteria;
+    }
+
+    private Criterion getHibernateCriterion( Schema schema, Restriction restriction )
+    {
+        if ( restriction == null || restriction.getOperator() == null )
+        {
+            return null;
+        }
+
+        Property property = schema.getProperty( restriction.getPath() );
+
+        switch ( restriction.getOperator() )
+        {
+            case EQ:
+            {
+                return Restrictions.eq( property.getFieldName(), restriction.getParameter( 0 ) );
+            }
+            case NE:
+            {
+                return Restrictions.ne( property.getFieldName(), restriction.getParameter( 0 ) );
+            }
+            case GT:
+            {
+                return Restrictions.gt( property.getFieldName(), restriction.getParameter( 0 ) );
+            }
+            case LT:
+            {
+                return Restrictions.lt( property.getFieldName(), restriction.getParameter( 0 ) );
+            }
+            case GE:
+            {
+                return Restrictions.ge( property.getFieldName(), restriction.getParameter( 0 ) );
+            }
+            case LE:
+            {
+                return Restrictions.le( property.getFieldName(), restriction.getParameter( 0 ) );
+            }
+            case BETWEEN:
+            {
+                return Restrictions.between( property.getFieldName(), restriction.getParameter( 0 ), restriction.getParameter( 1 ) );
+            }
+            case LIKE:
+            {
+                return Restrictions.like( property.getFieldName(), restriction.getParameter( 0 ) );
+            }
+            case IN:
+            {
+                return Restrictions.in( property.getFieldName(), restriction.getParameters() );
+            }
+        }
+
+        return null;
+    }
+
+    public org.hibernate.criterion.Order getHibernateOrder( Order order )
+    {
+        if ( order == null || order.getProperty() == null || !order.getProperty().isPersisted() || !order.getProperty().isSimple() )
+        {
+            return null;
+        }
+
+        org.hibernate.criterion.Order criteriaOrder;
+
+        if ( order.isAscending() )
+        {
+            criteriaOrder = org.hibernate.criterion.Order.asc( order.getProperty().getFieldName() );
+        }
+        else
+        {
+            criteriaOrder = org.hibernate.criterion.Order.desc( order.getProperty().getFieldName() );
+        }
+
+        if ( order.isIgnoreCase() )
+        {
+            criteriaOrder.ignoreCase();
+        }
+
+        return criteriaOrder;
+    }
+}

=== added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/query/DefaultQueryService.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,63 @@
+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.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class DefaultQueryService implements QueryService
+{
+    @Autowired
+    private QueryEngine queryEngine;
+
+    @Override
+    public Result query( Query query )
+    {
+        List<? extends IdentifiableObject> objects = queryEngine.query( query );
+        return new Result( objects );
+    }
+
+    @Override
+    public Result query( Query query, ResultTransformer transformer )
+    {
+        List<? extends IdentifiableObject> objects = queryEngine.query( query );
+
+        if ( transformer != null )
+        {
+            return transformer.transform( new MutableResult( objects ) );
+        }
+
+        return new Result( objects );
+    }
+}

=== 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-02-13 15:56:34 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2015-02-18 13:08:37 +0000
@@ -15,6 +15,10 @@
 
   <bean id="org.hisp.dhis.schema.SchemaService" class="org.hisp.dhis.schema.DefaultSchemaService" />
 
+  <bean id="org.hisp.dhis.query.QueryEngine" class="org.hisp.dhis.query.CriteriaQueryEngine" />
+
+  <bean id="org.hisp.dhis.query.QueryService" class="org.hisp.dhis.query.DefaultQueryService" />
+
   <bean id="org.hisp.dhis.schema.PropertyIntrospectorService" class="org.hisp.dhis.schema.Jackson2PropertyIntrospectorService" />
 
   <bean id="org.hisp.dhis.acl.AclService" class="org.hisp.dhis.acl.DefaultAclService" />

=== added directory 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query'
=== added file 'dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/QueryServiceTest.java'
--- dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/QueryServiceTest.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/test/java/org/hisp/dhis/query/QueryServiceTest.java	2015-02-18 13:08:37 +0000
@@ -0,0 +1,312 @@
+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.DhisSpringTest;
+import org.hisp.dhis.common.IdentifiableObject;
+import org.hisp.dhis.common.IdentifiableObjectManager;
+import org.hisp.dhis.dataelement.DataElement;
+import org.hisp.dhis.schema.SchemaService;
+import org.jfree.data.time.Year;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Morten Olav Hansen <mortenoh@xxxxxxxxx>
+ */
+public class QueryServiceTest
+    extends DhisSpringTest
+{
+    @Autowired
+    private SchemaService schemaService;
+
+    @Autowired
+    private QueryService queryService;
+
+    @Autowired
+    private IdentifiableObjectManager _identifiableObjectManager;
+
+    @Override
+    protected void setUpTest() throws Exception
+    {
+        this.identifiableObjectManager = _identifiableObjectManager;
+    }
+
+    private void createDataElements()
+    {
+        DataElement dataElementA = createDataElement( 'A' );
+        DataElement dataElementB = createDataElement( 'B' );
+        DataElement dataElementC = createDataElement( 'C' );
+        DataElement dataElementD = createDataElement( 'D' );
+        DataElement dataElementE = createDataElement( 'E' );
+        DataElement dataElementF = createDataElement( 'F' );
+
+        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() );
+
+        identifiableObjectManager.save( dataElementA );
+        identifiableObjectManager.save( dataElementB );
+        identifiableObjectManager.save( dataElementC );
+        identifiableObjectManager.save( dataElementD );
+        identifiableObjectManager.save( dataElementE );
+        identifiableObjectManager.save( dataElementF );
+    }
+
+    private boolean collectionContainsUid( Collection<? extends IdentifiableObject> collection, String uid )
+    {
+        for ( IdentifiableObject identifiableObject : collection )
+        {
+            if ( identifiableObject.getUid().equals( uid ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Test
+    public void getAllQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 6, result.size() );
+    }
+
+    @Test
+    public void getMinMaxQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.setFirstResult( 2 );
+        query.setMaxResults( 10 );
+
+        assertEquals( 4, queryService.query( query ).size() );
+
+        query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.setFirstResult( 2 );
+        query.setMaxResults( 2 );
+
+        assertEquals( 2, queryService.query( query ).size() );
+    }
+
+    @Test
+    public void getEqQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.eq( "id", "deabcdefghA" ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 1, result.size() );
+        assertEquals( "deabcdefghA", result.getItems().get( 0 ).getUid() );
+    }
+
+    @Test
+    public void getNeQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.ne( "id", "deabcdefghA" ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 5, result.size() );
+    }
+
+    @Test
+    public void getLikeQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.like( "name", "%F" ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 1, result.size() );
+        assertEquals( "deabcdefghF", result.getItems().get( 0 ).getUid() );
+    }
+
+    @Test
+    public void getGtQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.gt( "created", Year.parseYear( "2003" ).getStart() ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 3, result.size() );
+
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghD" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghE" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghF" ) );
+    }
+
+    @Test
+    public void getLtQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.lt( "created", Year.parseYear( "2003" ).getStart() ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 2, result.size() );
+
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghA" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghB" ) );
+    }
+
+    @Test
+    public void getGeQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.ge( "created", Year.parseYear( "2003" ).getStart() ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 4, result.size() );
+
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghC" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghD" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghE" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghF" ) );
+    }
+
+    @Test
+    public void getLeQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.le( "created", Year.parseYear( "2003" ).getStart() ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 3, result.size() );
+
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghA" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghB" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghC" ) );
+    }
+
+    @Test
+    public void getBetweenQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.between( "created", Year.parseYear( "2003" ).getStart(), Year.parseYear( "2005" ).getStart() ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 3, result.size() );
+
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghC" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghD" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghE" ) );
+    }
+
+    @Test
+    public void getInQuery()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+        query.add( Restrictions.in( "id", "deabcdefghD", "deabcdefghF" ) );
+        Result result = queryService.query( query );
+
+        assertEquals( 2, result.size() );
+
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghD" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghF" ) );
+    }
+
+    @Test
+    public void resultTransformerTest()
+    {
+        createDataElements();
+        Query query = Query.from( schemaService.getDynamicSchema( DataElement.class ) );
+
+        Result result = queryService.query( query, new ResultTransformer()
+        {
+            @Override
+            public Result transform( MutableResult result )
+            {
+                return new Result();
+            }
+        } );
+
+        assertEquals( 0, result.size() );
+
+        result = queryService.query( query, new ResultTransformer()
+        {
+            @Override
+            public Result transform( MutableResult result )
+            {
+                return new Result( result.getItems() );
+            }
+        } );
+
+        assertEquals( 6, result.size() );
+
+        result = queryService.query( query, new ResultTransformer()
+        {
+            @Override
+            public Result transform( MutableResult result )
+            {
+                Iterator<? extends IdentifiableObject> iterator = result.getItems().iterator();
+
+                while ( iterator.hasNext() )
+                {
+                    IdentifiableObject identifiableObject = iterator.next();
+
+                    if ( identifiableObject.getUid().equals( "deabcdefghD" ) )
+                    {
+                        iterator.remove();
+                    }
+                }
+
+                return result;
+            }
+        } );
+
+        assertEquals( 5, result.size() );
+
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghA" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghB" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghC" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghE" ) );
+        assertTrue( collectionContainsUid( result.getItems(), "deabcdefghF" ) );
+    }
+}

=== modified file 'dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java'
--- dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java	2015-02-17 07:22:47 +0000
+++ dhis-2/dhis-support/dhis-support-hibernate/src/main/java/org/hisp/dhis/hibernate/HibernateGenericStore.java	2015-02-18 13:08:37 +0000
@@ -179,12 +179,12 @@
      *
      * @return a Criteria instance.
      */
-    protected final Criteria getCriteria()
+    public final Criteria getCriteria()
     {
         return getClazzCriteria().setCacheable( cacheable );
     }
 
-    protected final Criteria getSharingCriteria()
+    public final Criteria getSharingCriteria()
     {
         return getSharingCriteria( currentUserService.getCurrentUser(), "r%" );
     }