You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2025/01/06 10:00:34 UTC

(unomi) 13/24: - Add new conditions bundle for ElasticSearch-specific conditions - Replace hardcoded past event ES query builder in pasteventconditionbuilder to use a generic interface - Replace GeoDistance and DistanceUnit used directly from ElasticSearch with clean-room implementation that are validated through unit tests. Also added unit tests on the ElasticSearch implementation to be able to test any differences - Also added clean-room implementation of the DateMathParser so that it can be used with [...]

This is an automated email from the ASF dual-hosted git repository.

shuber pushed a commit to branch opensearch-persistence
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit 88bfdc188350ca39690c22dae4791b567fdcfb33
Author: Serge Huber <sh...@jahia.com>
AuthorDate: Fri Dec 20 16:36:09 2024 +0100

    - Add new conditions bundle for ElasticSearch-specific conditions
    - Replace hardcoded past event ES query builder in pasteventconditionbuilder to use a generic interface
    - Replace GeoDistance and DistanceUnit used directly from ElasticSearch with clean-room implementation that are validated through unit tests. Also added unit tests on the ElasticSearch implementation to be able to test any differences
    - Also added clean-room implementation of the DateMathParser so that it can be used with ElasticSearch or OpenSearch
    - Modified Unomi startup mechanism to use features instead of bundles. Unfortunately due to complex interdependencies between bundles the features could not be split as wanted, so there is some duplication in the list of bundles.
    - Removed last of inter-dependencies in the base plugin and the persistence-spi to try to resolve bundle inter-dependencies but that wasn't enough.
    - ElasticSearch integration tests are now execute without any errors !
---
 .../main/java/org/apache/unomi/api/GeoPoint.java   |   1 -
 build.sh                                           |  13 -
 kar/src/main/feature/feature.xml                   |  24 +-
 .../src/main/asciidoc/builtin-condition-types.adoc |   4 +-
 manual/src/main/asciidoc/writing-plugins.adoc      |   6 +-
 .../conditions/pom.xml                             |  24 +-
 .../conditions/BooleanConditionESQueryBuilder.java |   6 +-
 ...ationByPointSessionConditionESQueryBuilder.java |   6 +-
 .../conditions/IdsConditionESQueryBuilder.java     |   6 +-
 .../MatchAllConditionESQueryBuilder.java           |   6 +-
 .../conditions/NestedConditionESQueryBuilder.java  |   6 +-
 .../conditions/NotConditionESQueryBuilder.java     |   6 +-
 .../PastEventConditionESQueryBuilder.java          |  13 +-
 .../PropertyConditionESQueryBuilder.java           |   8 +-
 ...SourceEventPropertyConditionESQueryBuilder.java |   8 +-
 .../resources/OSGI-INF/blueprint/blueprint.xml     |  55 ++--
 persistence-elasticsearch/core/pom.xml             |   2 +-
 .../{conditions => }/ConditionESQueryBuilder.java  |   2 +-
 .../ConditionESQueryBuilderDispatcher.java         |   2 +-
 .../ElasticSearchPersistenceServiceImpl.java       |  11 +-
 .../resources/OSGI-INF/blueprint/blueprint.xml     |   4 +-
 .../elasticsearch/geo/DistanceUnitTest.java        | 121 ++++++++
 .../elasticsearch/geo/GeoDistanceTest.java         |  59 ++++
 persistence-elasticsearch/pom.xml                  |   1 +
 persistence-opensearch/conditions/pom.xml          |   7 -
 .../conditions/BooleanConditionOSQueryBuilder.java |   6 +-
 ...ationByPointSessionConditionOSQueryBuilder.java |   6 +-
 .../conditions/IdsConditionOSQueryBuilder.java     |   6 +-
 .../MatchAllConditionOSQueryBuilder.java           |   6 +-
 .../conditions/NestedConditionOSQueryBuilder.java  |   6 +-
 .../conditions/NotConditionOSQueryBuilder.java     |   6 +-
 .../PastEventConditionOSQueryBuilder.java          |  13 +-
 .../PropertyConditionOSQueryBuilder.java           |  10 +-
 ...SourceEventPropertyConditionOSQueryBuilder.java |   6 +-
 .../resources/OSGI-INF/blueprint/blueprint.xml     |  41 +--
 persistence-opensearch/core/pom.xml                |   2 +-
 .../{conditions => }/ConditionOSQueryBuilder.java  |   2 +-
 .../ConditionOSQueryBuilderDispatcher.java         |   2 +-
 .../OpenSearchPersistenceServiceImpl.java          |   3 +-
 .../resources/OSGI-INF/blueprint/blueprint.xml     |   4 +-
 .../persistence/spi/conditions/DateUtils.java      |  54 ++++
 .../PastEventConditionPersistenceQueryBuilder.java |  15 +-
 .../datemath/DateMathParseException.java           |  26 +-
 .../spi/conditions/datemath/DateMathParser.java    | 257 ++++++++++++++++
 .../spi/conditions/datemath/JavaDateFormatter.java | 339 +++++++++++++++++++++
 .../spi/conditions/geo/DistanceUnit.java           | 163 ++++++++++
 .../spi/conditions/geo/GeoDistance.java            | 102 +++++++
 .../persistence/{ => spi}/PropertyHelperTest.java  |   3 +-
 .../conditions/datemath/DateMathParserTest.java    | 192 ++++++++++++
 .../conditions/datemath/JavaDateFormatterTest.java | 164 ++++++++++
 .../spi/conditions/geo/DistanceUnitTest.java       | 136 +++++++++
 .../spi/conditions/geo/GeoDistanceTest.java        |  56 ++++
 plugins/baseplugin/pom.xml                         |   6 -
 ...eoLocationByPointSessionConditionEvaluator.java |   4 +-
 .../conditions/PastEventConditionEvaluator.java    |  10 +-
 .../conditions/PropertyConditionEvaluator.java     |  44 +--
 .../resources/OSGI-INF/blueprint/blueprint.xml     |  85 +-----
 .../HoverEventConditionESQueryBuilder.java         |   4 +-
 58 files changed, 1860 insertions(+), 320 deletions(-)

diff --git a/api/src/main/java/org/apache/unomi/api/GeoPoint.java b/api/src/main/java/org/apache/unomi/api/GeoPoint.java
index b3d0d02a6..4472bbe45 100644
--- a/api/src/main/java/org/apache/unomi/api/GeoPoint.java
+++ b/api/src/main/java/org/apache/unomi/api/GeoPoint.java
@@ -16,7 +16,6 @@
  */
 package org.apache.unomi.api;
 
-
 import java.util.Map;
 
 /**
diff --git a/build.sh b/build.sh
index d53030d20..6eccd7069 100755
--- a/build.sh
+++ b/build.sh
@@ -61,7 +61,6 @@ USE_MAVEN_CACHE=true
 PURGE_MAVEN_CACHE=false
 MAVEN_DEBUG=false
 MAVEN_OFFLINE=false
-SEARCH_ENGINE="elasticsearch"
 KARAF_DEBUG_PORT=5005
 KARAF_DEBUG_SUSPEND=n
 USE_OPENSEARCH=false
@@ -90,7 +89,6 @@ EOF
     echo "  --debug-suspend           Suspend JVM until debugger connects"
     echo "  --no-maven-cache           Disable Maven build cache"
     echo "  --purge-maven-cache        Purge local Maven cache before building"
-    echo "  --search-engine ENGINE     Set search engine (elasticsearch|opensearch)"
     echo "  --karaf-home PATH          Set Karaf home directory for deployment"
     echo "  --use-opensearch          Use OpenSearch instead of ElasticSearch for tests"
     echo ""
@@ -142,10 +140,6 @@ while [ "$1" != "" ]; do
         --purge-maven-cache)
             PURGE_MAVEN_CACHE=true
             ;;
-        --search-engine)
-            shift
-            SEARCH_ENGINE=$1
-            ;;
         --karaf-home)
             shift
             CONTEXT_SERVER_KARAF_HOME=$1
@@ -175,12 +169,6 @@ if [ "$PURGE_MAVEN_CACHE" = true ]; then
     echo "Maven cache purged."
 fi
 
-# Validate search engine value
-if [ "$SEARCH_ENGINE" != "elasticsearch" ] && [ "$SEARCH_ENGINE" != "opensearch" ]; then
-    echo "Invalid search engine: $SEARCH_ENGINE. Must be 'elasticsearch' or 'opensearch'"
-    exit 1
-fi
-
 # Function to check if command exists
 command_exists() {
     command -v "$1" >/dev/null 2>&1
@@ -254,7 +242,6 @@ if [ "$RUN_INTEGRATION_TESTS" = true ]; then
         MVN_OPTS="$MVN_OPTS -Duse.opensearch=true -P opensearch"
         echo "Running integration tests with OpenSearch"
     else
-        MVN_OPTS="$MVN_OPTS -Duse.opensearch=false"
         echo "Running integration tests with ElasticSearch"
     fi
     MVN_OPTS="$MVN_OPTS -P integration-tests"
diff --git a/kar/src/main/feature/feature.xml b/kar/src/main/feature/feature.xml
index f7570ab51..6835ba1a8 100644
--- a/kar/src/main/feature/feature.xml
+++ b/kar/src/main/feature/feature.xml
@@ -84,15 +84,27 @@
     <feature name="unomi-persistence-elasticsearch" description="ElasticSearch persistence implementation for Apache Unomi" version="${project.version}">
         <configfile finalname="/etc/org.apache.unomi.persistence.elasticsearch.cfg">mvn:org.apache.unomi/unomi-persistence-elasticsearch-core/${project.version}/cfg/elasticsearchcfg</configfile>
         <bundle start="false">mvn:org.apache.unomi/unomi-persistence-elasticsearch-core/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-services/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-json-schema-services/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-json-schema-rest/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/cxs-lists-extension-services/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/cxs-lists-extension-rest/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/cxs-geonames-services/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/cxs-geonames-rest/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/cxs-privacy-extension-services/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/cxs-privacy-extension-rest/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-persistence-elasticsearch-conditions/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-rest/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-plugins-base/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-plugins-request/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-plugins-mail/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-plugins-optimization-test/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/cxs-lists-extension-actions/${project.version}</bundle>
     </feature>
 
     <feature name="unomi-persistence-opensearch" description="OpenSearch persistence implementation for Apache Unomi" version="${project.version}">
         <configfile finalname="/etc/org.apache.unomi.persistence.opensearch.cfg">mvn:org.apache.unomi/unomi-persistence-opensearch-core/${project.version}/cfg/opensearchcfg</configfile>
         <bundle start="false">mvn:org.apache.unomi/unomi-persistence-opensearch-core/${project.version}</bundle>
-        <bundle start="false">mvn:org.apache.unomi/unomi-persistence-opensearch-conditions/${project.version}</bundle>
-    </feature>
-
-    <feature name="unomi-services" description="Apache Unomi :: Services" version="${project.version}">
         <bundle start="false">mvn:org.apache.unomi/unomi-services/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/unomi-json-schema-services/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/unomi-json-schema-rest/${project.version}</bundle>
@@ -102,12 +114,16 @@
         <bundle start="false">mvn:org.apache.unomi/cxs-geonames-rest/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/cxs-privacy-extension-services/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/cxs-privacy-extension-rest/${project.version}</bundle>
+        <bundle start="false">mvn:org.apache.unomi/unomi-persistence-opensearch-conditions/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/unomi-rest/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/unomi-plugins-base/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/unomi-plugins-request/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/unomi-plugins-mail/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/unomi-plugins-optimization-test/${project.version}</bundle>
         <bundle start="false">mvn:org.apache.unomi/cxs-lists-extension-actions/${project.version}</bundle>
+    </feature>
+
+    <feature name="unomi-services" description="Apache Unomi :: Services" version="${project.version}">
         <bundle start="false">mvn:org.apache.unomi/shell-dev-commands/${project.version}</bundle>
     </feature>
 
diff --git a/manual/src/main/asciidoc/builtin-condition-types.adoc b/manual/src/main/asciidoc/builtin-condition-types.adoc
index 68a484ba7..39e18f873 100644
--- a/manual/src/main/asciidoc/builtin-condition-types.adoc
+++ b/manual/src/main/asciidoc/builtin-condition-types.adoc
@@ -68,11 +68,11 @@ From https://github.com/apache/unomi/blob/master/plugins/baseplugin/src/main/res
 ----
 ...
     <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="booleanConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.BooleanConditionESQueryBuilder"/>
+        <bean class="conditions.org.apache.unomi.persistence.elasticsearch.BooleanConditionESQueryBuilder"/>
     </service>
 ...
     <!-- Condition evaluators -->
diff --git a/manual/src/main/asciidoc/writing-plugins.adoc b/manual/src/main/asciidoc/writing-plugins.adoc
index 7b230499d..f74cb305b 100644
--- a/manual/src/main/asciidoc/writing-plugins.adoc
+++ b/manual/src/main/asciidoc/writing-plugins.adoc
@@ -519,11 +519,11 @@ src/main/resources/OSGI-INF/blueprint/blueprint.xml
            xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
 
     <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="matchAllConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.MatchAllConditionESQueryBuilder"/>
+        <bean class="conditions.org.apache.unomi.persistence.elasticsearch.MatchAllConditionESQueryBuilder"/>
     </service>
 
     <service interface="org.apache.unomi.persistence.spi.conditions.ConditionEvaluator">
@@ -538,6 +538,6 @@ src/main/resources/OSGI-INF/blueprint/blueprint.xml
 
 You can find the implementation of the two classes here :
 
-* https://github.com/apache/unomi/blob/master/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionESQueryBuilder.java[org.apache.unomi.plugins.baseplugin.conditions.MatchAllConditionESQueryBuilder]
+* https://github.com/apache/unomi/blob/master/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionESQueryBuilder.java[conditions.org.apache.unomi.persistence.elasticsearch.MatchAllConditionESQueryBuilder]
 * https://github.com/apache/unomi/blob/master/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionEvaluator.java[org.apache.unomi.plugins.baseplugin.conditions.MatchAllConditionEvaluator]
 
diff --git a/persistence-opensearch/conditions/pom.xml b/persistence-elasticsearch/conditions/pom.xml
similarity index 89%
copy from persistence-opensearch/conditions/pom.xml
copy to persistence-elasticsearch/conditions/pom.xml
index ae970a3cf..26d4462c5 100644
--- a/persistence-opensearch/conditions/pom.xml
+++ b/persistence-elasticsearch/conditions/pom.xml
@@ -21,13 +21,13 @@
 
     <parent>
         <groupId>org.apache.unomi</groupId>
-        <artifactId>unomi-persistence-opensearch</artifactId>
+        <artifactId>unomi-persistence-elasticsearch</artifactId>
         <version>2.7.0-SNAPSHOT</version>
     </parent>
 
-    <artifactId>unomi-persistence-opensearch-conditions</artifactId>
-    <name>Apache Unomi :: Persistence :: OpenSearch :: Conditions</name>
-    <description>Conditions OpenSearch persistence implementation for the Apache Unomi Context Server</description>
+    <artifactId>unomi-persistence-elasticsearch-conditions</artifactId>
+    <name>Apache Unomi :: Persistence :: ElasticSearch :: Conditions</name>
+    <description>Conditions ElasticSearch persistence implementation for the Apache Unomi Context Server</description>
     <packaging>bundle</packaging>
 
     <dependencies>
@@ -110,16 +110,9 @@
             <scope>provided</scope>
         </dependency>
 
-        <dependency>
-            <groupId>org.opensearch.client</groupId>
-            <artifactId>opensearch-java</artifactId>
-            <version>${opensearch.version}</version>
-            <scope>provided</scope>
-        </dependency>
-
         <dependency>
             <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-persistence-opensearch-core</artifactId>
+            <artifactId>unomi-persistence-elasticsearch-core</artifactId>
             <version>${project.version}</version>
             <scope>provided</scope>
         </dependency>
@@ -130,13 +123,6 @@
             <version>2.12.7</version>
         </dependency>
 
-        <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-plugins-base</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
-        </dependency>
-
     </dependencies>
 
     <build>
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/BooleanConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/BooleanConditionESQueryBuilder.java
similarity index 93%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/BooleanConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/BooleanConditionESQueryBuilder.java
index fa33b29b6..0da3fcac1 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/BooleanConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/BooleanConditionESQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/GeoLocationByPointSessionConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/GeoLocationByPointSessionConditionESQueryBuilder.java
similarity index 92%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/GeoLocationByPointSessionConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/GeoLocationByPointSessionConditionESQueryBuilder.java
index 21d27b127..d77ae0d74 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/GeoLocationByPointSessionConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/GeoLocationByPointSessionConditionESQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/IdsConditionESQueryBuilder.java
similarity index 90%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/IdsConditionESQueryBuilder.java
index d535c1b65..4999f186a 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/IdsConditionESQueryBuilder.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.IdsQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/MatchAllConditionESQueryBuilder.java
similarity index 84%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/MatchAllConditionESQueryBuilder.java
index 700286ac4..f1555d689 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/MatchAllConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/MatchAllConditionESQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/NestedConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/NestedConditionESQueryBuilder.java
similarity index 89%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/NestedConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/NestedConditionESQueryBuilder.java
index e8c3701eb..a6c01c5ea 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/NestedConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/NestedConditionESQueryBuilder.java
@@ -15,12 +15,12 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.lucene.search.join.ScoreMode;
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/NotConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/NotConditionESQueryBuilder.java
similarity index 85%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/NotConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/NotConditionESQueryBuilder.java
index 9574fe82f..1be236f2c 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/NotConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/NotConditionESQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/PastEventConditionESQueryBuilder.java
similarity index 96%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/PastEventConditionESQueryBuilder.java
index b2481e794..c440793fc 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/PastEventConditionESQueryBuilder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.unomi.api.Event;
 import org.apache.unomi.api.Profile;
@@ -23,18 +23,19 @@ import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.api.services.SegmentService;
 import org.apache.unomi.api.utils.ConditionBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.apache.unomi.persistence.spi.PersistenceService;
 import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
+import org.apache.unomi.persistence.spi.conditions.PastEventConditionPersistenceQueryBuilder;
 import org.apache.unomi.scripting.ScriptExecutor;
 import org.elasticsearch.index.query.QueryBuilder;
 
 import java.util.*;
 import java.util.stream.Collectors;
 
-public class PastEventConditionESQueryBuilder implements ConditionESQueryBuilder {
+public class PastEventConditionESQueryBuilder implements ConditionESQueryBuilder, PastEventConditionPersistenceQueryBuilder {
 
     private DefinitionsService definitionsService;
     private PersistenceService persistenceService;
@@ -117,7 +118,7 @@ public class PastEventConditionESQueryBuilder implements ConditionESQueryBuilder
         }
     }
 
-    protected static boolean getStrategyFromOperator(String operator) {
+    public boolean getStrategyFromOperator(String operator) {
         if (operator != null && !operator.equals("eventsOccurred") && !operator.equals("eventsNotOccurred")) {
             throw new UnsupportedOperationException("Unsupported operator: " + operator + ", please use either 'eventsOccurred' or 'eventsNotOccurred'");
         }
@@ -198,7 +199,7 @@ public class PastEventConditionESQueryBuilder implements ConditionESQueryBuilder
         }
     }
 
-    protected static Condition getEventCondition(Condition condition, Map<String, Object> context, String profileId,
+    public Condition getEventCondition(Condition condition, Map<String, Object> context, String profileId,
                                                  DefinitionsService definitionsService, ScriptExecutor scriptExecutor) {
         Condition eventCondition;
         try {
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/PropertyConditionESQueryBuilder.java
similarity index 97%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/PropertyConditionESQueryBuilder.java
index d923d76ad..e4e259661 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/PropertyConditionESQueryBuilder.java
@@ -15,13 +15,13 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.common.unit.DistanceUnit;
 import org.elasticsearch.index.query.BoolQueryBuilder;
@@ -33,7 +33,7 @@ import org.joda.time.format.ISODateTimeFormat;
 
 import java.util.*;
 
-import static org.apache.unomi.plugins.baseplugin.conditions.PropertyConditionEvaluator.getDate;
+import static org.apache.unomi.persistence.spi.conditions.DateUtils.getDate;
 
 public class PropertyConditionESQueryBuilder implements ConditionESQueryBuilder {
 
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/SourceEventPropertyConditionESQueryBuilder.java b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/SourceEventPropertyConditionESQueryBuilder.java
similarity index 91%
rename from plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/SourceEventPropertyConditionESQueryBuilder.java
rename to persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/SourceEventPropertyConditionESQueryBuilder.java
index 7ba6c6f65..d763e6b03 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/SourceEventPropertyConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/conditions/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/SourceEventPropertyConditionESQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.plugins.baseplugin.conditions;
+package org.apache.unomi.persistence.elasticsearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
@@ -60,4 +60,4 @@ public class SourceEventPropertyConditionESQueryBuilder implements ConditionESQu
             return null;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/persistence-opensearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/persistence-elasticsearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
similarity index 68%
copy from persistence-opensearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
copy to persistence-elasticsearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 48e4c400b..8c38cfa96 100644
--- a/persistence-opensearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/persistence-elasticsearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -30,8 +30,8 @@
         </cm:default-properties>
     </cm:property-placeholder>
 
-    <cm:property-placeholder persistent-id="org.apache.unomi.persistence.opensearch"
-                             update-strategy="reload" placeholder-prefix="${os.">
+    <cm:property-placeholder persistent-id="org.apache.unomi.persistence.elasticsearch"
+                             update-strategy="reload" placeholder-prefix="${es.">
         <cm:default-properties>
             <cm:property name="maximumIdsQueryCount" value="5000"/>
             <cm:property name="aggregateQueryBucketSize" value="5000"/>
@@ -46,90 +46,91 @@
     <reference id="schedulerService" interface="org.apache.unomi.api.services.SchedulerService"/>
     <reference id="segmentService" interface="org.apache.unomi.api.services.SegmentService"/>
     <reference id="eventService" interface="org.apache.unomi.api.services.EventService"/>
-    <reference id="configSharingService" interface="org.apache.unomi.api.services.ConfigSharingService"/>
     <reference id="scriptExecutor" interface="org.apache.unomi.scripting.ScriptExecutor"/>
-    <reference id="topicService" interface="org.apache.unomi.api.services.TopicService"/>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="idsConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.IdsConditionOSQueryBuilder">
-            <property name="maximumIdsQueryCount" value="${os.maximumIdsQueryCount}"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.IdsConditionESQueryBuilder">
+            <property name="maximumIdsQueryCount" value="${es.maximumIdsQueryCount}"/>
         </bean>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="booleanConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.BooleanConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.BooleanConditionESQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="notConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.NotConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.NotConditionESQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="matchAllConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.MatchAllConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.MatchAllConditionESQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="propertyConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.PropertyConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.PropertyConditionESQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="geoLocationByPointSessionConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.GeoLocationByPointSessionConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.GeoLocationByPointSessionConditionESQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="sourceEventPropertyConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.SourceEventPropertyConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.SourceEventPropertyConditionESQueryBuilder"/>
     </service>
 
-    <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+    <service>
+        <interfaces>
+            <value>org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder</value>
+            <value>org.apache.unomi.persistence.spi.conditions.PastEventConditionPersistenceQueryBuilder</value>
+        </interfaces>
         <service-properties>
             <entry key="queryBuilderId" value="pastEventConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.PastEventConditionOSQueryBuilder">
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.PastEventConditionESQueryBuilder">
             <property name="definitionsService" ref="definitionsService"/>
             <property name="persistenceService" ref="persistenceService"/>
             <property name="segmentService" ref="segmentService"/>
             <property name="scriptExecutor" ref="scriptExecutor"/>
-            <property name="maximumIdsQueryCount" value="${os.maximumIdsQueryCount}"/>
-            <property name="pastEventsDisablePartitions" value="${os.pastEventsDisablePartitions}"/>
-            <property name="aggregateQueryBucketSize" value="${os.aggregateQueryBucketSize}"/>
+            <property name="maximumIdsQueryCount" value="${es.maximumIdsQueryCount}"/>
+            <property name="pastEventsDisablePartitions" value="${es.pastEventsDisablePartitions}"/>
+            <property name="aggregateQueryBucketSize" value="${es.aggregateQueryBucketSize}"/>
         </bean>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="nestedConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.NestedConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.elasticsearch.conditions.NestedConditionESQueryBuilder"/>
     </service>
 
 </blueprint>
diff --git a/persistence-elasticsearch/core/pom.xml b/persistence-elasticsearch/core/pom.xml
index ee5afc77f..21d472288 100644
--- a/persistence-elasticsearch/core/pom.xml
+++ b/persistence-elasticsearch/core/pom.xml
@@ -297,7 +297,7 @@
                             org.elasticsearch.*;version="${elasticsearch.version}",
                             org.elasticsearch.index.query.*;version="${elasticsearch.version}",
                             org.apache.lucene.search.join.*;version="${lucene.version}",
-                            org.apache.unomi.persistence.elasticsearch.conditions;version="${project.version}"
+                            org.apache.unomi.persistence.elasticsearch;version="${project.version}"
                         </Export-Package>
                         <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
                         <Embed-Transitive>true</Embed-Transitive>
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilder.java
similarity index 95%
copy from persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java
copy to persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilder.java
index 7a100d05a..b1348c3d2 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java
+++ b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.persistence.elasticsearch.conditions;
+package org.apache.unomi.persistence.elasticsearch;
 
 import org.apache.unomi.api.conditions.Condition;
 import org.elasticsearch.index.query.QueryBuilder;
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilderDispatcher.java b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilderDispatcher.java
similarity index 98%
rename from persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilderDispatcher.java
rename to persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilderDispatcher.java
index bed2cb5bc..937d642e9 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilderDispatcher.java
+++ b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilderDispatcher.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.persistence.elasticsearch.conditions;
+package org.apache.unomi.persistence.elasticsearch;
 
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
index a691cf0ae..6ee564a8b 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
+++ b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
@@ -37,7 +37,6 @@ import org.apache.unomi.api.query.IpRange;
 import org.apache.unomi.api.query.NumericRange;
 import org.apache.unomi.metrics.MetricAdapter;
 import org.apache.unomi.metrics.MetricsService;
-import org.apache.unomi.persistence.elasticsearch.conditions.*;
 import org.apache.unomi.persistence.spi.PersistenceService;
 import org.apache.unomi.persistence.spi.aggregate.*;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
@@ -67,8 +66,8 @@ import org.elasticsearch.action.search.SearchScrollRequest;
 import org.elasticsearch.action.support.WriteRequest;
 import org.elasticsearch.action.support.master.AcknowledgedResponse;
 import org.elasticsearch.action.update.UpdateRequest;
-import org.elasticsearch.client.*;
 import org.elasticsearch.action.update.UpdateResponse;
+import org.elasticsearch.client.*;
 import org.elasticsearch.client.core.CountRequest;
 import org.elasticsearch.client.core.CountResponse;
 import org.elasticsearch.client.core.MainResponse;
@@ -84,10 +83,14 @@ import org.elasticsearch.common.unit.ByteSizeUnit;
 import org.elasticsearch.common.unit.ByteSizeValue;
 import org.elasticsearch.common.unit.DistanceUnit;
 import org.elasticsearch.common.unit.TimeValue;
-import org.elasticsearch.common.xcontent.*;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.elasticsearch.common.xcontent.XContentType;
 import org.elasticsearch.index.IndexNotFoundException;
 import org.elasticsearch.index.query.*;
-import org.elasticsearch.index.reindex.*;
+import org.elasticsearch.index.reindex.AbstractBulkByScrollRequest;
+import org.elasticsearch.index.reindex.DeleteByQueryRequest;
+import org.elasticsearch.index.reindex.UpdateByQueryRequest;
 import org.elasticsearch.rest.RestStatus;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.script.ScriptException;
diff --git a/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 5dab36ff6..830a6e12d 100644
--- a/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -89,7 +89,7 @@
     </service>
 
     <bean id="conditionESQueryBuilderDispatcher"
-          class="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher">
+          class="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher">
         <property name="scriptExecutor" ref="scriptExecutor" />
     </bean>
 
@@ -169,7 +169,7 @@
     </reference-list>
 
     <reference-list id="conditionESQueryBuilders"
-                    interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder"
+                    interface="org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder"
         availability="optional">
         <reference-listener
                 bind-method="bindConditionESQueryBuilder" unbind-method="unbindConditionESQueryBuilder" ref="elasticSearchPersistenceServiceImpl"/>
diff --git a/persistence-elasticsearch/core/src/test/java/org/apache/unomi/persistence/elasticsearch/geo/DistanceUnitTest.java b/persistence-elasticsearch/core/src/test/java/org/apache/unomi/persistence/elasticsearch/geo/DistanceUnitTest.java
new file mode 100644
index 000000000..9af49c3e9
--- /dev/null
+++ b/persistence-elasticsearch/core/src/test/java/org/apache/unomi/persistence/elasticsearch/geo/DistanceUnitTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.elasticsearch.geo;
+
+import org.elasticsearch.common.unit.DistanceUnit;
+import org.junit.Test;
+
+import static org.elasticsearch.common.geo.GeoUtils.EARTH_EQUATOR;
+import static org.elasticsearch.common.geo.GeoUtils.EARTH_SEMI_MAJOR_AXIS;
+import static org.junit.Assert.assertEquals;
+
+public class DistanceUnitTest {
+
+
+    @Test
+    public void testEarthCircumference() {
+        assertEquals(EARTH_EQUATOR / DistanceUnit.KILOMETERS.toMeters(1.0), DistanceUnit.KILOMETERS.getEarthCircumference(), 0.0001);
+        assertEquals(EARTH_EQUATOR, DistanceUnit.METERS.getEarthCircumference(), 0.0001);
+    }
+
+    @Test
+    public void testEarthRadius() {
+        assertEquals(EARTH_SEMI_MAJOR_AXIS / DistanceUnit.MILES.toMeters(1.0), DistanceUnit.MILES.getEarthRadius(), 0.0001);
+        assertEquals(EARTH_SEMI_MAJOR_AXIS, DistanceUnit.METERS.getEarthRadius(), 0.0001);
+    }
+
+    @Test
+    public void testDistancePerDegree() {
+        assertEquals(EARTH_EQUATOR / 360.0, DistanceUnit.METERS.getDistancePerDegree(), 0.0001);
+        assertEquals(EARTH_EQUATOR / (360.0 * DistanceUnit.KILOMETERS.toMeters(1.0)), DistanceUnit.KILOMETERS.getDistancePerDegree(), 0.0001);
+    }
+
+    @Test
+    public void testToMeters() {
+        assertEquals(1000.0, DistanceUnit.KILOMETERS.toMeters(1.0), 0.0001);
+        assertEquals(1609.344, DistanceUnit.MILES.toMeters(1.0), 0.0001);
+        assertEquals(1.0, DistanceUnit.METERS.toMeters(1.0), 0.0001);
+    }
+
+    @Test
+    public void testFromMeters() {
+        assertEquals(1.0, DistanceUnit.KILOMETERS.fromMeters(1000.0), 0.0001);
+        assertEquals(1.0, DistanceUnit.MILES.fromMeters(1609.344), 0.0001);
+        assertEquals(1.0, DistanceUnit.METERS.fromMeters(1.0), 0.0001);
+    }
+
+    @Test
+    public void testConvert() {
+        assertEquals(1.0, DistanceUnit.convert(1.0, DistanceUnit.KILOMETERS, DistanceUnit.KILOMETERS), 0.0001);
+        assertEquals(1000.0, DistanceUnit.convert(1.0, DistanceUnit.KILOMETERS, DistanceUnit.METERS), 0.0001);
+        assertEquals(1.0, DistanceUnit.convert(1609.344, DistanceUnit.METERS, DistanceUnit.MILES), 0.0001);
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("km", DistanceUnit.KILOMETERS.toString());
+        assertEquals("mi", DistanceUnit.MILES.toString());
+    }
+
+    @Test
+    public void testParse() {
+        assertEquals(1.0, DistanceUnit.MILES.parse("1mi", DistanceUnit.METERS), 0.0001);
+        assertEquals(1609.344, DistanceUnit.METERS.parse("1mi", DistanceUnit.METERS), 0.0001);
+        assertEquals(1000.0, DistanceUnit.METERS.parse("1km", DistanceUnit.METERS), 0.0001);
+        assertEquals(1.0, DistanceUnit.KILOMETERS.parse("1km", DistanceUnit.KILOMETERS), 0.0001);
+    }
+
+    @Test
+    public void testParseUnit() {
+        assertEquals(DistanceUnit.KILOMETERS, DistanceUnit.parseUnit("1km", DistanceUnit.METERS));
+        assertEquals(DistanceUnit.MILES, DistanceUnit.parseUnit("1mi", DistanceUnit.METERS));
+        assertEquals(DistanceUnit.METERS, DistanceUnit.parseUnit("1", DistanceUnit.METERS));
+    }
+
+    @Test
+    public void testDistanceParsing() {
+        DistanceUnit.Distance distance = DistanceUnit.Distance.parseDistance("1km");
+        assertEquals(1.0, distance.value, 0.0001);
+        assertEquals(DistanceUnit.KILOMETERS, distance.unit);
+
+        DistanceUnit.Distance defaultDistance = DistanceUnit.Distance.parseDistance("100");
+        assertEquals(100.0, defaultDistance.value, 0.0001);
+        assertEquals(DistanceUnit.DEFAULT, defaultDistance.unit);
+    }
+
+    @Test
+    public void testDistanceConversion() {
+        DistanceUnit.Distance distance = new DistanceUnit.Distance(1.0, DistanceUnit.KILOMETERS);
+        DistanceUnit.Distance converted = distance.convert(DistanceUnit.METERS);
+        assertEquals(1000.0, converted.value, 0.0001);
+        assertEquals(DistanceUnit.METERS, converted.unit);
+    }
+
+    @Test
+    public void testDistanceEqualsAndHashCode() {
+        DistanceUnit.Distance d1 = new DistanceUnit.Distance(1.0, DistanceUnit.KILOMETERS);
+        DistanceUnit.Distance d2 = new DistanceUnit.Distance(1000.0, DistanceUnit.METERS);
+        assertEquals(d1, d2);
+        assertEquals(d1.hashCode(), d2.hashCode());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromStringInvalidUnit() {
+        DistanceUnit.fromString("invalid");
+    }
+
+}
diff --git a/persistence-elasticsearch/core/src/test/java/org/apache/unomi/persistence/elasticsearch/geo/GeoDistanceTest.java b/persistence-elasticsearch/core/src/test/java/org/apache/unomi/persistence/elasticsearch/geo/GeoDistanceTest.java
new file mode 100644
index 000000000..f91666c02
--- /dev/null
+++ b/persistence-elasticsearch/core/src/test/java/org/apache/unomi/persistence/elasticsearch/geo/GeoDistanceTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.elasticsearch.geo;
+
+import org.elasticsearch.common.geo.GeoDistance;
+import org.elasticsearch.common.unit.DistanceUnit;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class GeoDistanceTest {
+
+    private static final double SRC_LAT = 40.7128; // Example source latitude
+    private static final double SRC_LON = -74.0060; // Example source longitude
+    private static final double DST_LAT = 34.0522; // Example destination latitude
+    private static final double DST_LON = -118.2437; // Example destination longitude
+
+    @Test
+    public void testFromString() {
+        assertEquals(GeoDistance.PLANE, GeoDistance.fromString("plane"));
+        assertEquals(GeoDistance.ARC, GeoDistance.fromString("arc"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromStringInvalid() {
+        GeoDistance.fromString("invalid");
+    }
+
+    @Test
+    public void testCalculatePlane() {
+        // Assuming GeoUtils.planeDistance returns 3940.0 km for the given coordinates
+        double expectedDistanceInMeters = 3978199.0100920075;
+        double actualDistance = GeoDistance.PLANE.calculate(SRC_LAT, SRC_LON, DST_LAT, DST_LON, DistanceUnit.METERS);
+        assertEquals(expectedDistanceInMeters, actualDistance, 0.01);
+    }
+
+    @Test
+    public void testCalculateArc() {
+        // Assuming GeoUtils.arcDistance returns 3971.0 km for the given coordinates
+        double expectedDistanceInMeters = 3935751.673226063;
+        double actualDistance = GeoDistance.ARC.calculate(SRC_LAT, SRC_LON, DST_LAT, DST_LON, DistanceUnit.METERS);
+        assertEquals(expectedDistanceInMeters, actualDistance, 0.01);
+    }
+
+}
diff --git a/persistence-elasticsearch/pom.xml b/persistence-elasticsearch/pom.xml
index 01aa88967..b8980ec0d 100644
--- a/persistence-elasticsearch/pom.xml
+++ b/persistence-elasticsearch/pom.xml
@@ -32,6 +32,7 @@
 
     <modules>
         <module>core</module>
+        <module>conditions</module>
     </modules>
 
     <build>
diff --git a/persistence-opensearch/conditions/pom.xml b/persistence-opensearch/conditions/pom.xml
index ae970a3cf..a0364c429 100644
--- a/persistence-opensearch/conditions/pom.xml
+++ b/persistence-opensearch/conditions/pom.xml
@@ -130,13 +130,6 @@
             <version>2.12.7</version>
         </dependency>
 
-        <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-plugins-base</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
-        </dependency>
-
     </dependencies>
 
     <build>
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/BooleanConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/BooleanConditionOSQueryBuilder.java
similarity index 93%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/BooleanConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/BooleanConditionOSQueryBuilder.java
index 287391f87..034e189d4 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/BooleanConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/BooleanConditionOSQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.opensearch.client.opensearch._types.query_dsl.BoolQuery;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 import org.slf4j.Logger;
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/GeoLocationByPointSessionConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/GeoLocationByPointSessionConditionOSQueryBuilder.java
similarity index 93%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/GeoLocationByPointSessionConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/GeoLocationByPointSessionConditionOSQueryBuilder.java
index ddc61643a..aeb0a78ff 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/GeoLocationByPointSessionConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/GeoLocationByPointSessionConditionOSQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 
 import java.util.Map;
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/IdsConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/IdsConditionOSQueryBuilder.java
similarity index 89%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/IdsConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/IdsConditionOSQueryBuilder.java
index 5697e9a4f..936d6af50 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/IdsConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/IdsConditionOSQueryBuilder.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 
 import java.util.ArrayList;
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/MatchAllConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/MatchAllConditionOSQueryBuilder.java
similarity index 84%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/MatchAllConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/MatchAllConditionOSQueryBuilder.java
index 7dfdaf35a..16886cfef 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/MatchAllConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/MatchAllConditionOSQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 
 import java.util.Map;
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/NestedConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/NestedConditionOSQueryBuilder.java
similarity index 89%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/NestedConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/NestedConditionOSQueryBuilder.java
index 85b839d17..5552a323d 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/NestedConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/NestedConditionOSQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.opensearch.client.opensearch._types.query_dsl.ChildScoreMode;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/NotConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/NotConditionOSQueryBuilder.java
similarity index 85%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/NotConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/NotConditionOSQueryBuilder.java
index cb1f52187..3134a4d20 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/NotConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/NotConditionOSQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 
 import java.util.Map;
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/PastEventConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/PastEventConditionOSQueryBuilder.java
similarity index 96%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/PastEventConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/PastEventConditionOSQueryBuilder.java
index 2eaaffbd5..fb6e0d9e6 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/PastEventConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/PastEventConditionOSQueryBuilder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.Event;
 import org.apache.unomi.api.Profile;
@@ -23,18 +23,19 @@ import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.conditions.ConditionType;
 import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.api.services.SegmentService;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.apache.unomi.persistence.spi.PersistenceService;
 import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
+import org.apache.unomi.persistence.spi.conditions.PastEventConditionPersistenceQueryBuilder;
 import org.apache.unomi.scripting.ScriptExecutor;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 
 import java.util.*;
 import java.util.stream.Collectors;
 
-public class PastEventConditionOSQueryBuilder implements ConditionOSQueryBuilder {
+public class PastEventConditionOSQueryBuilder implements ConditionOSQueryBuilder, PastEventConditionPersistenceQueryBuilder {
 
     private DefinitionsService definitionsService;
     private PersistenceService persistenceService;
@@ -115,7 +116,7 @@ public class PastEventConditionOSQueryBuilder implements ConditionOSQueryBuilder
         }
     }
 
-    protected static boolean getStrategyFromOperator(String operator) {
+    public boolean getStrategyFromOperator(String operator) {
         if (operator != null && !operator.equals("eventsOccurred") && !operator.equals("eventsNotOccurred")) {
             throw new UnsupportedOperationException("Unsupported operator: " + operator + ", please use either 'eventsOccurred' or 'eventsNotOccurred'");
         }
@@ -202,7 +203,7 @@ public class PastEventConditionOSQueryBuilder implements ConditionOSQueryBuilder
         }
     }
 
-    protected static Condition getEventCondition(Condition condition, Map<String, Object> context, String profileId,
+    public Condition getEventCondition(Condition condition, Map<String, Object> context, String profileId,
                                                  DefinitionsService definitionsService, ScriptExecutor scriptExecutor) {
         Condition eventCondition;
         try {
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/PropertyConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/PropertyConditionOSQueryBuilder.java
similarity index 97%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/PropertyConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/PropertyConditionOSQueryBuilder.java
index 55b27bf8b..c67719f38 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/PropertyConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/PropertyConditionOSQueryBuilder.java
@@ -15,26 +15,26 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
 import org.joda.time.DateTime;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
 import org.opensearch.client.json.JsonData;
+import org.opensearch.client.opensearch._types.FieldValue;
 import org.opensearch.client.opensearch._types.GeoDistanceType;
 import org.opensearch.client.opensearch._types.query_dsl.BoolQuery;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
-import org.opensearch.client.opensearch._types.FieldValue;
 import org.opensearch.client.util.ObjectBuilder;
 
 import java.util.*;
 
-import static org.apache.unomi.plugins.baseplugin.conditions.PropertyConditionEvaluator.getDate;
+import static org.apache.unomi.persistence.spi.conditions.DateUtils.getDate;
 
 public class PropertyConditionOSQueryBuilder implements ConditionOSQueryBuilder {
 
diff --git a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/SourceEventPropertyConditionOSQueryBuilder.java b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/SourceEventPropertyConditionOSQueryBuilder.java
similarity index 91%
rename from persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/SourceEventPropertyConditionOSQueryBuilder.java
rename to persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/SourceEventPropertyConditionOSQueryBuilder.java
index d2745f29f..a113421a6 100644
--- a/persistence-opensearch/conditions/src/main/java/org/apache/unomi/opensearch/conditions/SourceEventPropertyConditionOSQueryBuilder.java
+++ b/persistence-opensearch/conditions/src/main/java/org/apache/unomi/persistence/opensearch/conditions/SourceEventPropertyConditionOSQueryBuilder.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder;
-import org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder;
+import org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher;
 import org.opensearch.client.opensearch._types.query_dsl.BoolQuery;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
 
diff --git a/persistence-opensearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/persistence-opensearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 48e4c400b..0cb936ac5 100644
--- a/persistence-opensearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/persistence-opensearch/conditions/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -51,69 +51,72 @@
     <reference id="topicService" interface="org.apache.unomi.api.services.TopicService"/>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="idsConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.IdsConditionOSQueryBuilder">
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.IdsConditionOSQueryBuilder">
             <property name="maximumIdsQueryCount" value="${os.maximumIdsQueryCount}"/>
         </bean>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="booleanConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.BooleanConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.BooleanConditionOSQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="notConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.NotConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.NotConditionOSQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="matchAllConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.MatchAllConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.MatchAllConditionOSQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="propertyConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.PropertyConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.PropertyConditionOSQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="geoLocationByPointSessionConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.GeoLocationByPointSessionConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.GeoLocationByPointSessionConditionOSQueryBuilder"/>
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="sourceEventPropertyConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.SourceEventPropertyConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.SourceEventPropertyConditionOSQueryBuilder"/>
     </service>
 
-    <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+    <service>
+        <interfaces>
+            <value>org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder</value>
+            <value>org.apache.unomi.persistence.spi.conditions.PastEventConditionPersistenceQueryBuilder</value>
+        </interfaces>
         <service-properties>
             <entry key="queryBuilderId" value="pastEventConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.PastEventConditionOSQueryBuilder">
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.PastEventConditionOSQueryBuilder">
             <property name="definitionsService" ref="definitionsService"/>
             <property name="persistenceService" ref="persistenceService"/>
             <property name="segmentService" ref="segmentService"/>
@@ -125,11 +128,11 @@
     </service>
 
     <service
-            interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder">
+            interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder">
         <service-properties>
             <entry key="queryBuilderId" value="nestedConditionESQueryBuilder"/>
         </service-properties>
-        <bean class="org.apache.unomi.opensearch.conditions.NestedConditionOSQueryBuilder"/>
+        <bean class="org.apache.unomi.persistence.opensearch.conditions.NestedConditionOSQueryBuilder"/>
     </service>
 
 </blueprint>
diff --git a/persistence-opensearch/core/pom.xml b/persistence-opensearch/core/pom.xml
index d950d82fc..2e0431f1c 100644
--- a/persistence-opensearch/core/pom.xml
+++ b/persistence-opensearch/core/pom.xml
@@ -156,7 +156,7 @@
                             org.opensearch.*;version="${opensearch.version}",
                             org.opensearch.index.query.*;version="${opensearch.version}",
                             org.apache.lucene.search.join.*;version="${lucene.version}",
-                            org.apache.unomi.persistence.opensearch.conditions;version="${project.version}"
+                            org.apache.unomi.persistence.opensearch;version="${project.version}"
                         </Export-Package>
                         <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
                         <Embed-Transitive>true</Embed-Transitive>
diff --git a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/conditions/ConditionOSQueryBuilder.java b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilder.java
similarity index 95%
rename from persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/conditions/ConditionOSQueryBuilder.java
rename to persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilder.java
index c9012b207..e89ce3d79 100644
--- a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/conditions/ConditionOSQueryBuilder.java
+++ b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilder.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.persistence.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch;
 
 import org.apache.unomi.api.conditions.Condition;
 import org.opensearch.client.opensearch._types.query_dsl.Query;
diff --git a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/conditions/ConditionOSQueryBuilderDispatcher.java b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilderDispatcher.java
similarity index 98%
rename from persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/conditions/ConditionOSQueryBuilderDispatcher.java
rename to persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilderDispatcher.java
index 4f645c047..b5ad16498 100644
--- a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/conditions/ConditionOSQueryBuilderDispatcher.java
+++ b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilderDispatcher.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.unomi.persistence.opensearch.conditions;
+package org.apache.unomi.persistence.opensearch;
 
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
diff --git a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/OpenSearchPersistenceServiceImpl.java b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/OpenSearchPersistenceServiceImpl.java
index 71269433c..c0b9c4736 100644
--- a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/OpenSearchPersistenceServiceImpl.java
+++ b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/OpenSearchPersistenceServiceImpl.java
@@ -37,12 +37,11 @@ import org.apache.unomi.api.query.IpRange;
 import org.apache.unomi.api.query.NumericRange;
 import org.apache.unomi.metrics.MetricAdapter;
 import org.apache.unomi.metrics.MetricsService;
-import org.apache.unomi.persistence.opensearch.conditions.*;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
 import org.apache.unomi.persistence.spi.PersistenceService;
-import org.apache.unomi.persistence.spi.aggregate.*;
 import org.apache.unomi.persistence.spi.aggregate.DateRangeAggregate;
 import org.apache.unomi.persistence.spi.aggregate.IpRangeAggregate;
+import org.apache.unomi.persistence.spi.aggregate.*;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluator;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluatorDispatcher;
diff --git a/persistence-opensearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/persistence-opensearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 2536f9e10..72b0a4b9c 100644
--- a/persistence-opensearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/persistence-opensearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -94,7 +94,7 @@
     </service>
 
     <bean id="conditionOSQueryBuilderDispatcher"
-          class="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilderDispatcher">
+          class="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilderDispatcher">
         <property name="scriptExecutor" ref="scriptExecutor" />
     </bean>
 
@@ -162,7 +162,7 @@
 
     <!-- We use a listener here because using the list directly for listening to proxies coming from the same bundle didn't seem to work -->
     <reference-list id="conditionOSQueryBuilders"
-                    interface="org.apache.unomi.persistence.opensearch.conditions.ConditionOSQueryBuilder"
+                    interface="org.apache.unomi.persistence.opensearch.ConditionOSQueryBuilder"
         availability="optional">
         <reference-listener
                 bind-method="bindConditionOSQueryBuilder" unbind-method="unbindConditionOSQueryBuilder" ref="openSearchPersistenceServiceImpl"/>
diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/DateUtils.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/DateUtils.java
new file mode 100644
index 000000000..eac3e358b
--- /dev/null
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/DateUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions;
+
+import org.apache.unomi.persistence.spi.conditions.datemath.DateMathParseException;
+import org.apache.unomi.persistence.spi.conditions.datemath.DateMathParser;
+import org.apache.unomi.persistence.spi.conditions.datemath.JavaDateFormatter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+public class DateUtils {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DateUtils.class.getName());
+
+    public static Date getDate(Object value) {
+        if (value == null) {
+            return null;
+        }
+        if (value instanceof Date) {
+            return (Date) value;
+        } else {
+            JavaDateFormatter formatter = new JavaDateFormatter("strict_date_optional_time||epoch_millis");
+            DateMathParser dateMathParser = new DateMathParser(formatter, DateTimeFormatter.ISO_DATE_TIME);
+            try {
+                Instant instant = dateMathParser.parse(value.toString(), System::currentTimeMillis, false, ZoneOffset.UTC);
+                return Date.from(instant);
+            } catch (DateMathParseException e) {
+                LOGGER.warn("unable to parse date. See debug log level for full stacktrace");
+                LOGGER.debug("unable to parse date {}", value, e);
+            }
+            return null;
+        }
+    }
+
+}
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
similarity index 64%
copy from persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java
copy to persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
index 7a100d05a..909c1dafe 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
@@ -14,19 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.unomi.persistence.elasticsearch.conditions;
+package org.apache.unomi.persistence.spi.conditions;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.elasticsearch.index.query.QueryBuilder;
+import org.apache.unomi.api.services.DefinitionsService;
+import org.apache.unomi.scripting.ScriptExecutor;
 
 import java.util.Map;
 
-public interface ConditionESQueryBuilder {
+public interface PastEventConditionPersistenceQueryBuilder {
 
-    QueryBuilder buildQuery(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher);
+    boolean getStrategyFromOperator(String operator);
 
-    default long count(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) {
-        throw new UnsupportedOperationException();
-    }
+    Condition getEventCondition(Condition condition, Map<String, Object> context, String profileId,
+                                                 DefinitionsService definitionsService, ScriptExecutor scriptExecutor);
 }
diff --git a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParseException.java
similarity index 54%
rename from persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java
rename to persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParseException.java
index 7a100d05a..60f5af9be 100644
--- a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionESQueryBuilder.java
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParseException.java
@@ -14,19 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.unomi.persistence.spi.conditions.datemath;
 
-package org.apache.unomi.persistence.elasticsearch.conditions;
-
-import org.apache.unomi.api.conditions.Condition;
-import org.elasticsearch.index.query.QueryBuilder;
-
-import java.util.Map;
+/**
+ * Exception thrown by the {@link DateMathParser} when a malformed date math expression is encountered.
+ */
+public class DateMathParseException extends RuntimeException {
+    public DateMathParseException(String message) {
+        super(message);
+    }
 
-public interface ConditionESQueryBuilder {
+    public DateMathParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
 
-    QueryBuilder buildQuery(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher);
+    public DateMathParseException(String message, Object... args) {
+        super(String.format(message, args));
+    }
 
-    default long count(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) {
-        throw new UnsupportedOperationException();
+    public DateMathParseException(String message, Throwable cause, Object... args) {
+        super(String.format(message, args), cause);
     }
 }
diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParser.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParser.java
new file mode 100644
index 000000000..95feb6994
--- /dev/null
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParser.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.datemath;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalAdjusters;
+import java.time.temporal.TemporalQueries;
+import java.util.function.Function;
+import java.util.function.LongSupplier;
+
+public class DateMathParser {
+
+    public static boolean isNullOrEmpty(CharSequence cs) {
+        return cs == null || cs.length() == 0;
+    }
+
+    public static class DateFormatters {
+        public static ZonedDateTime from(TemporalAccessor accessor) {
+            // Default to UTC if no zone or offset is present
+            ZoneId zone = accessor.query(TemporalQueries.zone());
+            if (zone == null) {
+                ZoneOffset offset = accessor.query(TemporalQueries.offset());
+                zone = (offset != null) ? offset : ZoneOffset.UTC;
+            }
+
+            // If the accessor supports INSTANT_SECONDS, construct from Instant
+            if (accessor.isSupported(ChronoField.INSTANT_SECONDS)) {
+                return ZonedDateTime.ofInstant(Instant.from(accessor), zone);
+            }
+
+            // Handle LocalDate and LocalTime explicitly
+            LocalDate date = accessor.query(TemporalQueries.localDate());
+            LocalTime time = accessor.query(TemporalQueries.localTime());
+
+            // Ensure missing components are handled gracefully
+            if (date == null && time == null) {
+                throw new DateTimeException("Cannot extract LocalDate or LocalTime from TemporalAccessor");
+            }
+
+            if (date == null) {
+                date = LocalDate.ofEpochDay(0); // Default to 1970-01-01
+            }
+
+            if (time == null) {
+                time = LocalTime.MIDNIGHT; // Default to 00:00
+            }
+
+            // Combine LocalDate and LocalTime with ZoneId
+            return ZonedDateTime.of(date, time, zone);
+        }
+    }
+
+    private final JavaDateFormatter formatter;
+    private final DateTimeFormatter roundUpFormatter;
+
+    public DateMathParser(JavaDateFormatter formatter, DateTimeFormatter roundUpFormatter) {
+        this.formatter = formatter;
+        this.roundUpFormatter = roundUpFormatter;
+    }
+
+    public Instant parse(String text, LongSupplier now, boolean roundUpProperty, ZoneId timeZone) {
+        Instant time;
+        String mathString;
+        if (text.startsWith("now")) {
+            try {
+                time = Instant.ofEpochMilli(now.getAsLong());
+            } catch (Exception e) {
+                throw new DateMathParseException("could not read the current timestamp", e);
+            }
+            mathString = text.substring("now".length());
+        } else {
+            int index = text.indexOf("||");
+            if (index == -1) {
+                // no math, just parse date
+                return parseDateTime(text, timeZone, roundUpProperty);
+            }
+            time = parseDateTime(text.substring(0, index), timeZone, false);
+            mathString = text.substring(index + 2);
+        }
+
+        return parseMath(mathString, time, roundUpProperty, timeZone);
+    }
+
+    private Instant parseMath(final String mathString, final Instant time, final boolean roundUpProperty,
+                              ZoneId timeZone) throws DateMathParseException {
+        if (timeZone == null) {
+            timeZone = ZoneOffset.UTC;
+        }
+        ZonedDateTime dateTime = ZonedDateTime.ofInstant(time, timeZone);
+        int i = 0;
+        while (i < mathString.length()) {
+            char c = mathString.charAt(i++);
+            final boolean round;
+            final int sign;
+            if (c == '/') {
+                round = true;
+                sign = 1;
+            } else {
+                round = false;
+                if (c == '+') {
+                    sign = 1;
+                } else if (c == '-') {
+                    sign = -1;
+                } else {
+                    throw new DateMathParseException("operator not supported for date math [%s]", mathString);
+                }
+            }
+
+            if (i >= mathString.length()) {
+                throw new DateMathParseException("truncated date math [%s]", mathString);
+            }
+
+            final int num;
+            int numStart = i;
+            if (!Character.isDigit(mathString.charAt(i))) {
+                num = 1;
+            } else {
+                while (i < mathString.length() && Character.isDigit(mathString.charAt(i))) {
+                    i++;
+                }
+                if (i >= mathString.length()) {
+                    throw new DateMathParseException("truncated date math [%s]", mathString);
+                }
+                num = Integer.parseInt(mathString.substring(numStart, i));
+            }
+            if (round && num != 1) {
+                throw new DateMathParseException("rounding `/` can only be used on single unit types [%s]", mathString);
+            }
+            char unit = mathString.charAt(i++);
+            switch (unit) {
+                case 'y':
+                    if (round) {
+                        dateTime = dateTime.withDayOfYear(1).with(LocalTime.MIN);
+                        if (roundUpProperty) {
+                            dateTime = dateTime.plusYears(1);
+                        }
+                    } else {
+                        dateTime = dateTime.plusYears(sign * num);
+                    }
+                    break;
+                case 'M':
+                    if (round) {
+                        dateTime = dateTime.withDayOfMonth(1).with(LocalTime.MIN);
+                        if (roundUpProperty) {
+                            dateTime = dateTime.plusMonths(1);
+                        }
+                    } else {
+                        dateTime = dateTime.plusMonths(sign * num);
+                    }
+                    break;
+                case 'w':
+                    if (round) {
+                        dateTime = dateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).with(LocalTime.MIN);
+                        if (roundUpProperty) {
+                            dateTime = dateTime.plusWeeks(1);
+                        }
+                    } else {
+                        dateTime = dateTime.plusWeeks(sign * num);
+                    }
+                    break;
+                case 'd':
+                    if (round) {
+                        dateTime = dateTime.with(LocalTime.MIN);
+                        if (roundUpProperty) {
+                            dateTime = dateTime.plusDays(1);
+                        }
+                    } else {
+                        dateTime = dateTime.plusDays(sign * num);
+                    }
+                    break;
+                case 'h':
+                case 'H':
+                    if (round) {
+                        dateTime = dateTime.withMinute(0).withSecond(0).withNano(0);
+                        if (roundUpProperty) {
+                            dateTime = dateTime.plusHours(1);
+                        }
+                    } else {
+                        dateTime = dateTime.plusHours(sign * num);
+                    }
+                    break;
+                case 'm':
+                    if (round) {
+                        dateTime = dateTime.withSecond(0).withNano(0);
+                        if (roundUpProperty) {
+                            dateTime = dateTime.plusMinutes(1);
+                        }
+                    } else {
+                        dateTime = dateTime.plusMinutes(sign * num);
+                    }
+                    break;
+                case 's':
+                    if (round) {
+                        dateTime = dateTime.withNano(0);
+                        if (roundUpProperty) {
+                            dateTime = dateTime.plusSeconds(1);
+                        }
+                    } else {
+                        dateTime = dateTime.plusSeconds(sign * num);
+                    }
+                    break;
+                default:
+                    // Adjust error message to remove the operator sign from the substring
+                    // We know substring from numStart to current i is the "1X" part
+                    // Operator was c, num was parsed, unit is unit
+                    String unitString = mathString.substring(numStart, numStart + Integer.toString(num).length()) + unit;
+                    throw new DateMathParseException("unit [%s] not supported for date math [%s]", unit, unitString);
+            }
+            if (round && roundUpProperty) {
+                // subtract 1 millisecond
+                dateTime = dateTime.minus(1, ChronoField.MILLI_OF_SECOND.getBaseUnit());
+            }
+        }
+        return dateTime.toInstant();
+    }
+
+    private Instant parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNoTime) {
+        if (isNullOrEmpty(value)) {
+            throw new DateMathParseException("cannot parse empty date");
+        }
+
+        Function<String, TemporalAccessor> parser = roundUpIfNoTime ? roundUpFormatter::parse : formatter::parse;
+        try {
+            TemporalAccessor accessor = parser.apply(value);
+
+            // Convert to ZonedDateTime from accessor
+            ZonedDateTime zdt = DateFormatters.from(accessor);
+            if (timeZone != null) {
+                // Convert to the same instant in the given timeZone
+                zdt = zdt.withZoneSameInstant(timeZone);
+            }
+            return zdt.toInstant();
+        } catch (Throwable t) {
+            throw new DateMathParseException(
+                    "failed to parse date field [%s] with format [%s]: [%s]",
+                    value, formatter, t.getMessage());
+        }
+    }
+
+}
diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/JavaDateFormatter.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/JavaDateFormatter.java
new file mode 100644
index 000000000..ab2616bf1
--- /dev/null
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/datemath/JavaDateFormatter.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.datemath;
+
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalQueries;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JavaDateFormatter {
+    private final List<FormatDefinition> formats;
+    private final boolean allowEpochMillis;
+    private final boolean allowEpochSecond;
+
+    public JavaDateFormatter(String formatString) {
+        this.formats = new ArrayList<>();
+        boolean epochMillis = false;
+        boolean epochSecond = false;
+
+        String[] formatParts = formatString.split("\\|\\|");
+        for (String f : formatParts) {
+            f = f.trim();
+            if (f.equals("epoch_millis")) {
+                formats.add(new FormatDefinition("epoch_millis", f, null));
+                epochMillis = true;
+            } else if (f.equals("epoch_second")) {
+                formats.add(new FormatDefinition("epoch_second", f, null));
+                epochSecond = true;
+            } else {
+                formats.add(createFormatDefinition(f));
+            }
+        }
+
+        this.allowEpochMillis = epochMillis;
+        this.allowEpochSecond = epochSecond;
+    }
+
+    public TemporalAccessor parse(String input) {
+        // Numeric check
+        if (isNumeric(input)) {
+            long value = Long.parseLong(input);
+            if (allowEpochMillis && containsFormatName("epoch_millis")) {
+                return Instant.ofEpochMilli(value);
+            }
+            if (allowEpochSecond && containsFormatName("epoch_second")) {
+                return Instant.ofEpochSecond(value);
+            }
+        }
+
+        for (FormatDefinition def : formats) {
+            try {
+                String adjusted = adjustForPattern(input, def.pattern);
+                TemporalAccessor ta = def.formatter.parse(adjusted);
+                return toInstant(ta, def.formatter.getZone() != null ? def.formatter.getZone() : ZoneOffset.UTC);
+            } catch (Throwable t) {
+                // try next
+            }
+        }
+
+        throw new DateMathParseException("failed to parse date field [" + input + "] with provided formats");
+    }
+
+    private Instant toInstant(TemporalAccessor ta, ZoneId zone) {
+        boolean hasYear = ta.isSupported(ChronoField.YEAR);
+        boolean hasMonth = ta.isSupported(ChronoField.MONTH_OF_YEAR);
+        boolean hasDay = ta.isSupported(ChronoField.DAY_OF_MONTH);
+        boolean hasDoY = ta.isSupported(ChronoField.DAY_OF_YEAR);
+        boolean hasHour = ta.isSupported(ChronoField.HOUR_OF_DAY);
+        boolean hasMinute = ta.isSupported(ChronoField.MINUTE_OF_HOUR);
+        boolean hasSecond = ta.isSupported(ChronoField.SECOND_OF_MINUTE);
+
+        LocalDate date;
+        if (hasYear && hasMonth && hasDay) {
+            // Normal date
+            date = LocalDate.from(ta);
+        } else if (hasYear && hasDoY) {
+            // Ordinal date: year + dayOfYear
+            int year = ta.get(ChronoField.YEAR);
+            int dayOfYear = ta.get(ChronoField.DAY_OF_YEAR);
+            date = LocalDate.ofYearDay(year, dayOfYear);
+        } else if (!hasYear && (hasHour || hasMinute || hasSecond)) {
+            // Time only → 1970-01-01
+            date = LocalDate.ofEpochDay(0);
+        } else if (hasYear && !hasMonth && !hasDay && !hasDoY) {
+            // Year only → yyyy means yyyy-01-01
+            int year = ta.get(ChronoField.YEAR);
+            date = LocalDate.of(year, 1, 1);
+        } else {
+            // Maybe week-based fields or partial fields:
+            // Attempt ZonedDateTime directly if possible
+            try {
+                return ZonedDateTime.from(ta).toInstant();
+            } catch (DateTimeException e) {
+                // If fails, handle week-based or incomplete dates
+                if (ta.isSupported(ChronoField.YEAR_OF_ERA)) {
+                    throw new DateMathParseException("Week-based date formats need additional logic.");
+                }
+                // Default to epoch day
+                date = LocalDate.ofEpochDay(0);
+            }
+        }
+
+        int hour = hasHour ? ta.get(ChronoField.HOUR_OF_DAY) : 0;
+        int minute = hasMinute ? ta.get(ChronoField.MINUTE_OF_HOUR) : 0;
+        int second = hasSecond ? ta.get(ChronoField.SECOND_OF_MINUTE) : 0;
+        int nano = ta.isSupported(ChronoField.NANO_OF_SECOND) ? ta.get(ChronoField.NANO_OF_SECOND) : 0;
+
+        LocalTime time = LocalTime.of(hour, minute, second, nano);
+
+        // Handle zone and offset explicitly
+        ZoneOffset offset = ta.query(TemporalQueries.offset());
+        if (offset != null) {
+            return ZonedDateTime.of(date, time, offset).toInstant();
+        }
+
+        // Fall back to provided zone
+        return ZonedDateTime.of(date, time, zone).toInstant();
+    }
+
+    private String adjustForPattern(String input, String pattern) {
+        // If pattern is strict_date_* and only date is given, append midnight
+        if (pattern.contains("strict_date") && input.matches("^\\d{4}-\\d{2}-\\d{2}$")) {
+            return input + "T00:00:00Z";
+        }
+        return input;
+    }
+
+    private boolean isNumeric(String s) {
+        if (s.isEmpty()) return false;
+        for (char c : s.toCharArray()) {
+            if (!Character.isDigit(c)) return false;
+        }
+        return true;
+    }
+
+    private boolean containsFormatName(String name) {
+        return formats.stream().anyMatch(def -> def.name.equals(name));
+    }
+
+    private FormatDefinition createFormatDefinition(String f) {
+        // Known patterns from documentation:
+        // We'll define exact patterns for each built-in format:
+        switch (f) {
+            // Already handled: epoch_millis, epoch_second
+            case "strict_date_optional_time":
+            case "date_optional_time":
+                // yyyy-MM-dd or yyyy-MM-dd'T'HH:mm:ss.SSSX
+                return fmt(f, "yyyy-MM-dd['T'HH:mm:ss[.SSS][XXX]]");
+
+            case "strict_date_optional_time_nanos":
+                // Nanosecond resolution: allow up to 9 fractional digits: .SSSSSSSSS
+                return fmt(f, "yyyy-MM-dd['T'HH:mm:ss[.SSSSSSSSS]][X]");
+
+            case "basic_date":
+                return fmt(f, "yyyyMMdd");
+            case "basic_date_time":
+                return fmt(f, "yyyyMMdd'T'HHmmss.SSSX");
+            case "basic_date_time_no_millis":
+                return fmt(f, "yyyyMMdd'T'HHmmssX");
+            case "basic_ordinal_date":
+                return fmt(f, "yyyyDDD");
+            case "basic_ordinal_date_time":
+                return fmt(f, "yyyyDDD'T'HHmmss.SSSX");
+            case "basic_ordinal_date_time_no_millis":
+                return fmt(f, "yyyyDDD'T'HHmmssX");
+            case "basic_time":
+                return fmt(f, "HHmmss.SSSX");
+            case "basic_time_no_millis":
+                return fmt(f, "HHmmssX");
+            case "basic_t_time":
+                return fmt(f, "'T'HHmmss.SSSX");
+            case "basic_t_time_no_millis":
+                return fmt(f, "'T'HHmmssX");
+
+            // Week-based formats:
+            // Week dates require 'YYYY' for weekyear, 'ww' for week of year, and 'e' for day of week.
+            // Example: basic_week_date: xxxx'W'wwe
+            // We'll assume ISO week date parsing works with pattern:
+            case "basic_week_date":
+            case "strict_basic_week_date":
+                return fmt(f, "xxxx'W'wwe");
+            case "basic_week_date_time":
+            case "strict_basic_week_date_time":
+                return fmt(f, "xxxx'W'wwe'T'HHmmss.SSSX");
+            case "basic_week_date_time_no_millis":
+            case "strict_basic_week_date_time_no_millis":
+                return fmt(f, "xxxx'W'wwe'T'HHmmssX");
+
+            case "date":
+            case "strict_date":
+                return fmt(f, "yyyy-MM-dd");
+            case "date_hour":
+            case "strict_date_hour":
+                return fmt(f, "yyyy-MM-dd'T'HH");
+            case "date_hour_minute":
+            case "strict_date_hour_minute":
+                return fmt(f, "yyyy-MM-dd'T'HH:mm");
+            case "date_hour_minute_second":
+            case "strict_date_hour_minute_second":
+                return fmt(f, "yyyy-MM-dd'T'HH:mm:ss");
+            case "date_hour_minute_second_fraction":
+            case "strict_date_hour_minute_second_fraction":
+                return fmt(f, "yyyy-MM-dd'T'HH:mm:ss.SSS");
+            case "date_hour_minute_second_millis":
+            case "strict_date_hour_minute_second_millis":
+                // same as fraction?
+                return fmt(f, "yyyy-MM-dd'T'HH:mm:ss.SSS");
+            case "date_time":
+            case "strict_date_time":
+                return fmt(f, "yyyy-MM-dd'T'HH:mm:ss.SSSX");
+            case "date_time_no_millis":
+            case "strict_date_time_no_millis":
+                return fmt(f, "yyyy-MM-dd'T'HH:mm:ssXXX");
+
+            case "hour":
+            case "strict_hour":
+                return fmt(f, "HH");
+            case "hour_minute":
+            case "strict_hour_minute":
+                return fmt(f, "HH:mm");
+            case "hour_minute_second":
+            case "strict_hour_minute_second":
+                return fmt(f, "HH:mm:ss");
+            case "hour_minute_second_fraction":
+            case "strict_hour_minute_second_fraction":
+                return fmt(f, "HH:mm:ss.SSS");
+            case "hour_minute_second_millis":
+            case "strict_hour_minute_second_millis":
+                return fmt(f, "HH:mm:ss.SSS");
+            case "ordinal_date":
+            case "strict_ordinal_date":
+                return fmt(f, "yyyy-DDD");
+            case "ordinal_date_time":
+            case "strict_ordinal_date_time":
+                return fmt(f, "yyyy-DDD'T'HH:mm:ss.SSSX");
+            case "ordinal_date_time_no_millis":
+            case "strict_ordinal_date_time_no_millis":
+                return fmt(f, "yyyy-DDD'T'HH:mm:ssX");
+            case "time":
+            case "strict_time":
+                return fmt(f, "HH:mm:ss.SSSX");
+            case "time_no_millis":
+            case "strict_time_no_millis":
+                return fmt(f, "HH:mm:ssX");
+            case "t_time":
+            case "strict_t_time":
+                return fmt(f, "'T'HH:mm:ss.SSSX");
+            case "t_time_no_millis":
+            case "strict_t_time_no_millis":
+                return fmt(f, "'T'HH:mm:ssX");
+            case "week_date":
+            case "strict_week_date":
+                return fmt(f, "YYYY-'W'ww-e");
+            case "week_date_time":
+            case "strict_week_date_time":
+                return fmt(f, "YYYY-'W'ww-e'T'HH:mm:ss.SSSX");
+            case "week_date_time_no_millis":
+            case "strict_week_date_time_no_millis":
+                return fmt(f, "YYYY-'W'ww-e'T'HH:mm:ssX");
+            case "weekyear":
+            case "strict_weekyear":
+                return fmt(f, "YYYY");
+            case "weekyear_week":
+            case "strict_weekyear_week":
+                return fmt(f, "YYYY-'W'ww");
+            case "weekyear_week_day":
+            case "strict_weekyear_week_day":
+                return fmt(f, "YYYY-'W'ww-e");
+            case "year":
+            case "strict_year":
+                return fmt(f, "yyyy");
+            case "year_month":
+            case "strict_year_month":
+                return fmt(f, "yyyy-MM");
+            case "year_month_day":
+            case "strict_year_month_day":
+                return fmt(f, "yyyy-MM-dd");
+
+            default:
+                // Custom pattern
+                return fmt(f, f);
+        }
+    }
+
+    private FormatDefinition fmt(String name, String pattern) {
+        // Apply UTC zone to all and consider using strict resolver if needed
+        DateTimeFormatter dtf = new DateTimeFormatterBuilder()
+                .appendPattern(pattern)
+                .toFormatter()
+                .withZone(ZoneOffset.UTC);
+        return new FormatDefinition(name, pattern, dtf);
+    }
+
+    public static class FormatDefinition {
+        final String name;
+        final String pattern;
+        final DateTimeFormatter formatter;
+
+        public FormatDefinition(String name, String pattern, DateTimeFormatter formatter) {
+            this.name = name;
+            this.pattern = pattern;
+            this.formatter = formatter;
+        }
+
+        @Override
+        public String toString() {
+            return "FormatDefinition{" +
+                    "name='" + name + '\'' +
+                    ", pattern='" + pattern + '\'' +
+                    '}';
+        }
+    }
+
+
+    @Override
+    public String toString() {
+        return "JavaDateFormatter{" +
+                "formats=" + formats +
+                '}';
+    }
+}
diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/geo/DistanceUnit.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/geo/DistanceUnit.java
new file mode 100644
index 000000000..292cbd383
--- /dev/null
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/geo/DistanceUnit.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.geo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum DistanceUnit {
+    KILOMETERS(1000.0, "km", "kilometers"),
+    MILES(1609.344, "mi", "miles"),
+    YARDS(0.9144, "yd", "yards"),
+    FEET(0.3048, "ft", "feet"),
+    INCHES(0.0254, "in", "inches"),
+    NAUTICAL_MILES(1852.0, "NM", "nauticalmiles"),
+    METERS(1.0, "m", "meters");
+
+    // Constants for Earth's properties
+    private static final double EARTH_SEMI_MAJOR_AXIS = 6378137.0; // in meters
+    private static final double EARTH_EQUATOR = 2*Math.PI * EARTH_SEMI_MAJOR_AXIS; // in meters
+
+    private static final Map<String, DistanceUnit> UNIT_MAP = new HashMap<>();
+
+    public static final DistanceUnit DEFAULT = METERS;
+
+    static {
+        for (DistanceUnit unit : values()) {
+            for (String alias : unit.aliases) {
+                UNIT_MAP.put(alias.toLowerCase(), unit);
+            }
+        }
+    }
+
+    private final double metersPerUnit;
+    private final String[] aliases;
+
+    DistanceUnit(double metersPerUnit, String... aliases) {
+        this.metersPerUnit = metersPerUnit;
+        this.aliases = aliases;
+    }
+
+    public double getEarthCircumference() {
+        return EARTH_EQUATOR / metersPerUnit;
+    }
+
+    public double getEarthRadius() {
+        return EARTH_SEMI_MAJOR_AXIS / metersPerUnit;
+    }
+
+    public double getDistancePerDegree() {
+        return EARTH_EQUATOR / (360.0 * metersPerUnit);
+    }
+
+    public double toMeters(double value) {
+        return value * metersPerUnit;
+    }
+
+    public double fromMeters(double value) {
+        return value / metersPerUnit;
+    }
+
+    public double convert(double value, DistanceUnit toUnit) {
+        return (value * metersPerUnit) / toUnit.metersPerUnit;
+    }
+
+    public static double convert(double value, DistanceUnit from, DistanceUnit to) {
+        return (value * from.metersPerUnit) / to.metersPerUnit;
+    }
+
+    public static DistanceUnit fromString(String unit) {
+        if (unit == null || unit.isEmpty()) {
+            throw new IllegalArgumentException("Unit string must not be null or empty");
+        }
+        DistanceUnit distanceUnit = UNIT_MAP.get(unit.toLowerCase());
+        if (distanceUnit == null) {
+            throw new IllegalArgumentException("Unknown distance unit: " + unit);
+        }
+        return distanceUnit;
+    }
+
+    public static DistanceUnit parseUnit(String distance, DistanceUnit defaultUnit) {
+        for (DistanceUnit unit : values()) {
+            for (String alias : unit.aliases) {
+                if (distance.endsWith(alias)) {
+                    return unit;
+                }
+            }
+        }
+        return defaultUnit;
+    }
+
+    public double parse(String distance, DistanceUnit defaultUnit) {
+        Distance parsed = Distance.parseDistance(distance, defaultUnit);
+        return convert(parsed.value, parsed.unit, this);
+    }
+
+    @Override
+    public String toString() {
+        return aliases[0];
+    }
+
+    public static class Distance {
+        public final double value;
+        public final DistanceUnit unit;
+
+        public Distance(double value, DistanceUnit unit) {
+            this.value = value;
+            this.unit = unit;
+        }
+
+        public Distance convert(DistanceUnit toUnit) {
+            double convertedValue = DistanceUnit.convert(value, unit, toUnit);
+            return new Distance(convertedValue, toUnit);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null || getClass() != obj.getClass()) return false;
+            Distance other = (Distance) obj;
+            return Double.compare(DistanceUnit.convert(value, unit, other.unit), other.value) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Double.hashCode(value * unit.metersPerUnit);
+        }
+
+        @Override
+        public String toString() {
+            return value + " " + unit.toString();
+        }
+
+        public static Distance parseDistance(String distance) {
+            return parseDistance(distance, DistanceUnit.METERS);
+        }
+
+        public static Distance parseDistance(String distance, DistanceUnit defaultUnit) {
+            for (DistanceUnit unit : values()) {
+                for (String alias : unit.aliases) {
+                    if (distance.endsWith(alias)) {
+                        String valuePart = distance.substring(0, distance.length() - alias.length()).trim();
+                        return new Distance(Double.parseDouble(valuePart), unit);
+                    }
+                }
+            }
+            return new Distance(Double.parseDouble(distance), defaultUnit);
+        }
+    }
+}
diff --git a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/geo/GeoDistance.java b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/geo/GeoDistance.java
new file mode 100644
index 000000000..117671c02
--- /dev/null
+++ b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/geo/GeoDistance.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.geo;
+
+public enum GeoDistance {
+    HAVERSINE {
+        @Override
+        public double calculate(double lat1, double lon1, double lat2, double lon2, DistanceUnit unit) {
+            double latRad1 = toRadians(lat1);
+            double latRad2 = toRadians(lat2);
+            double deltaLat = toRadians(lat2 - lat1);
+            double deltaLon = toRadians(lon2 - lon1);
+
+            double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
+                    + Math.cos(latRad1) * Math.cos(latRad2)
+                    * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
+
+            double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+            return unit.convert(EARTH_MEAN_RADIUS * c, DistanceUnit.METERS);
+        }
+    },
+    ARC {
+        @Override
+        public double calculate(double lat1, double lon1, double lat2, double lon2, DistanceUnit unit) {
+            double latRad1 = toRadians(lat1);
+            double lonRad1 = toRadians(lon1);
+            double latRad2 = toRadians(lat2);
+            double lonRad2 = toRadians(lon2);
+
+            double deltaLon = lonRad2 - lonRad1;
+
+            double cosTheta = Math.sin(latRad1) * Math.sin(latRad2)
+                    + Math.cos(latRad1) * Math.cos(latRad2) * Math.cos(deltaLon);
+
+            double theta = Math.acos(Math.min(1.0, Math.max(-1.0, cosTheta))); // Clamp to avoid NaN
+            return unit.convert(EARTH_MEAN_RADIUS * theta, DistanceUnit.METERS);
+        }
+    },
+    PLANE {
+        @Override
+        public double calculate(double lat1, double lon1, double lat2, double lon2, DistanceUnit unit) {
+            double x = toRadians(lon2 - lon1) * Math.cos(toRadians((lat1 + lat2) / 2));
+            double y = toRadians(lat2 - lat1);
+            double distance = Math.sqrt(x * x + y * y) * EARTH_MEAN_RADIUS;
+            return unit.convert(distance, DistanceUnit.METERS);
+        }
+    };
+
+    public static final double EARTH_MEAN_RADIUS = 6371008.7714D;      // meters (WGS 84)
+
+    public static final double TO_RADIANS = Math.PI / 180D;
+    public static final double TO_DEGREES = 180D / Math.PI;
+
+    public static double toRadians(double degrees) {
+        return degrees * TO_RADIANS;
+    }
+
+    /**
+     * Calculate the distance between two geographic points and return it in the specified unit.
+     *
+     * @param lat1 Latitude of the first point.
+     * @param lon1 Longitude of the first point.
+     * @param lat2 Latitude of the second point.
+     * @param lon2 Longitude of the second point.
+     * @param unit The distance unit for the result.
+     * @return The calculated distance in the specified unit.
+     */
+    public abstract double calculate(double lat1, double lon1, double lat2, double lon2, DistanceUnit unit);
+
+    /**
+     * Converts a string representation of a GeoDistance method to its corresponding enum.
+     *
+     * @param name The string representation of the GeoDistance method (e.g., "plane" or "arc").
+     * @return The corresponding GeoDistance enum.
+     * @throws IllegalArgumentException If the name does not match any known GeoDistance methods.
+     */
+    public static GeoDistance fromString(String name) {
+        if ("plane".equalsIgnoreCase(name)) {
+            return PLANE;
+        } else if ("arc".equalsIgnoreCase(name)) {
+            return ARC;
+        } else if ("haversine".equalsIgnoreCase(name)) {
+            return HAVERSINE;
+        } else {
+            throw new IllegalArgumentException("Unknown GeoDistance method: " + name);
+        }
+    }
+}
diff --git a/persistence-spi/src/test/java/org/apache/unomi/persistence/PropertyHelperTest.java b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/PropertyHelperTest.java
similarity index 98%
rename from persistence-spi/src/test/java/org/apache/unomi/persistence/PropertyHelperTest.java
rename to persistence-spi/src/test/java/org/apache/unomi/persistence/spi/PropertyHelperTest.java
index 3ee01b395..8be1a6285 100644
--- a/persistence-spi/src/test/java/org/apache/unomi/persistence/PropertyHelperTest.java
+++ b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/PropertyHelperTest.java
@@ -14,10 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.unomi.persistence;
+package org.apache.unomi.persistence.spi;
 
 import org.apache.unomi.api.Profile;
-import org.apache.unomi.persistence.spi.PropertyHelper;
 import org.junit.Test;
 
 import java.util.Arrays;
diff --git a/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParserTest.java b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParserTest.java
new file mode 100644
index 000000000..7882a3bff
--- /dev/null
+++ b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/datemath/DateMathParserTest.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.datemath;
+
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.function.LongSupplier;
+
+import static org.junit.Assert.*;
+
+public class DateMathParserTest {
+
+    // Create the JavaDateFormatter with epoch millis support
+    JavaDateFormatter formatter = new JavaDateFormatter("strict_date_optional_time||epoch_millis");
+
+    // Round up formatter can be the same or similar:
+    DateTimeFormatter roundUpFormatter = DateTimeFormatter.ISO_DATE_TIME;
+
+    DateMathParser parser = new DateMathParser(formatter, roundUpFormatter);
+
+    // A fixed "now" supplier returning a fixed timestamp: 2001-01-01T12:00:00Z in epoch millis
+    private final LongSupplier fixedNow = () -> ZonedDateTime.of(2001, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC).toInstant().toEpochMilli();
+
+    @Test
+    public void testNowWithFixedSupplier() {
+        Instant parsed = parser.parse("now", fixedNow, false, ZoneOffset.UTC);
+        assertEquals("2001-01-01T12:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testNowPlus1HourWithFixedSupplier() {
+        Instant parsed = parser.parse("now+1h", fixedNow, false, ZoneOffset.UTC);
+        assertEquals("2001-01-01T13:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testNowMinus1HourWithFixedSupplier() {
+        Instant parsed = parser.parse("now-1h", fixedNow, false, ZoneOffset.UTC);
+        assertEquals("2001-01-01T11:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testRoundingWithRoundUpProperty() {
+        // now = 2001-01-01T12:00:00Z
+        // now/d with roundUp = true should round to the beginning of the day + 1 day -1 millisecond
+        // Actually: rounding sets time to 2001-01-01T00:00:00Z, roundUp would move to 2001-01-02T00:00:00Z,
+        // and subtract one millisecond → 2001-01-01T23:59:59.999Z
+        Instant parsed = parser.parse("now/d", fixedNow, true, ZoneOffset.UTC);
+        // Just before midnight of the next day
+        assertEquals("2001-01-01T23:59:59.999Z", parsed.toString());
+    }
+
+    @Test
+    public void testRoundingWithoutRoundUpProperty() {
+        Instant parsed = parser.parse("now/d", fixedNow, false, ZoneOffset.UTC);
+        // Rounding down to the start of the day: 2001-01-01T00:00:00Z
+        assertEquals("2001-01-01T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testFixedDatePlusMonthAndRoundToDay() {
+        // "2001-02-01||+1M/d"
+        // start: 2001-02-01T00:00:00Z
+        // +1M → 2001-03-01T00:00:00Z
+        // /d rounds to start of day (same day)
+        Instant parsed = parser.parse("2001-02-01||+1M/d", fixedNow, false, ZoneOffset.UTC);
+        assertEquals("2001-03-01T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testInvalidUnit() {
+        try {
+            parser.parse("now+1X", fixedNow, false, ZoneOffset.UTC);
+            fail("Expected an exception");
+        } catch (DateMathParseException e) {
+            // "Invalid unit: X"
+            // Actually from code: "unit [X] not supported for date math [1X]"
+            // The exact message:
+            // "unit [X] not supported for date math [1X]" is expected
+            // The code tries to parse "now+1X" → operator '+' recognized, num=1, unit='X'.
+            // The mathString is "1X". We'll accept the default message.
+            assertEquals("unit [X] not supported for date math [1X]", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testInvalidOperator() {
+        try {
+            parser.parse("now*1d", fixedNow, false, ZoneOffset.UTC);
+            fail("Expected an exception");
+        } catch (DateMathParseException e) {
+            // operator not supported
+            assertEquals("operator not supported for date math [*1d]", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testTruncatedMath() {
+        try {
+            parser.parse("now+", fixedNow, false, ZoneOffset.UTC);
+            fail("Expected an exception");
+        } catch (DateMathParseException e) {
+            // truncated date math
+            assertEquals("truncated date math [+]", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testRoundSingleUnitOnly() {
+        try {
+            parser.parse("now/2d", fixedNow, false, ZoneOffset.UTC);
+            fail("Expected an exception");
+        } catch (DateMathParseException e) {
+            // rounding `/` can only be used on single unit types
+            assertEquals("rounding `/` can only be used on single unit types [/2d]", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testTimeZoneAwareParsing() {
+        // Parse a date with a different timezone
+        // We'll pick a date in a different zone and ensure it adjusts if timeZone is provided
+        Instant parsed = parser.parse("2001-01-01T12:00:00-02:00", fixedNow, false, ZoneOffset.UTC);
+        // 12:00 at -02:00 is actually 14:00 UTC
+        assertEquals("2001-01-01T14:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testNowWithMultipleOperationsAndRoundUp() {
+        // now = 2001-01-01T12:00:00Z
+        // now+1M-1d/d with roundUp = true
+        // Steps:
+        // +1M → 2001-02-01T12:00:00Z
+        // -1d → 2001-01-31T12:00:00Z
+        // /d with roundUp → round down: 2001-01-31T00:00:00Z plus 1 day = 2001-02-01T00:00:00Z minus 1 ms = 2001-01-31T23:59:59.999Z
+        Instant parsed = parser.parse("now+1M-1d/d", fixedNow, true, ZoneOffset.UTC);
+        assertEquals("2001-01-31T23:59:59.999Z", parsed.toString());
+    }
+
+    @Test
+    public void testEmptyDate() {
+        try {
+            parser.parse("", fixedNow, false, ZoneOffset.UTC);
+            fail("Expected an exception");
+        } catch (DateMathParseException e) {
+            assertEquals("cannot parse empty date", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testInvalidExpression() {
+        // Something like 2022-05-18||invalid
+        try {
+            parser.parse("2022-05-18||invalid", fixedNow, false, ZoneOffset.UTC);
+            fail("Expected an exception");
+        } catch (DateMathParseException e) {
+            // operator not supported or truncated math. Actually, 'i' is not recognized as '+', '-', or '/'
+            // This should fail as "operator not supported for date math [invalid]"
+            assertEquals("operator not supported for date math [invalid]", e.getMessage());
+        }
+    }
+
+    @Test
+    public void testFailedToParseDate() {
+        try {
+            parser.parse("not-a-date", fixedNow, false, ZoneOffset.UTC);
+            fail("Expected an exception");
+        } catch (DateMathParseException e) {
+            // "failed to parse date field [not-a-date] with format [strict_date_optional_time]: [Text 'not-a-date' could not be parsed...]"
+            // Checking just the start of the message:
+            assertTrue(e.getMessage().startsWith("failed to parse date field [not-a-date] with format"));
+        }
+    }
+}
diff --git a/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/datemath/JavaDateFormatterTest.java b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/datemath/JavaDateFormatterTest.java
new file mode 100644
index 000000000..76a52c79e
--- /dev/null
+++ b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/datemath/JavaDateFormatterTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.datemath;
+
+import org.junit.Test;
+
+import java.time.Instant;
+
+import static org.junit.Assert.*;
+
+/**
+ * Comprehensive tests for JavaDateFormatter covering various formats:
+ * - Epoch formats (epoch_millis, epoch_second)
+ * - ISO-based formats (strict_date_optional_time, strict_date_time_no_millis, etc.)
+ * - Basic formats (basic_date, basic_date_time, etc.)
+ * - Ordinal formats (ordinal_date, etc.)
+ * - Strict vs non-strict variants
+ * - Custom patterns
+ * - Fallback between multiple formats
+ */
+public class JavaDateFormatterTest {
+
+    @Test
+    public void testEpochMillis() {
+        // epoch_millis: 978307200000L = 2001-01-01T12:00:00Z
+        // The expected original code had midnight, let's confirm correct epoch:
+        // 978307200000 ms = 2001-01-01T12:00:00Z indeed.
+        JavaDateFormatter formatter = new JavaDateFormatter("epoch_millis");
+        Instant parsed = Instant.from(formatter.parse("978307200000"));
+        assertEquals("2001-01-01T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testEpochSecond() {
+        // epoch_second: 978307200 = 2001-01-01T12:00:00Z
+        JavaDateFormatter formatter = new JavaDateFormatter("epoch_second");
+        Instant parsed = Instant.from(formatter.parse("978307200"));
+        assertEquals("2001-01-01T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testStrictDateOptionalTime() {
+        // strict_date_optional_time: "2022-05-18T15:23:17Z"
+        JavaDateFormatter formatter = new JavaDateFormatter("strict_date_optional_time");
+        Instant parsed = Instant.from(formatter.parse("2022-05-18T15:23:17Z"));
+        assertEquals("2022-05-18T15:23:17Z", parsed.toString());
+    }
+
+    @Test
+    public void testDateOnlyWithStrictDateOptionalTime() {
+        // strict_date_optional_time: "2022-05-18" should default to midnight
+        JavaDateFormatter formatter = new JavaDateFormatter("strict_date_optional_time");
+        Instant parsed = Instant.from(formatter.parse("2022-05-18"));
+        assertEquals("2022-05-18T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testDateTimeNoMillis() {
+        // date_time_no_millis: "yyyy-MM-dd'T'HH:mm:ssZ"
+        JavaDateFormatter formatter = new JavaDateFormatter("date_time_no_millis");
+        Instant parsed = Instant.from(formatter.parse("2022-05-18T15:23:17+00:00"));
+        assertEquals("2022-05-18T15:23:17Z", parsed.toString());
+    }
+
+    @Test
+    public void testStrictDateTimeNoMillis() {
+        // strict_date_time_no_millis: same pattern but strict
+        JavaDateFormatter formatter = new JavaDateFormatter("strict_date_time_no_millis");
+        Instant parsed = Instant.from(formatter.parse("2022-05-18T15:23:17Z"));
+        assertEquals("2022-05-18T15:23:17Z", parsed.toString());
+    }
+
+    @Test
+    public void testBasicDate() {
+        // basic_date: yyyyMMdd
+        // "20010101" = 2001-01-01T00:00:00Z
+        JavaDateFormatter formatter = new JavaDateFormatter("basic_date");
+        Instant parsed = Instant.from(formatter.parse("20010101"));
+        assertEquals("2001-01-01T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testBasicDateTime() {
+        // basic_date_time: yyyyMMdd'T'HHmmss.SSSZ
+        // "20010101T123000.000Z" = 2001-01-01T12:30:00Z
+        JavaDateFormatter formatter = new JavaDateFormatter("basic_date_time");
+        Instant parsed = Instant.from(formatter.parse("20010101T123000.000Z"));
+        assertEquals("2001-01-01T12:30:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testOrdinalDate() {
+        // ordinal_date: yyyy-DDD, e.g. "2001-001" = 2001-01-01
+        JavaDateFormatter formatter = new JavaDateFormatter("ordinal_date");
+        Instant parsed = Instant.from(formatter.parse("2001-001"));
+        assertEquals("2001-01-01T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testOrdinalDateTimeNoMillis() {
+        // ordinal_date_time_no_millis: yyyy-DDD'T'HH:mm:ssZ
+        // "2001-001T12:00:00Z" = 2001-01-01T12:00:00Z
+        JavaDateFormatter formatter = new JavaDateFormatter("ordinal_date_time_no_millis");
+        Instant parsed = Instant.from(formatter.parse("2001-001T12:00:00Z"));
+        assertEquals("2001-01-01T12:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testHourMinuteSecond() {
+        // hour_minute_second: HH:mm:ss
+        // "12:34:56" with no date = defaults to today's date at that time?
+        // Our code sets date-only defaults. For time-only, must default to 1970-01-01?
+        // If not implemented, either skip or fix code to handle pure times.
+        // Let's assume we default to 1970-01-01 if time only:
+        JavaDateFormatter formatter = new JavaDateFormatter("hour_minute_second");
+        Instant parsed = Instant.from(formatter.parse("12:34:56"));
+        assertEquals("1970-01-01T12:34:56Z", parsed.toString());
+    }
+
+    @Test
+    public void testCustomPattern() {
+        // "MM/dd/yyyy": "03/21/2019" = 2019-03-21T00:00:00Z
+        JavaDateFormatter formatter = new JavaDateFormatter("MM/dd/yyyy");
+        Instant parsed = Instant.from(formatter.parse("03/21/2019"));
+        assertEquals("2019-03-21T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testFallbackBetweenFormats() {
+        // If first format doesn't match, second one should
+        JavaDateFormatter formatter = new JavaDateFormatter("yyyy-MM-dd||epoch_millis");
+        // Not a yyyy-MM-dd date, but epoch_millis:
+        Instant parsed = Instant.from(formatter.parse("978307200000"));
+        assertEquals("2001-01-01T00:00:00Z", parsed.toString());
+    }
+
+    @Test
+    public void testNoMatch() {
+        JavaDateFormatter formatter = new JavaDateFormatter("yyyy/MM/dd||basic_date");
+        try {
+            formatter.parse("not-a-date");
+            fail("Expected exception");
+        } catch (DateMathParseException e) {
+            assertTrue(e.getMessage().contains("failed to parse date field [not-a-date]"));
+        }
+    }
+
+    // Add more tests as needed for strict variants, time_no_millis, t_time, week_date, etc.
+    // The provided tests give a broad coverage of different formats.
+}
diff --git a/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/geo/DistanceUnitTest.java b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/geo/DistanceUnitTest.java
new file mode 100644
index 000000000..14e727555
--- /dev/null
+++ b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/geo/DistanceUnitTest.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.geo;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class DistanceUnitTest {
+
+    // Local constants
+    /** Earth ellipsoid major axis defined by WGS 84 in meters */
+    public static final double EARTH_SEMI_MAJOR_AXIS = 6378137.0;      // meters (WGS 84)
+
+    /** Earth ellipsoid minor axis defined by WGS 84 in meters */
+    public static final double EARTH_SEMI_MINOR_AXIS = 6356752.314245; // meters (WGS 84)
+
+    /** Earth mean radius defined by WGS 84 in meters */
+    public static final double EARTH_MEAN_RADIUS = 6371008.7714D;      // meters (WGS 84)
+
+    /** Earth axis ratio defined by WGS 84 (0.996647189335) */
+    public static final double EARTH_AXIS_RATIO = EARTH_SEMI_MINOR_AXIS / EARTH_SEMI_MAJOR_AXIS;
+
+    /** Earth ellipsoid equator length in meters */
+    public static final double EARTH_EQUATOR = 2*Math.PI * EARTH_SEMI_MAJOR_AXIS;
+
+    /** Earth ellipsoid polar distance in meters */
+    public static final double EARTH_POLAR_DISTANCE = Math.PI * EARTH_SEMI_MINOR_AXIS;
+
+    @Test
+    public void testEarthCircumference() {
+        assertEquals(EARTH_EQUATOR / DistanceUnit.KILOMETERS.toMeters(1.0), DistanceUnit.KILOMETERS.getEarthCircumference(), 0.0001);
+        assertEquals(EARTH_EQUATOR, DistanceUnit.METERS.getEarthCircumference(), 0.0001);
+    }
+
+    @Test
+    public void testEarthRadius() {
+        assertEquals(EARTH_SEMI_MAJOR_AXIS / DistanceUnit.MILES.toMeters(1.0), DistanceUnit.MILES.getEarthRadius(), 0.0001);
+        assertEquals(EARTH_SEMI_MAJOR_AXIS, DistanceUnit.METERS.getEarthRadius(), 0.0001);
+    }
+
+    @Test
+    public void testDistancePerDegree() {
+        assertEquals(EARTH_EQUATOR / 360.0, DistanceUnit.METERS.getDistancePerDegree(), 0.0001);
+        assertEquals(EARTH_EQUATOR / (360.0 * DistanceUnit.KILOMETERS.toMeters(1.0)), DistanceUnit.KILOMETERS.getDistancePerDegree(), 0.0001);
+    }
+
+    @Test
+    public void testToMeters() {
+        assertEquals(1000.0, DistanceUnit.KILOMETERS.toMeters(1.0), 0.0001);
+        assertEquals(1609.344, DistanceUnit.MILES.toMeters(1.0), 0.0001);
+        assertEquals(1.0, DistanceUnit.METERS.toMeters(1.0), 0.0001);
+    }
+
+    @Test
+    public void testFromMeters() {
+        assertEquals(1.0, DistanceUnit.KILOMETERS.fromMeters(1000.0), 0.0001);
+        assertEquals(1.0, DistanceUnit.MILES.fromMeters(1609.344), 0.0001);
+        assertEquals(1.0, DistanceUnit.METERS.fromMeters(1.0), 0.0001);
+    }
+
+    @Test
+    public void testConvert() {
+        assertEquals(1.0, DistanceUnit.convert(1.0, DistanceUnit.KILOMETERS, DistanceUnit.KILOMETERS), 0.0001);
+        assertEquals(1000.0, DistanceUnit.convert(1.0, DistanceUnit.KILOMETERS, DistanceUnit.METERS), 0.0001);
+        assertEquals(1.0, DistanceUnit.convert(1609.344, DistanceUnit.METERS, DistanceUnit.MILES), 0.0001);
+    }
+
+    @Test
+    public void testToString() {
+        assertEquals("km", DistanceUnit.KILOMETERS.toString());
+        assertEquals("mi", DistanceUnit.MILES.toString());
+    }
+
+    @Test
+    public void testParse() {
+        assertEquals(1.0, DistanceUnit.MILES.parse("1mi", DistanceUnit.METERS), 0.0001);
+        assertEquals(1609.344, DistanceUnit.METERS.parse("1mi", DistanceUnit.METERS), 0.0001);
+        assertEquals(1000.0, DistanceUnit.METERS.parse("1km", DistanceUnit.METERS), 0.0001);
+        assertEquals(1.0, DistanceUnit.KILOMETERS.parse("1km", DistanceUnit.KILOMETERS), 0.0001);
+    }
+
+    @Test
+    public void testParseUnit() {
+        assertEquals(DistanceUnit.KILOMETERS, DistanceUnit.parseUnit("1km", DistanceUnit.METERS));
+        assertEquals(DistanceUnit.MILES, DistanceUnit.parseUnit("1mi", DistanceUnit.METERS));
+        assertEquals(DistanceUnit.METERS, DistanceUnit.parseUnit("1", DistanceUnit.METERS));
+    }
+
+    @Test
+    public void testDistanceParsing() {
+        DistanceUnit.Distance distance = DistanceUnit.Distance.parseDistance("1km");
+        assertEquals(1.0, distance.value, 0.0001);
+        assertEquals(DistanceUnit.KILOMETERS, distance.unit);
+
+        DistanceUnit.Distance defaultDistance = DistanceUnit.Distance.parseDistance("100");
+        assertEquals(100.0, defaultDistance.value, 0.0001);
+        assertEquals(DistanceUnit.DEFAULT, defaultDistance.unit);
+    }
+
+    @Test
+    public void testDistanceConversion() {
+        DistanceUnit.Distance distance = new DistanceUnit.Distance(1.0, DistanceUnit.KILOMETERS);
+        DistanceUnit.Distance converted = distance.convert(DistanceUnit.METERS);
+        assertEquals(1000.0, converted.value, 0.0001);
+        assertEquals(DistanceUnit.METERS, converted.unit);
+    }
+
+    @Test
+    public void testDistanceEqualsAndHashCode() {
+        DistanceUnit.Distance d1 = new DistanceUnit.Distance(1.0, DistanceUnit.KILOMETERS);
+        DistanceUnit.Distance d2 = new DistanceUnit.Distance(1000.0, DistanceUnit.METERS);
+        assertEquals(d1, d2);
+        assertEquals(d1.hashCode(), d2.hashCode());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromStringInvalidUnit() {
+        DistanceUnit.fromString("invalid");
+    }
+
+}
diff --git a/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/geo/GeoDistanceTest.java b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/geo/GeoDistanceTest.java
new file mode 100644
index 000000000..e8cf785e2
--- /dev/null
+++ b/persistence-spi/src/test/java/org/apache/unomi/persistence/spi/conditions/geo/GeoDistanceTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.unomi.persistence.spi.conditions.geo;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class GeoDistanceTest {
+
+    private static final double SRC_LAT = 40.7128; // Example source latitude
+    private static final double SRC_LON = -74.0060; // Example source longitude
+    private static final double DST_LAT = 34.0522; // Example destination latitude
+    private static final double DST_LON = -118.2437; // Example destination longitude
+
+    @Test
+    public void testFromString() {
+        assertEquals(GeoDistance.PLANE, GeoDistance.fromString("plane"));
+        assertEquals(GeoDistance.ARC, GeoDistance.fromString("arc"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromStringInvalid() {
+        GeoDistance.fromString("invalid");
+    }
+
+    @Test
+    public void testCalculatePlane() {
+        double expectedDistanceInMeters = 3978199.0100920075;
+        double actualDistance = GeoDistance.PLANE.calculate(SRC_LAT, SRC_LON, DST_LAT, DST_LON, DistanceUnit.METERS);
+        assertEquals(expectedDistanceInMeters, actualDistance, 0.01);
+    }
+
+    @Test
+    public void testCalculateArc() {
+        double expectedDistanceInMeters = 3935751.673226063;
+        double actualDistance = GeoDistance.ARC.calculate(SRC_LAT, SRC_LON, DST_LAT, DST_LON, DistanceUnit.METERS);
+        assertEquals(expectedDistanceInMeters, actualDistance, 0.01);
+    }
+
+
+}
diff --git a/plugins/baseplugin/pom.xml b/plugins/baseplugin/pom.xml
index e072e1887..6ec4c65ce 100644
--- a/plugins/baseplugin/pom.xml
+++ b/plugins/baseplugin/pom.xml
@@ -59,12 +59,6 @@
             <artifactId>joda-time</artifactId>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.unomi</groupId>
-            <artifactId>unomi-persistence-elasticsearch-core</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
-        </dependency>
         <dependency>
             <groupId>org.apache.unomi</groupId>
             <artifactId>unomi-persistence-spi</artifactId>
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/GeoLocationByPointSessionConditionEvaluator.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/GeoLocationByPointSessionConditionEvaluator.java
index 7c7d55e17..b98ce9150 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/GeoLocationByPointSessionConditionEvaluator.java
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/GeoLocationByPointSessionConditionEvaluator.java
@@ -22,8 +22,8 @@ import org.apache.unomi.api.Item;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluator;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluatorDispatcher;
-import org.elasticsearch.common.geo.GeoDistance;
-import org.elasticsearch.common.unit.DistanceUnit;
+import org.apache.unomi.persistence.spi.conditions.geo.DistanceUnit;
+import org.apache.unomi.persistence.spi.conditions.geo.GeoDistance;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java
index 8caf01322..6d9ce91e1 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java
@@ -25,6 +25,7 @@ import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.persistence.spi.PersistenceService;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluator;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluatorDispatcher;
+import org.apache.unomi.persistence.spi.conditions.PastEventConditionPersistenceQueryBuilder;
 import org.apache.unomi.scripting.ScriptExecutor;
 
 import java.util.ArrayList;
@@ -36,6 +37,7 @@ public class PastEventConditionEvaluator implements ConditionEvaluator {
     private PersistenceService persistenceService;
     private DefinitionsService definitionsService;
     private ScriptExecutor scriptExecutor;
+    private PastEventConditionPersistenceQueryBuilder pastEventConditionPersistenceQueryBuilder;
 
     public void setPersistenceService(PersistenceService persistenceService) {
         this.persistenceService = persistenceService;
@@ -45,6 +47,10 @@ public class PastEventConditionEvaluator implements ConditionEvaluator {
         this.definitionsService = definitionsService;
     }
 
+    public void setPastEventConditionPersistenceQueryBuilder(PastEventConditionPersistenceQueryBuilder pastEventConditionPersistenceQueryBuilder) {
+        this.pastEventConditionPersistenceQueryBuilder = pastEventConditionPersistenceQueryBuilder;
+    }
+
     public void setScriptExecutor(ScriptExecutor scriptExecutor) {
         this.scriptExecutor = scriptExecutor;
     }
@@ -70,10 +76,10 @@ public class PastEventConditionEvaluator implements ConditionEvaluator {
             }
         } else {
             // TODO see for deprecation, this should not happen anymore each past event condition should have a generatedPropertyKey
-            count = persistenceService.queryCount(PastEventConditionESQueryBuilder.getEventCondition(condition, context, item.getItemId(), definitionsService, scriptExecutor), Event.ITEM_TYPE);
+            count = persistenceService.queryCount(pastEventConditionPersistenceQueryBuilder.getEventCondition(condition, context, item.getItemId(), definitionsService, scriptExecutor), Event.ITEM_TYPE);
         }
 
-        boolean eventsOccurred = PastEventConditionESQueryBuilder.getStrategyFromOperator((String) condition.getParameter("operator"));
+        boolean eventsOccurred = pastEventConditionPersistenceQueryBuilder.getStrategyFromOperator((String) condition.getParameter("operator"));
         if (eventsOccurred) {
             int minimumEventCount = parameters.get("minimumEventCount") == null  ? 0 : (Integer) parameters.get("minimumEventCount");
             int maximumEventCount = parameters.get("maximumEventCount") == null  ? Integer.MAX_VALUE : (Integer) parameters.get("maximumEventCount");
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java
index c6e117305..a258b52aa 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java
@@ -21,19 +21,19 @@ import ognl.*;
 import ognl.enhance.ExpressionAccessor;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.unomi.api.*;
+import org.apache.unomi.api.Event;
+import org.apache.unomi.api.GeoPoint;
+import org.apache.unomi.api.Item;
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.plugins.baseplugin.conditions.accessors.HardcodedPropertyAccessor;
-import org.apache.unomi.scripting.ExpressionFilterFactory;
-import org.apache.unomi.scripting.SecureFilteringClassLoader;
+import org.apache.unomi.persistence.spi.PropertyHelper;
 import org.apache.unomi.persistence.spi.conditions.ConditionContextHelper;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluator;
 import org.apache.unomi.persistence.spi.conditions.ConditionEvaluatorDispatcher;
-import org.apache.unomi.persistence.spi.PropertyHelper;
-import org.elasticsearch.ElasticsearchParseException;
-import org.elasticsearch.common.joda.Joda;
-import org.elasticsearch.common.joda.JodaDateMathParser;
-import org.elasticsearch.common.unit.DistanceUnit;
+import org.apache.unomi.persistence.spi.conditions.DateUtils;
+import org.apache.unomi.persistence.spi.conditions.geo.DistanceUnit;
+import org.apache.unomi.plugins.baseplugin.conditions.accessors.HardcodedPropertyAccessor;
+import org.apache.unomi.scripting.ExpressionFilterFactory;
+import org.apache.unomi.scripting.SecureFilteringClassLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,6 +44,8 @@ import java.util.*;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import static org.apache.unomi.persistence.spi.conditions.DateUtils.getDate;
+
 /**
  * Evaluator for property comparison conditions
  */
@@ -99,7 +101,7 @@ public class PropertyConditionEvaluator implements ConditionEvaluator {
     private boolean compareValues(Object actualValue, Collection<?> expectedValues, Collection<?> expectedValuesInteger,  Collection<?> expectedValuesDouble,  Collection<?> expectedValuesDate, Collection<?> expectedValuesDateExpr, String op) {
         Collection<Object> expectedDateExpr = null;
         if (expectedValuesDateExpr != null) {
-            expectedDateExpr = expectedValuesDateExpr.stream().map(PropertyConditionEvaluator::getDate).collect(Collectors.toList());
+            expectedDateExpr = expectedValuesDateExpr.stream().map(DateUtils::getDate).collect(Collectors.toList());
         }
         @SuppressWarnings("unchecked")
         Collection<?> expected = ObjectUtils.firstNonNull(expectedValues, expectedValuesDate, expectedValuesInteger, expectedValuesDouble, expectedDateExpr);
@@ -306,8 +308,8 @@ public class PropertyConditionEvaluator implements ConditionEvaluator {
             }
 
             final GeoPoint expectedCenter = GeoPoint.fromString(centerString);
-            final DistanceUnit expectedUnit = unitString != null ? DistanceUnit.fromString(unitString) : DistanceUnit.DEFAULT;
-            final double distanceInMeters = expectedUnit.convert(distance, DistanceUnit.METERS);
+            final DistanceUnit expectedUnit = unitString != null ? DistanceUnit.fromString(unitString) : DistanceUnit.METERS;
+            final double distanceInMeters = expectedUnit.toMeters(distance);
 
             return expectedCenter.distanceTo(actualCenter) <= distanceInMeters;
         }
@@ -434,24 +436,6 @@ public class PropertyConditionEvaluator implements ConditionEvaluator {
         return accessor;
     }
 
-    public static Date getDate(Object value) {
-        if (value == null) {
-            return null;
-        }
-        if (value instanceof Date) {
-            return ((Date) value);
-        } else {
-            JodaDateMathParser parser = new JodaDateMathParser(Joda.forPattern("strictDateOptionalTime||epoch_millis"));
-            try {
-                return Date.from(parser.parse(value.toString(), System::currentTimeMillis));
-            } catch (ElasticsearchParseException e) {
-                LOGGER.warn("unable to parse date. See debug log level for full stacktrace");
-                LOGGER.debug("unable to parse date {}", value, e);
-            }
-        }
-        return null;
-    }
-
     @SuppressWarnings("unchecked")
     private List<Object> getValueSet(Object expectedValue) {
         if (expectedValue instanceof List) {
diff --git a/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 67a2c537c..64cf0eef1 100644
--- a/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -49,89 +49,7 @@
     <reference id="configSharingService" interface="org.apache.unomi.api.services.ConfigSharingService"/>
     <reference id="scriptExecutor" interface="org.apache.unomi.scripting.ScriptExecutor"/>
     <reference id="topicService" interface="org.apache.unomi.api.services.TopicService"/>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="idsConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.IdsConditionESQueryBuilder">
-            <property name="maximumIdsQueryCount" value="${es.maximumIdsQueryCount}"/>
-        </bean>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="booleanConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.BooleanConditionESQueryBuilder"/>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="notConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.NotConditionESQueryBuilder"/>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="matchAllConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.MatchAllConditionESQueryBuilder"/>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="propertyConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.PropertyConditionESQueryBuilder"/>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="geoLocationByPointSessionConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.GeoLocationByPointSessionConditionESQueryBuilder"/>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="sourceEventPropertyConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.SourceEventPropertyConditionESQueryBuilder"/>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="pastEventConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.PastEventConditionESQueryBuilder">
-            <property name="definitionsService" ref="definitionsService"/>
-            <property name="persistenceService" ref="persistenceService"/>
-            <property name="segmentService" ref="segmentService"/>
-            <property name="scriptExecutor" ref="scriptExecutor"/>
-            <property name="maximumIdsQueryCount" value="${es.maximumIdsQueryCount}"/>
-            <property name="pastEventsDisablePartitions" value="${es.pastEventsDisablePartitions}"/>
-            <property name="aggregateQueryBucketSize" value="${es.aggregateQueryBucketSize}"/>
-        </bean>
-    </service>
-
-    <service
-            interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
-        <service-properties>
-            <entry key="queryBuilderId" value="nestedConditionESQueryBuilder"/>
-        </service-properties>
-        <bean class="org.apache.unomi.plugins.baseplugin.conditions.NestedConditionESQueryBuilder"/>
-    </service>
-
+    <reference id="pastEventConditionPersistenceQueryBuilder" interface="org.apache.unomi.persistence.spi.conditions.PastEventConditionPersistenceQueryBuilder"/>
 
     <!-- Condition evaluators -->
     <service interface="org.apache.unomi.persistence.spi.conditions.ConditionEvaluator">
@@ -196,6 +114,7 @@
             <property name="definitionsService" ref="definitionsService"/>
             <property name="persistenceService" ref="persistenceService"/>
             <property name="scriptExecutor" ref="scriptExecutor"/>
+            <property name="pastEventConditionPersistenceQueryBuilder" ref="pastEventConditionPersistenceQueryBuilder"/>
         </bean>
     </service>
 
diff --git a/plugins/hover-event/src/main/java/org/apache/unomi/plugins/events/hover/querybuilders/HoverEventConditionESQueryBuilder.java b/plugins/hover-event/src/main/java/org/apache/unomi/plugins/events/hover/querybuilders/HoverEventConditionESQueryBuilder.java
index 978edef2a..36cc59b99 100644
--- a/plugins/hover-event/src/main/java/org/apache/unomi/plugins/events/hover/querybuilders/HoverEventConditionESQueryBuilder.java
+++ b/plugins/hover-event/src/main/java/org/apache/unomi/plugins/events/hover/querybuilders/HoverEventConditionESQueryBuilder.java
@@ -18,8 +18,8 @@
 package org.apache.unomi.plugins.events.hover.querybuilders;
 
 import org.apache.unomi.api.conditions.Condition;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilder;
+import org.apache.unomi.persistence.elasticsearch.ConditionESQueryBuilderDispatcher;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;