diff --git a/.classpath b/.classpath
index 1339473..909ca97 100644
--- a/.classpath
+++ b/.classpath
@@ -1,8 +1,8 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/java"/>
-	<classpathentry excluding="src/resources/|src/java/" including="META-INF/MANIFEST.MF|plugin.xml" kind="src" path=""/>
-	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/java"/>
+	<classpathentry excluding="src/java/|src/resources/" including="META-INF/MANIFEST.MF|plugin.xml" kind="src" path=""/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
index 33e6d61..79dcaec 100644
--- a/META-INF/MANIFEST.MF
+++ b/META-INF/MANIFEST.MF
@@ -4,9 +4,60 @@ Bundle-Name: DataNucleus LDAP
 Bundle-SymbolicName: org.datanucleus.store.ldap;singleton:=true
 Bundle-Version: 1.0.2
 Bundle-Vendor: DataNucleus
-Require-Bundle: org.datanucleus;bundle-version="[1.0.0.final, 2.0)"
+Require-Bundle: org.datanucleus;bundle-version="[1.0.0.final,2.0)"
 Import-Package: javax.jdo,
  javax.jdo.annotations,
  javax.jdo.datastore,
  javax.jdo.identity,
- javax.jdo.listener
+ javax.jdo.listener,
+ org.apache.directory.shared.asn1;resolution:=optional,
+ org.apache.directory.shared.asn1.ber;resolution:=optional,
+ org.apache.directory.shared.asn1.ber.grammar;resolution:=optional,
+ org.apache.directory.shared.asn1.ber.tlv;resolution:=optional,
+ org.apache.directory.shared.asn1.codec;resolution:=optional,
+ org.apache.directory.shared.asn1.codec.binary;resolution:=optional,
+ org.apache.directory.shared.asn1.codec.stateful;resolution:=optional,
+ org.apache.directory.shared.asn1.codec.stateful.examples;resolution:=optional,
+ org.apache.directory.shared.asn1.der;resolution:=optional,
+ org.apache.directory.shared.asn1.primitives;resolution:=optional,
+ org.apache.directory.shared.asn1.util;resolution:=optional,
+ org.apache.directory.shared.ldap;resolution:=optional,
+ org.apache.directory.shared.ldap.aci;resolution:=optional,
+ org.apache.directory.shared.ldap.codec;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.abandon;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.actions;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.add;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.bind;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.compare;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.controls;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.del;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.extended;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.extended.operations;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.modify;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.modifyDn;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.search;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.search.controls;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.unbind;resolution:=optional,
+ org.apache.directory.shared.ldap.codec.util;resolution:=optional,
+ org.apache.directory.shared.ldap.constants;resolution:=optional,
+ org.apache.directory.shared.ldap.entry;resolution:=optional,
+ org.apache.directory.shared.ldap.entry.client;resolution:=optional,
+ org.apache.directory.shared.ldap.exception;resolution:=optional,
+ org.apache.directory.shared.ldap.filter;resolution:=optional,
+ org.apache.directory.shared.ldap.ldif;resolution:=optional,
+ org.apache.directory.shared.ldap.message;resolution:=optional,
+ org.apache.directory.shared.ldap.message.control;resolution:=optional,
+ org.apache.directory.shared.ldap.message.extended;resolution:=optional,
+ org.apache.directory.shared.ldap.message.spi;resolution:=optional,
+ org.apache.directory.shared.ldap.name;resolution:=optional,
+ org.apache.directory.shared.ldap.schema;resolution:=optional,
+ org.apache.directory.shared.ldap.schema.comparator;resolution:=optional,
+ org.apache.directory.shared.ldap.schema.parser;resolution:=optional,
+ org.apache.directory.shared.ldap.schema.syntax;resolution:=optional,
+ org.apache.directory.shared.ldap.schema.syntax.parser;resolution:=optional,
+ org.apache.directory.shared.ldap.sp;resolution:=optional,
+ org.apache.directory.shared.ldap.subtree;resolution:=optional,
+ org.apache.directory.shared.ldap.trigger;resolution:=optional,
+ org.apache.directory.shared.ldap.util;resolution:=optional,
+ org.apache.directory.shared.ldap.util.tree;resolution:=optional,
+ org.apache.directory.shared.ldap.util.unicode;resolution:=optional
diff --git a/pom.xml b/pom.xml
index ca66bee..139e597 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,26 @@
             <artifactId>datanucleus-core</artifactId>
             <version>[1.0-SNAPSHOT, 2.0)</version>
         </dependency>
+
+        <!-- Compile dependencies -->
+        <dependency>
+            <groupId>org.apache.directory.shared</groupId>
+            <artifactId>shared-ldap</artifactId>
+            <version>0.9.13</version>
+            <optional>true</optional>
+         </dependency>
+         <dependency>
+            <groupId>org.apache.directory.shared</groupId>
+            <artifactId>shared-asn1</artifactId>
+            <version>0.9.13</version>
+            <optional>true</optional>
+         </dependency>
+         <dependency>
+            <groupId>org.apache.directory.shared</groupId>
+            <artifactId>shared-ldap-constants</artifactId>
+            <version>0.9.13</version>
+            <optional>true</optional>
+         </dependency>
     </dependencies>
     
     <build>
diff --git a/src/java/org/datanucleus/store/ldap/LDAPUtils.java b/src/java/org/datanucleus/store/ldap/LDAPUtils.java
index 612a243..b01d8e2 100644
--- a/src/java/org/datanucleus/store/ldap/LDAPUtils.java
+++ b/src/java/org/datanucleus/store/ldap/LDAPUtils.java
@@ -14,7 +14,7 @@ limitations under the License.
 
 Contributors:
     ...
-**********************************************************************/
+ **********************************************************************/
 package org.datanucleus.store.ldap;
 
 import java.util.ArrayList;
@@ -35,8 +35,10 @@ import org.datanucleus.ObjectManager;
 import org.datanucleus.StateManager;
 import org.datanucleus.metadata.AbstractClassMetaData;
 import org.datanucleus.metadata.AbstractMemberMetaData;
+import org.datanucleus.query.compiler.QueryCompilation;
 import org.datanucleus.store.FieldValues;
 import org.datanucleus.store.ldap.fieldmanager.FetchFieldManager;
+import org.datanucleus.store.ldap.query.QueryToLDAPFilterMapper;
 
 /**
  * Convenience methods for LDAP datastores.
@@ -53,14 +55,14 @@ public class LDAPUtils
     public static String getDistinguishedNameForObject(StateManager sm)
     {
         Object value = sm.provideField(sm.getClassMetaData().getPKMemberPositions()[0]);
-        String dnKey = sm.getClassMetaData().getMetaDataForManagedMemberAtPosition(
-            sm.getClassMetaData().getPKMemberPositions()[0]).getValueForExtension("dn");
+        String dnKey = sm.getClassMetaData().getMetaDataForManagedMemberAtPosition(sm.getClassMetaData().getPKMemberPositions()[0])
+                .getValueForExtension("dn");
         return dnKey + "=" + value + "," + sm.getClassMetaData().getValueForExtension("dn");
     }
 
     /**
-     * Accessor for the (LDAP) distinguished name for the specified field.
-     * Uses the extension "dn" if specified, else uses the field name.
+     * Accessor for the (LDAP) distinguished name for the specified field. Uses the extension "dn" if specified, else
+     * uses the field name.
      * @param mmd MetaData for the field
      * @return The dn
      */
@@ -75,32 +77,34 @@ public class LDAPUtils
     }
 
     /**
-     * Convenience method to get all objects of the candidate type (and optional subclasses) from the 
-     * specified LDAP connection.
+     * Convenience method to get all objects of the candidate type (and optional subclasses) from the specified LDAP
+     * connection.
      * @param om ObjectManager
      * @param mconn Managed connection
+     * @param compilation Compilation
      * @param candidateClass Candidate
      * @param subclasses Include subclasses?
      * @param ignoreCache Whether to ignore the cache
+     * @param inMemory whether to filter in memory or to use native LDAP filters
      * @return List of objects of the candidate type (or subclass)
      */
-    public static List getObjectsOfCandidateType(ObjectManager om, ManagedConnection mconn, 
-            Class candidateClass, boolean subclasses, boolean ignoreCache)
+    public static List getObjectsOfCandidateType(ObjectManager om, ManagedConnection mconn, QueryCompilation compilation,
+            Class candidateClass, boolean subclasses, boolean ignoreCache, boolean inMemory)
     {
-        DirContext ctx = (DirContext)mconn.getConnection();
+        DirContext ctx = (DirContext) mconn.getConnection();
         ClassLoaderResolver clr = om.getClassLoaderResolver();
         AbstractClassMetaData acmd = om.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
-        List results = getObjectsOfCandidateType(om, ctx, acmd, ignoreCache);
+        List results = getObjectsOfCandidateType(om, ctx, compilation, acmd, ignoreCache, inMemory);
         if (subclasses)
         {
             // Add on any subclass objects
             String[] subclassNames = om.getMetaDataManager().getSubclassesForClass(candidateClass.getName(), true);
             if (subclassNames != null)
             {
-                for (int i=0;i<subclassNames.length;i++)
+                for (int i = 0; i < subclassNames.length; i++)
                 {
                     AbstractClassMetaData cmd = om.getMetaDataManager().getMetaDataForClass(subclassNames[i], clr);
-                    results.addAll(getObjectsOfCandidateType(om, ctx, cmd, ignoreCache));
+                    results.addAll(getObjectsOfCandidateType(om, ctx, compilation, cmd, ignoreCache, inMemory));
                 }
             }
         }
@@ -111,47 +115,60 @@ public class LDAPUtils
      * Method to extract the actual objects of a particular class from the LDAP server.
      * @param om ObjectManager
      * @param ctx Directory context
+     * @param compilation Compilation
      * @param acmd MetaData for the class
      * @param ignoreCache Whether to ignore the cache
+     * @param inMemory whether to filter in memory or to use native LDAP filters
      * @return List of objects (connected to StateManagers as required)
      */
-    private static List getObjectsOfCandidateType(ObjectManager om, DirContext ctx,
-            final AbstractClassMetaData acmd, boolean ignoreCache)
+    private static List getObjectsOfCandidateType(ObjectManager om, DirContext ctx, QueryCompilation compilation,
+            final AbstractClassMetaData acmd, boolean ignoreCache, boolean inMemory)
     {
         List results = new ArrayList();
         try
         {
             ClassLoaderResolver clr = om.getClassLoaderResolver();
-            Attributes matchAttrs = new BasicAttributes(true);
-
-            // Search for objects with these matching attributes
             String[] objectClasses = acmd.getValuesForExtension("objectClass");
-            BasicAttribute objectClass = new BasicAttribute( "objectClass");
-            if( objectClasses!=null ) //this is a sanity check, if the class does not have the objectClass
+            if (objectClasses != null) // this is a sanity check, if the class does not have the objectClass
             {
-                for (int i=0; i<objectClasses.length; i++)
+                NamingEnumeration enumeration;
+                if (!inMemory)
                 {
-                    objectClass.add(objectClasses[i]);
+                    QueryToLDAPFilterMapper evaluator = new QueryToLDAPFilterMapper(compilation, acmd);
+                    String filter = evaluator.compile();
+                    enumeration = ctx.search(acmd.getValueForExtension("dn"), filter, null);
                 }
-                matchAttrs.put(objectClass);
-                NamingEnumeration enumeration = ctx.search(acmd.getValueForExtension("dn"),matchAttrs);
+                else
+                {
+                    // Search for objects with these matching attributes
+                    Attributes matchAttrs = new BasicAttributes(true);
+                    BasicAttribute objectClass = new BasicAttribute("objectClass");
+                    for (int i = 0; i < objectClasses.length; i++)
+                    {
+                        objectClass.add(objectClasses[i]);
+                    }
+                    matchAttrs.put(objectClass);
+                    enumeration = ctx.search(acmd.getValueForExtension("dn"), matchAttrs);
+                }
+
                 while (enumeration.hasMoreElements())
                 {
                     final SearchResult sr = (SearchResult) enumeration.nextElement();
                     final Attributes attrs = sr.getAttributes();
-    
-                    results.add(om.findObjectUsingAID(clr.classForName(acmd.getFullClassName()),
-                        new FieldValues()
+
+                    results.add(om.findObjectUsingAID(clr.classForName(acmd.getFullClassName()), new FieldValues()
                     {
                         // StateManager calls the fetchFields method
                         public void fetchFields(StateManager sm)
                         {
-                            sm.replaceFields(acmd.getAllMemberPositions(), new FetchFieldManager(sm,attrs));
+                            sm.replaceFields(acmd.getAllMemberPositions(), new FetchFieldManager(sm, attrs));
                         }
+
                         public void fetchNonLoadedFields(StateManager sm)
                         {
-                            sm.replaceNonLoadedFields(acmd.getAllMemberPositions(), new FetchFieldManager(sm,attrs));
+                            sm.replaceNonLoadedFields(acmd.getAllMemberPositions(), new FetchFieldManager(sm, attrs));
                         }
+
                         public FetchPlan getFetchPlanForLoading()
                         {
                             return null;
@@ -166,4 +183,5 @@ public class LDAPUtils
         }
         return results;
     }
+
 }
\ No newline at end of file
diff --git a/src/java/org/datanucleus/store/ldap/query/JDOQLQuery.java b/src/java/org/datanucleus/store/ldap/query/JDOQLQuery.java
index ec0ea9c..2f43744 100644
--- a/src/java/org/datanucleus/store/ldap/query/JDOQLQuery.java
+++ b/src/java/org/datanucleus/store/ldap/query/JDOQLQuery.java
@@ -1,111 +1,124 @@
-/**********************************************************************
-Copyright (c) 2008 Erik Bengtson and others. All rights reserved.
-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.
-
-Contributors :
-2008 Andy Jefferson - Extract LDAP specific code out into LDAPUtils
-    ...
-***********************************************************************/
-package org.datanucleus.store.ldap.query;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import org.datanucleus.store.ldap.LDAPUtils;
-import org.datanucleus.ManagedConnection;
-import org.datanucleus.ObjectManager;
-import org.datanucleus.query.evaluator.JDOQLEvaluator;
-import org.datanucleus.query.evaluator.JavaQueryEvaluator;
-import org.datanucleus.store.query.AbstractJDOQLQuery;
-import org.datanucleus.util.NucleusLogger;
-
-/**
- * JDOQL query for LDAP datastores.
- * Performs a simple LDAP search for all objects of the requisite "distinguished name"
- * to limit it to the candidate class. Then utilises the generic JDOQLEvaluator to do
- * in-memory imposition of the JDOQL filter, ordering, result etc.
- */
-public class JDOQLQuery extends AbstractJDOQLQuery
-{
-    /**
-     * Constructs a new query instance that uses the given persistence manager.
-     * @param om the associated ObjectManager for this query.
-     */
-    public JDOQLQuery(ObjectManager om)
-    {
-        this(om, (JDOQLQuery) null);
-    }
-
-    /**
-     * Constructs a new query instance having the same criteria as the given query.
-     * @param om The ObjectManager
-     * @param q The query from which to copy criteria.
-     */
-    public JDOQLQuery(ObjectManager om, JDOQLQuery q)
-    {
-        super(om, q);
-    }
-
-    /**
-     * Constructor for a JDOQL query where the query is specified using the "Single-String" format.
-     * @param om The persistence manager
-     * @param query The query string
-     */
-    public JDOQLQuery(ObjectManager om, String query)
-    {
-        super(om, query);
-    }
-
-    protected Object performExecute(Map parameters)
-    {
-        ManagedConnection mconn = om.getStoreManager().getConnection(om);
-        try
-        {
-            long startTime = 0;
-            if (NucleusLogger.QUERY.isDebugEnabled())
-            {
-                startTime = System.currentTimeMillis();
-                NucleusLogger.QUERY.debug(LOCALISER.msg("021046", "JDOQL", getSingleStringQuery(), null));
-            }
-            List candidates = null;
-            if (candidateCollection == null)
-            {
-                candidates = LDAPUtils.getObjectsOfCandidateType(om, mconn, candidateClass, subclasses,
-                    ignoreCache);
-            }
-            else
-            {
-                candidates = new ArrayList(candidateCollection);
-            }
-
-            // Map any result restrictions onto the LDAP search results
-            JavaQueryEvaluator resultMapper = new JDOQLEvaluator(this, candidates, compilation, 
-                om.getClassLoaderResolver());
-            Collection results = resultMapper.execute(true, true, true, true, true);
-
-            if (NucleusLogger.QUERY.isDebugEnabled())
-            {
-                NucleusLogger.QUERY.debug(LOCALISER.msg("021074", "JDOQL", 
-                    "" + (System.currentTimeMillis() - startTime)));
-            }
-
-            return results;
-        }
-        finally
-        {
-            mconn.release();
-        }
-    }
+/**********************************************************************
+Copyright (c) 2008 Erik Bengtson and others. All rights reserved.
+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.
+
+Contributors :
+2008 Andy Jefferson - Extract LDAP specific code out into LDAPUtils
+    ...
+ ***********************************************************************/
+package org.datanucleus.store.ldap.query;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.datanucleus.ManagedConnection;
+import org.datanucleus.ObjectManager;
+import org.datanucleus.query.evaluator.JDOQLEvaluator;
+import org.datanucleus.query.evaluator.JavaQueryEvaluator;
+import org.datanucleus.store.ldap.LDAPUtils;
+import org.datanucleus.store.query.AbstractJDOQLQuery;
+import org.datanucleus.util.NucleusLogger;
+
+/**
+ * JDOQL query for LDAP datastores. There are two modes:
+ * <ul>
+ * <li>If the extension datanucleus.query.evaluateInMemory is set to true the query filter is evaluated in-memory. This
+ * means only a simple LDAP search is performed for all objects of the requisite "distinguished name" to limit it to the
+ * candidate class. Then utilises the generic JDOQLEvaluator to do in-memory imposition of the JDOQL filter, ordering,
+ * result etc.</li>
+ * <li>If the extension is absent a more specific LDAP search is performed in order to limit the number of matching
+ * objects on the LDAP server side. Nevertheless the resulting candidates are additionally evaluated the in-memory. One
+ * reason is that most LDAP attributes are case insensitive so an LDAP search may return more objects and they must be
+ * filtered additionally using the in-memory evaluator.</li>
+ * </ul>
+ * Performs a simple LDAP search for all objects of the requisite "distinguished name" to limit it to the candidate
+ * class. Then utilises the generic JDOQLEvaluator to do in-memory imposition of the JDOQL filter, ordering, result etc.
+ */
+public class JDOQLQuery extends AbstractJDOQLQuery
+{
+    /**
+     * Constructs a new query instance that uses the given persistence manager.
+     * @param om the associated ObjectManager for this query.
+     */
+    public JDOQLQuery(ObjectManager om)
+    {
+        this(om, (JDOQLQuery) null);
+    }
+
+    /**
+     * Constructs a new query instance having the same criteria as the given query.
+     * @param om The ObjectManager
+     * @param q The query from which to copy criteria.
+     */
+    public JDOQLQuery(ObjectManager om, JDOQLQuery q)
+    {
+        super(om, q);
+    }
+
+    /**
+     * Constructor for a JDOQL query where the query is specified using the "Single-String" format.
+     * @param om The persistence manager
+     * @param query The query string
+     */
+    public JDOQLQuery(ObjectManager om, String query)
+    {
+        super(om, query);
+    }
+
+    protected Object performExecute(Map parameters)
+    {
+        boolean inMemory = false;
+        String queryInMemoryStr = (String) getExtension("datanucleus.query.evaluateInMemory");
+        if (queryInMemoryStr != null && queryInMemoryStr.equalsIgnoreCase("true"))
+        {
+            inMemory = true;
+        }
+
+        ManagedConnection mconn = om.getStoreManager().getConnection(om);
+        try
+        {
+            long startTime = 0;
+            if (NucleusLogger.QUERY.isDebugEnabled())
+            {
+                startTime = System.currentTimeMillis();
+                NucleusLogger.QUERY.debug(LOCALISER.msg("021046", "JDOQL", getSingleStringQuery(), null));
+            }
+            List candidates = null;
+            if (candidateCollection == null)
+            {
+                candidates = LDAPUtils.getObjectsOfCandidateType(om, mconn, compilation, candidateClass, subclasses, ignoreCache, inMemory);
+            }
+            else
+            {
+                candidates = new ArrayList(candidateCollection);
+            }
+
+            // Map any result restrictions onto the LDAP search results
+            JavaQueryEvaluator resultMapper = new JDOQLEvaluator(this, candidates, compilation, om.getClassLoaderResolver());
+            Collection results = resultMapper.execute(true, true, true, true, true);
+
+            if (NucleusLogger.QUERY.isDebugEnabled())
+            {
+                NucleusLogger.QUERY.debug(LOCALISER.msg("021074", "JDOQL", "" + (System.currentTimeMillis() - startTime)));
+            }
+
+            return results;
+        }
+        finally
+        {
+            mconn.release();
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/java/org/datanucleus/store/ldap/query/JPQLQuery.java b/src/java/org/datanucleus/store/ldap/query/JPQLQuery.java
index cadf301..02b8c13 100644
--- a/src/java/org/datanucleus/store/ldap/query/JPQLQuery.java
+++ b/src/java/org/datanucleus/store/ldap/query/JPQLQuery.java
@@ -71,6 +71,13 @@ public class JPQLQuery extends AbstractJPQLQuery
 
     protected Object performExecute(Map parameters)
     {
+        boolean inMemory = false;
+        String queryInMemoryStr = (String)getExtension("datanucleus.query.evaluateInMemory");
+        if (queryInMemoryStr != null && queryInMemoryStr.equalsIgnoreCase("true"))
+        {
+            inMemory = true;
+        }
+
         ManagedConnection mconn = om.getStoreManager().getConnection(om);
         try
         {
@@ -83,8 +90,7 @@ public class JPQLQuery extends AbstractJPQLQuery
             List candidates = null;
             if (candidateCollection == null)
             {
-                candidates = LDAPUtils.getObjectsOfCandidateType(om, mconn, candidateClass, subclasses,
-                    ignoreCache);
+                candidates = LDAPUtils.getObjectsOfCandidateType(om, mconn, compilation, candidateClass, subclasses, ignoreCache, inMemory);
             }
             else
             {
diff --git a/src/java/org/datanucleus/store/ldap/query/QueryToLDAPFilterMapper.java b/src/java/org/datanucleus/store/ldap/query/QueryToLDAPFilterMapper.java
new file mode 100644
index 0000000..43d7081
--- /dev/null
+++ b/src/java/org/datanucleus/store/ldap/query/QueryToLDAPFilterMapper.java
@@ -0,0 +1,432 @@
+/**********************************************************************
+Copyright (c) 2008 Stefan Seelmann and others. All rights reserved.
+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.
+
+Contributors:
+    ...
+ **********************************************************************/
+package org.datanucleus.store.ldap.query;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.directory.shared.ldap.entry.Value;
+import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
+import org.apache.directory.shared.ldap.filter.AndNode;
+import org.apache.directory.shared.ldap.filter.EqualityNode;
+import org.apache.directory.shared.ldap.filter.ExprNode;
+import org.apache.directory.shared.ldap.filter.GreaterEqNode;
+import org.apache.directory.shared.ldap.filter.LessEqNode;
+import org.apache.directory.shared.ldap.filter.NotNode;
+import org.apache.directory.shared.ldap.filter.OrNode;
+import org.apache.directory.shared.ldap.filter.PresenceNode;
+import org.apache.directory.shared.ldap.filter.SubstringNode;
+import org.datanucleus.exceptions.NucleusException;
+import org.datanucleus.metadata.AbstractClassMetaData;
+import org.datanucleus.metadata.AbstractMemberMetaData;
+import org.datanucleus.query.compiler.QueryCompilation;
+import org.datanucleus.query.evaluator.AbstractExpressionEvaluator;
+import org.datanucleus.query.expression.Expression;
+import org.datanucleus.query.expression.InvokeExpression;
+import org.datanucleus.query.expression.Literal;
+import org.datanucleus.query.expression.PrimaryExpression;
+import org.datanucleus.query.expression.Expression.Operator;
+import org.datanucleus.store.ldap.LDAPUtils;
+
+/**
+ * Class which maps a compiled query to an LDAP filter. Utilizes the filter of the java query and adds them to the
+ * underlying LDAP filter. All other components are not handled here and instead processed by the in-memory evaluator.
+ */
+public class QueryToLDAPFilterMapper extends AbstractExpressionEvaluator
+{
+
+    /** Filter expression. */
+    Expression filterExpr;
+
+    /** The class meta data */
+    AbstractClassMetaData acmd;
+
+    /** The stack */
+    Stack stack = new Stack();
+
+    /** Map with LDAP attribute types */
+    Map ldapAttributeTypeMap;
+
+    /**
+     * Constructor.
+     * @param compilation The generic query compilation
+     * @param acmd
+     * @param parameters Parameters needed
+     */
+    public QueryToLDAPFilterMapper(QueryCompilation compilation, AbstractClassMetaData acmd)
+    {
+        this.filterExpr = compilation.getExprFilter();
+        this.acmd = acmd;
+        // System.out.println(filterExpr);
+    }
+
+    /**
+     * Compiles the query and returns the mapped LDAP filter.
+     * @return the mapped LDAP filter or null if no filter
+     */
+    public String compile()
+    {
+        // objectClass filter
+        AndNode filter = new AndNode();
+        String[] objectClasses = acmd.getValuesForExtension("objectClass");
+        for (int i = 0; i < objectClasses.length; i++)
+        {
+            ExprNode ocNode = new EqualityNode("objectClass", new ClientStringValue(objectClasses[i]));
+            filter.addNode(ocNode);
+        }
+
+        // additional filter
+        if (filterExpr != null)
+        {
+            filterExpr.evaluate(this);
+            if (!stack.empty())
+            {
+                Object object = stack.pop();
+                if (object instanceof ExprNode)
+                {
+                    ExprNode additionalFilter = (ExprNode) object;
+                    filter.addNode(additionalFilter);
+                }
+                else
+                {
+                    throw new NucleusException("Unexpected element on stack: object=" + object);
+                }
+            }
+            else
+            {
+                throw new NucleusException("Unexpected empty stack");
+            }
+        }
+        System.out.println(filter.toString());
+
+        return filter.toString();
+    }
+
+    protected Object processOrExpression(Expression expr)
+    {
+        Object right = stack.pop();
+        Object left = stack.pop();
+        OrNode filter;
+        if (left instanceof ExprNode && right instanceof ExprNode)
+        {
+            filter = new OrNode();
+            filter.addNode((ExprNode) left);
+            filter.addNode((ExprNode) right);
+            stack.push(filter);
+        }
+        else
+        {
+            // TODO: implement other cases
+            throw new NucleusException("Case not handled yet: left=" + left + ", right=" + right);
+        }
+
+        return filter;
+    }
+
+    protected Object processAndExpression(Expression expr)
+    {
+        Object right = stack.pop();
+        Object left = stack.pop();
+        AndNode filter;
+        if (left instanceof ExprNode && right instanceof ExprNode)
+        {
+            filter = new AndNode();
+            filter.addNode((ExprNode) left);
+            filter.addNode((ExprNode) right);
+            stack.push(filter);
+        }
+        else
+        {
+            // TODO: implement other cases
+            throw new NucleusException("Case not handled yet: left=" + left + ", right=" + right);
+        }
+
+        return filter;
+    }
+
+    protected Object processEqExpression(Expression expr)
+    {
+        return processExpressionWithOperator(expr, Expression.OP_EQ);
+    }
+
+    protected Object processNoteqExpression(Expression expr)
+    {
+        return processExpressionWithOperator(expr, Expression.OP_NOTEQ);
+    }
+
+    protected Object processLtExpression(Expression expr)
+    {
+        return processExpressionWithOperator(expr, Expression.OP_LT);
+    }
+
+    protected Object processLteqExpression(Expression expr)
+    {
+        return processExpressionWithOperator(expr, Expression.OP_LTEQ);
+    }
+
+    protected Object processGtExpression(Expression expr)
+    {
+        return processExpressionWithOperator(expr, Expression.OP_GT);
+    }
+
+    protected Object processGteqExpression(Expression expr)
+    {
+        return processExpressionWithOperator(expr, Expression.OP_GTEQ);
+    }
+
+    private Object processExpressionWithOperator(Expression expr, Operator operator)
+    {
+        Object right = stack.pop();
+        Object left = stack.pop();
+        ExprNode filter;
+        if (left instanceof PrimaryExpression && right instanceof Literal)
+        {
+            filter = getFilterForPrimaryLiteralValue(operator, (PrimaryExpression) left, ((Literal) right));
+            stack.push(filter);
+        }
+        else
+        {
+            // TODO: implement other cases
+            throw new NucleusException("Case not handled yet: left=" + left + ", right=" + right);
+        }
+
+        return filter;
+    }
+
+    protected Object processPrimaryExpression(PrimaryExpression expr)
+    {
+        stack.push(expr);
+        return expr;
+    }
+
+    protected Object processLiteral(Literal expr)
+    {
+        stack.push(expr);
+        return expr;
+    }
+
+    /*
+     * Support for startsWith, endsWith and contains
+     */
+    protected Object processInvokeExpression(InvokeExpression expr)
+    {
+        Expression invokedExpr = expr.getLeft();
+        String method = expr.getOperation();
+        SubstringNode filter;
+        if (invokedExpr instanceof PrimaryExpression)
+        {
+            PrimaryExpression primaryExpression = (PrimaryExpression) invokedExpr;
+            String attribute = getLdapAttributeType(primaryExpression);
+            if (method.equals("startsWith"))
+            {
+                // TODO Check if the field we invoke on is String-based
+                Literal param = (Literal) expr.getParameters().get(0);
+                String value = getValue(param);
+                filter = new SubstringNode(attribute);
+                filter.setInitial(value);
+                stack.push(filter);
+            }
+            else if (method.equals("endsWith"))
+            {
+                // TODO Check if the field we invoke on is String-based
+                Literal param = (Literal) expr.getParameters().get(0);
+                String value = getValue(param);
+                filter = new SubstringNode(attribute);
+                filter.setFinal(value);
+                stack.push(filter);
+            }
+            else if (method.equals("contains"))
+            {
+                // TODO Check if the field we invoke on is String-based
+                Literal param = (Literal) expr.getParameters().get(0);
+                String value = getValue(param);
+                filter = new SubstringNode(attribute);
+                filter.addAny(value);
+                stack.push(filter);
+            }
+
+            else
+            {
+                // TODO: implement other cases
+                throw new NucleusException("Case not handled yet: expr=" + expr);
+            }
+        }
+        else
+        {
+            // TODO: implement other cases
+            throw new NucleusException("Case not handled yet: expr=" + expr);
+        }
+        return filter;
+    }
+
+    private String getValue(Literal param)
+    {
+        String value = null;
+        if (param.getLiteral() instanceof String)
+        {
+            value = (String) param.getLiteral();
+        }
+        else if (param.getLiteral() instanceof Character)
+        {
+            value = ((Character) param.getLiteral()).toString();
+        }
+        else if (param.getLiteral() instanceof Number)
+        {
+            value = ((Number) param.getLiteral()).toString();
+        }
+        else if (param.getLiteral() == null)
+        {
+            value = null;
+        }
+        else
+        {
+            // TODO: handle other and binary values
+            throw new NucleusException("Case not handled yet: param=" + param.getLiteral());
+        }
+
+        /*
+         * From RFC 4515: The <valueencoding> rule ensures that the entire filter string is a valid UTF-8 string and
+         * provides that the octets that represent the ASCII characters "*" (ASCII 0x2a), "(" (ASCII 0x28), ")" (ASCII
+         * 0x29), "\" (ASCII 0x5c), and NUL (ASCII 0x00) are represented as a backslash "\" (ASCII 0x5c) followed by the
+         * two hexadecimal digits representing the value of the encoded octet.
+         */
+        if (value != null)
+        {
+            value = value.replace("\\", "\\5c");
+            value = value.replace("\u0000", "\\00");
+            value = value.replace("*", "\\2a");
+            value = value.replace("(", "\\28");
+            value = value.replace(")", "\\29");
+        }
+
+        return value;
+    }
+
+    private ExprNode getFilterForPrimaryLiteralValue(Operator operator, PrimaryExpression expr, Literal param)
+    {
+        String attribute = getLdapAttributeType(expr);
+        Value value = new ClientStringValue(getValue(param));
+
+        ExprNode filter = null;
+        if (operator == Expression.OP_EQ)
+        {
+            if (!value.isNull())
+            {
+                filter = new EqualityNode(attribute, value);
+            }
+            else
+            {
+                // (attribute == null) -> attribute must be absent
+                NotNode notNode = new NotNode();
+                notNode.addNode(new PresenceNode(attribute));
+                filter = notNode;
+            }
+        }
+        else if (operator == Expression.OP_NOTEQ)
+        {
+            if (!value.isNull())
+            {
+                NotNode notNode = new NotNode();
+                notNode.addNode(new EqualityNode(attribute, value));
+                filter = notNode;
+            }
+            else
+            {
+                // (attribute != null) -> attribute must be present
+                filter = new PresenceNode(attribute);
+            }
+        }
+        else if (operator == Expression.OP_LT)
+        {
+            // LDAP filters doesn't support a pure "lesser than" but only a
+            // "lesser than or equal. So we have two possibilities to handle this:
+            // 1st: use "lesser than or equal" and let the in-memory evaluator filter the equal ones
+            // 2nd: use an AND filter to exclude the equal (&(att<=5)(!(att=5)))
+            LessEqNode lessEqualNode = new LessEqNode(attribute, value);
+            NotNode notEqualNode = new NotNode();
+            notEqualNode.addNode(new EqualityNode(attribute, value));
+            AndNode andNode = new AndNode();
+            andNode.addNode(lessEqualNode);
+            andNode.addNode(notEqualNode);
+            filter = andNode;
+        }
+        else if (operator == Expression.OP_LTEQ)
+        {
+            filter = new LessEqNode(attribute, value);
+        }
+        else if (operator == Expression.OP_GT)
+        {
+            // LDAP filters doesn't support a pure "greater than" but only a
+            // "greater than or equal. So we have two possibilities to handle this:
+            // 1st: use "greater than or equal" and let the in-memory evaluator filter the equal ones
+            // 2nd: use an AND filter to exclude the equal (&(att>=5)(!(att=5)))
+            GreaterEqNode greaterEqualNode = new GreaterEqNode(attribute, value);
+            NotNode notEqualNode = new NotNode();
+            notEqualNode.addNode(new EqualityNode(attribute, value));
+            AndNode andNode = new AndNode();
+            andNode.addNode(greaterEqualNode);
+            andNode.addNode(notEqualNode);
+            filter = andNode;
+        }
+        else if (operator == Expression.OP_GTEQ)
+        {
+            filter = new GreaterEqNode(attribute, value);
+        }
+        else
+        {
+            // TODO: implement other cases
+            throw new NucleusException("Case not handled yet: operator=" + operator);
+        }
+
+        return filter;
+    }
+
+    /**
+     * Gets the LDAP attribute type from the given expression.
+     * @param expr the expression
+     * @return the LDAP attribute type
+     * @TODO: is this the right way to get the LDAP attribute type form the expression?
+     */
+    private String getLdapAttributeType(PrimaryExpression expr)
+    {
+        // TODO: handle other cases expr.getTuples(), expr.getId(), ...
+        String id = expr.getId();
+
+        if (ldapAttributeTypeMap == null)
+        {
+            ldapAttributeTypeMap = new HashMap();
+
+            AbstractMemberMetaData[] managedMembers = acmd.getManagedMembers();
+            for (int i = 0; i < managedMembers.length; i++)
+            {
+                AbstractMemberMetaData mmd = managedMembers[i];
+                String ldapAttributeType = LDAPUtils.getDistinguishedNameForField(mmd);
+                ldapAttributeTypeMap.put(mmd.getName(), ldapAttributeType);
+            }
+        }
+
+        String ldapAttributeType = id;
+        if (ldapAttributeTypeMap.containsKey(id))
+        {
+            ldapAttributeType = (String) ldapAttributeTypeMap.get(id);
+        }
+        return ldapAttributeType;
+    }
+
+}
\ No newline at end of file

