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
The following commit(s) were added to refs/heads/opensearch-persistence by this
push:
new f1f3e7670 Add detailed Javadoc comments to aggregation and condition
interfaces for improved documentation
f1f3e7670 is described below
commit f1f3e76703de3cce76f2d6599e97dbe857aee55f
Author: Serge Huber <[email protected]>
AuthorDate: Tue Oct 14 11:49:54 2025 +0200
Add detailed Javadoc comments to aggregation and condition interfaces for
improved documentation
---
.../unomi/healthcheck/HealthCheckProvider.java | 20 ++++++++
.../elasticsearch/ConditionESQueryBuilder.java | 25 ++++++++++
.../opensearch/ConditionOSQueryBuilder.java | 25 ++++++++++
.../persistence/spi/aggregate/BaseAggregate.java | 18 +++++++
.../persistence/spi/aggregate/DateAggregate.java | 33 ++++++++++++-
.../spi/aggregate/DateRangeAggregate.java | 23 +++++++++
.../spi/aggregate/IpRangeAggregate.java | 15 ++++++
.../spi/aggregate/NumericRangeAggregate.java | 15 ++++++
.../persistence/spi/aggregate/TermsAggregate.java | 16 +++++++
.../PastEventConditionPersistenceQueryBuilder.java | 55 ++++++++++++++++++++++
.../evaluator/ConditionEvaluatorDispatcher.java | 43 +++++++++++++++++
11 files changed, 287 insertions(+), 1 deletion(-)
diff --git
a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckProvider.java
b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckProvider.java
index 982ea8a5b..9cabede77 100644
---
a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckProvider.java
+++
b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckProvider.java
@@ -17,12 +17,32 @@
package org.apache.unomi.healthcheck;
+/**
+ * Contract for pluggable health checks. Implementations provide a name and
+ * return a {@link HealthCheckResponse} when executed; a default timeout
+ * response is available via {@link #timeout()}.
+ */
public interface HealthCheckProvider {
+ /**
+ * Unique provider name used in responses and logs.
+ *
+ * @return the health check provider name
+ */
String name();
+ /**
+ * Executes the health check, returning a {@link HealthCheckResponse}
describing status and optional data.
+ *
+ * @return the health check result
+ */
HealthCheckResponse execute();
+ /**
+ * Convenience method returning a standardized timeout error response for
this provider.
+ *
+ * @return a timeout {@link HealthCheckResponse}
+ */
default HealthCheckResponse timeout() {
return new
HealthCheckResponse.Builder().name(name()).withData("error.cause",
"timeout").error().build();
}
diff --git
a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilder.java
b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilder.java
index 7fc1f3a34..92d5bf7e2 100644
---
a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilder.java
+++
b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ConditionESQueryBuilder.java
@@ -22,10 +22,35 @@ import org.apache.unomi.api.conditions.Condition;
import java.util.Map;
+/**
+ * SPI for building Elasticsearch {@link Query} objects from Unomi {@link
org.apache.unomi.api.conditions.Condition}
+ * instances. Implementations translate high-level conditions into ES queries
and may optionally provide a
+ * {@link #count(Condition, Map, ConditionESQueryBuilderDispatcher)} strategy
when needed by callers.
+ */
public interface ConditionESQueryBuilder {
+ /**
+ * Builds an Elasticsearch {@link Query} from the provided Unomi {@link
Condition}.
+ * Implementations may use the {@code context} (e.g., resolved parameters)
and delegate to
+ * the {@code dispatcher} for nested/child conditions.
+ *
+ * @param condition the condition to translate
+ * @param context additional context for parameter resolution and
sub-builders
+ * @param dispatcher dispatcher to build sub-conditions when composing
queries
+ * @return a concrete Elasticsearch {@link Query}
+ */
Query buildQuery(Condition condition, Map<String, Object> context,
ConditionESQueryBuilderDispatcher dispatcher);
+ /**
+ * Optionally returns a fast count for the provided condition using an
implementation-specific
+ * strategy. Default throws {@link UnsupportedOperationException};
override when a specialized
+ * count path exists.
+ *
+ * @param condition the condition to count
+ * @param context additional context for parameter resolution
+ * @param dispatcher dispatcher to count sub-conditions if needed
+ * @return the number of matching documents
+ */
default long count(Condition condition, Map<String, Object> context,
ConditionESQueryBuilderDispatcher dispatcher) {
throw new UnsupportedOperationException();
}
diff --git
a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilder.java
b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilder.java
index e89ce3d79..f0b1f048c 100644
---
a/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilder.java
+++
b/persistence-opensearch/core/src/main/java/org/apache/unomi/persistence/opensearch/ConditionOSQueryBuilder.java
@@ -22,10 +22,35 @@ import
org.opensearch.client.opensearch._types.query_dsl.Query;
import java.util.Map;
+/**
+ * SPI for building OpenSearch {@link Query} objects from Unomi {@link
org.apache.unomi.api.conditions.Condition}
+ * instances. Implementations translate high-level conditions into OS queries
and may optionally provide a
+ * {@link #count(Condition, Map, ConditionOSQueryBuilderDispatcher)} strategy
when needed by callers.
+ */
public interface ConditionOSQueryBuilder {
+ /**
+ * Builds an OpenSearch {@link Query} from the provided Unomi {@link
Condition}.
+ * Implementations may use the {@code context} (e.g., resolved parameters)
and delegate to
+ * the {@code dispatcher} for nested/child conditions.
+ *
+ * @param condition the condition to translate
+ * @param context additional context for parameter resolution and
sub-builders
+ * @param dispatcher dispatcher to build sub-conditions when composing
queries
+ * @return a concrete OpenSearch {@link Query}
+ */
Query buildQuery(Condition condition, Map<String, Object> context,
ConditionOSQueryBuilderDispatcher dispatcher);
+ /**
+ * Optionally returns a fast count for the provided condition using an
implementation-specific
+ * strategy. Default throws {@link UnsupportedOperationException};
override when a specialized
+ * count path exists.
+ *
+ * @param condition the condition to count
+ * @param context additional context for parameter resolution
+ * @param dispatcher dispatcher to count sub-conditions if needed
+ * @return the number of matching documents
+ */
default long count(Condition condition, Map<String, Object> context,
ConditionOSQueryBuilderDispatcher dispatcher) {
throw new UnsupportedOperationException();
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/BaseAggregate.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/BaseAggregate.java
index 4bd26a28d..242b1fd17 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/BaseAggregate.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/BaseAggregate.java
@@ -17,13 +17,31 @@
package org.apache.unomi.persistence.spi.aggregate;
+/**
+ * Base type for aggregation requests targeting a single field.
+ * Concrete aggregate types extend this class and add their specific
+ * aggregation parameters.
+ */
public abstract class BaseAggregate {
+ /**
+ * The name of the field to aggregate on.
+ */
private String field;
+ /**
+ * Creates a new aggregate for the given field.
+ *
+ * @param field the target field name
+ */
public BaseAggregate(String field) {
this.field = field;
}
+ /**
+ * Returns the target field name this aggregation applies to.
+ *
+ * @return the field name
+ */
public String getField() {
return field;
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateAggregate.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateAggregate.java
index 9eca674c8..3ec6f1fcf 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateAggregate.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateAggregate.java
@@ -21,13 +21,17 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+/**
+ * Aggregation that buckets documents by time intervals on a date/datetime
field.
+ * Supports legacy short interval formats (e.g., {@code 1M}) and new named
formats (e.g., {@code Month}).
+ */
public class DateAggregate extends BaseAggregate {
private static final String DEFAULT_INTERVAL = "1M";
private String interval;
private String format;
- // Maps bidirectionnelles pour la conversion entre formats
+ // Bidirectional maps for conversion between interval formats
private static final Map<String, String> OLD_TO_NEW_FORMAT = Map.ofEntries(
Map.entry("1s", "Second"),
Map.entry("1m", "Minute"),
@@ -49,22 +53,43 @@ public class DateAggregate extends BaseAggregate {
return Collections.unmodifiableMap(reverseMap);
}
+ /**
+ * Creates a date aggregation with the default interval.
+ *
+ * @param field the field to aggregate on
+ */
public DateAggregate(String field) {
super(field);
this.interval = DEFAULT_INTERVAL;
}
+ /**
+ * Creates a date aggregation with a specific interval (old or new format).
+ *
+ * @param field the field to aggregate on
+ * @param interval the interval, in old (e.g., {@code 1M}) or new (e.g.,
{@code Month}) format
+ */
public DateAggregate(String field, String interval) {
super(field);
setInterval(interval);
}
+ /**
+ * Creates a date aggregation with a specific interval and output format.
+ *
+ * @param field the field to aggregate on
+ * @param interval the interval, in old or new format
+ * @param format an optional output format understood by the persistence
layer
+ */
public DateAggregate(String field, String interval, String format) {
super(field);
setInterval(interval);
this.format = format;
}
+ /**
+ * Sets the interval; falls back to default when null/empty.
+ */
public void setInterval(String interval) {
this.interval = (interval != null && !interval.isEmpty()) ? interval :
DEFAULT_INTERVAL;
}
@@ -136,10 +161,16 @@ public class DateAggregate extends BaseAggregate {
return NEW_TO_OLD_FORMAT.getOrDefault(newFormat, newFormat);
}
+ /**
+ * Returns the output format, if any.
+ */
public String getFormat() {
return format;
}
+ /**
+ * Sets the output format.
+ */
public void setFormat(String format) {
this.format = format;
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateRangeAggregate.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateRangeAggregate.java
index 66daa51ff..74d51c98a 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateRangeAggregate.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/DateRangeAggregate.java
@@ -21,29 +21,52 @@ import org.apache.unomi.api.query.DateRange;
import java.util.List;
+/**
+ * Aggregation that buckets date/time values of a field into the provided
ranges,
+ * using an optional date {@code format}.
+ */
public class DateRangeAggregate extends BaseAggregate{
private String format;
private List<DateRange> dateRanges;
+ /**
+ * Creates a date range aggregation.
+ *
+ * @param field the field to aggregate on
+ * @param format optional date format understood by the persistence
layer
+ * @param dateRanges the list of date ranges
+ */
public DateRangeAggregate(String field, String format, List<DateRange>
dateRanges) {
super(field);
this.format = format;
this.dateRanges = dateRanges;
}
+ /**
+ * Returns the configured date ranges.
+ */
public List<DateRange> getDateRanges() {
return dateRanges;
}
+ /**
+ * Sets the date ranges to use for bucketing.
+ */
public void setDateRanges(List<DateRange> dateRanges) {
this.dateRanges = dateRanges;
}
+ /**
+ * Returns the date format, if any.
+ */
public String getFormat() {
return format;
}
+ /**
+ * Sets the date format.
+ */
public void setFormat(String format) {
this.format = format;
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/IpRangeAggregate.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/IpRangeAggregate.java
index d5cc8490e..2793c3d9b 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/IpRangeAggregate.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/IpRangeAggregate.java
@@ -21,18 +21,33 @@ import org.apache.unomi.api.query.IpRange;
import java.util.List;
+/**
+ * Aggregation that buckets IP values of a field into the provided IP ranges.
+ */
public class IpRangeAggregate extends BaseAggregate{
private List<IpRange> ranges;
+ /**
+ * Creates an IP range aggregation.
+ *
+ * @param field the field to aggregate on
+ * @param ranges the list of IP ranges
+ */
public IpRangeAggregate(String field, List<IpRange> ranges) {
super(field);
this.ranges = ranges;
}
+ /**
+ * Returns the configured IP ranges.
+ */
public List<IpRange> getRanges() {
return ranges;
}
+ /**
+ * Sets the IP ranges to use for bucketing.
+ */
public void setRanges(List<IpRange> ranges) {
this.ranges = ranges;
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/NumericRangeAggregate.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/NumericRangeAggregate.java
index 65fbf606b..fcb6f3546 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/NumericRangeAggregate.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/NumericRangeAggregate.java
@@ -21,18 +21,33 @@ import org.apache.unomi.api.query.NumericRange;
import java.util.List;
+/**
+ * Aggregation that buckets numeric values of a field into the provided ranges.
+ */
public class NumericRangeAggregate extends BaseAggregate{
private List<NumericRange> ranges;
+ /**
+ * Creates a numeric range aggregation.
+ *
+ * @param field the field to aggregate on
+ * @param ranges the list of numeric ranges
+ */
public NumericRangeAggregate(String field, List<NumericRange> ranges) {
super(field);
this.ranges = ranges;
}
+ /**
+ * Returns the configured numeric ranges.
+ */
public List<NumericRange> getRanges() {
return ranges;
}
+ /**
+ * Sets the numeric ranges to use for bucketing.
+ */
public void setRanges(List<NumericRange> ranges) {
this.ranges = ranges;
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/TermsAggregate.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/TermsAggregate.java
index 3b9d74142..9a71f20bd 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/TermsAggregate.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/aggregate/TermsAggregate.java
@@ -17,8 +17,18 @@
package org.apache.unomi.persistence.spi.aggregate;
+/**
+ * Aggregation that buckets documents by unique terms of a field.
+ * Optionally supports partitioning to split large cardinalities across
multiple requests.
+ */
public class TermsAggregate extends BaseAggregate{
+ /**
+ * Zero-based partition index when using partitioned terms aggregation;
{@code -1} means disabled.
+ */
private int partition = -1;
+ /**
+ * Total number of partitions when using partitioned terms aggregation;
{@code -1} means disabled.
+ */
private int numPartitions = -1;
@@ -32,10 +42,16 @@ public class TermsAggregate extends BaseAggregate{
this.numPartitions = numPartitions;
}
+ /**
+ * Returns the zero-based partition index, or {@code -1} if partitioning
is disabled.
+ */
public int getPartition() {
return partition;
}
+ /**
+ * Returns the total number of partitions, or {@code -1} if partitioning
is disabled.
+ */
public int getNumPartitions() {
return numPartitions;
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
index 0379c9711..c3ca0eb4d 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/PastEventConditionPersistenceQueryBuilder.java
@@ -22,10 +22,65 @@ import org.apache.unomi.scripting.ScriptExecutor;
import java.util.Map;
+/**
+ * SPI for translating the high-level {@code pastEventCondition} into a
concrete, persistence-friendly
+ * {@link Condition} so it can be executed efficiently by the underlying store
(Elasticsearch, OpenSearch, ...).
+ *
+ * <p>Why this exists</p>
+ * <ul>
+ * <li>{@code pastEventCondition} is a composite logical filter (event type,
time window, constraints, counts).
+ * The most efficient query structure varies by operator and by persistence
technology.</li>
+ * <li>This SPI decouples Unomi's condition model from storage-specific
optimizations, letting each persistence
+ * module build the best executable event condition.</li>
+ * </ul>
+ *
+ * <p>How it is used at runtime</p>
+ * <ul>
+ * <li>Implementations are provided by persistence modules and injected
where needed.</li>
+ * <li>
+ * In {@code
org.apache.unomi.plugins.baseplugin.conditions.PastEventConditionEvaluator},
evaluation follows two paths:
+ * <ol>
+ * <li>If the condition has a {@code generatedPropertyKey}, the
evaluator reads a precomputed count from the
+ * {@link org.apache.unomi.api.Profile} system properties (no
persistence query).</li>
+ * <li>Otherwise (legacy/fallback), it calls
+ * {@link #getEventCondition(Condition, Map, String, DefinitionsService,
ScriptExecutor)} to build an event-level
+ * condition and uses {@code PersistenceService#queryCount} to compute
the count against the event index.</li>
+ * </ol>
+ * </li>
+ * <li>Finally, the evaluator calls {@link #getStrategyFromOperator(String)}
to interpret the operator and decide if
+ * the result means "events occurred within bounds" or "no events
occurred".</li>
+ * </ul>
+ *
+ * @see
org.apache.unomi.plugins.baseplugin.conditions.PastEventConditionEvaluator
+ */
public interface PastEventConditionPersistenceQueryBuilder {
+ /**
+ * Derives the execution strategy from the operator provided by the {@code
pastEventCondition}.
+ * Implementations typically use this to switch between strategies like
"exists" vs "count",
+ * or inclusion vs exclusion logic, depending on what the underlying
engine can do most
+ * efficiently.
+ *
+ * @param operator the operator string coming from the condition
parameters (e.g. "equals",
+ * "greaterThan", custom operator, etc.)
+ * @return {@code true} or {@code false} depending on the chosen strategy;
the meaning is
+ * implementation-specific and documented by the persistence module
+ */
boolean getStrategyFromOperator(String operator);
+ /**
+ * Builds a persistence-friendly {@link Condition} that represents the
event filtering part of a
+ * {@code pastEventCondition}. The returned condition will be used by the
persistence layer to
+ * query historical events for a given profile.
+ *
+ * @param condition the original high-level {@code
pastEventCondition}
+ * @param context additional context values to resolve dynamic
parameters and scripts
+ * @param profileId the target profile identifier for which past
events are evaluated
+ * @param definitionsService service to resolve Unomi condition and type
definitions when needed
+ * @param scriptExecutor executor to evaluate scripted/dynamic
parameters if present
+ * @return a concrete {@link Condition} targeting events, suitable for
direct execution by the
+ * underlying persistence engine
+ */
Condition getEventCondition(Condition condition, Map<String, Object>
context, String profileId,
DefinitionsService definitionsService,
ScriptExecutor scriptExecutor);
}
diff --git
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
index 458a06c96..29b9286c7 100644
---
a/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
+++
b/persistence-spi/src/main/java/org/apache/unomi/persistence/spi/conditions/evaluator/ConditionEvaluatorDispatcher.java
@@ -21,9 +21,52 @@ import org.apache.unomi.api.conditions.Condition;
import java.util.Map;
+/**
+ * Central entry point for evaluating {@link Condition} instances against a
given {@link Item}.
+ * <p>
+ * The dispatcher locates the appropriate {@link ConditionEvaluator} based on
the condition type's
+ * configured evaluator identifier, then delegates evaluation to that
evaluator. Implementations
+ * typically:
+ * <ul>
+ * <li>Resolve a parent condition first when the condition type defines a
{@code parentCondition}
+ * (merging the current condition's parameter values into the context and
evaluating the parent).</li>
+ * <li>Contextualize dynamic parameters (scripts/placeholders) via
+ * {@code ConditionContextHelper} and a {@code ScriptExecutor} before
delegation.</li>
+ * <li>Wrap evaluation with metrics and handle missing evaluators
defensively.</li>
+ * </ul>
+ * <p>
+ * Evaluators are registered as OSGi services under a {@code
conditionEvaluatorId}; the dispatcher
+ * implementation maintains a map of these evaluators and dispatches
accordingly.
+ * <p>
+ * See {@code ConditionEvaluatorDispatcherImpl} for the reference
implementation and
+ * {@code PastEventConditionEvaluator} for a typical evaluator.
+ *
+ * @see
org.apache.unomi.persistence.spi.conditions.evaluator.impl.ConditionEvaluatorDispatcherImpl
+ * @see
org.apache.unomi.plugins.baseplugin.conditions.PastEventConditionEvaluator
+ */
public interface ConditionEvaluatorDispatcher {
+ /**
+ * Evaluates the provided {@link Condition} on the given {@link Item}
using an empty context.
+ * This is a convenience overload equivalent to calling
+ * {@link #eval(Condition, Item, Map)} with an empty map.
+ *
+ * @param condition the condition to evaluate
+ * @param item the target item (e.g., Profile, Event, Session)
+ * @return {@code true} if the condition matches, {@code false} otherwise
+ */
boolean eval(Condition condition, Item item);
+ /**
+ * Evaluates the provided {@link Condition} on the given {@link Item}
using the supplied
+ * execution context. Implementations may enrich the context with
parameter values when a
+ * parent condition is present and will contextualize dynamic parameters
before delegating
+ * to the appropriate {@link ConditionEvaluator}.
+ *
+ * @param condition the condition to evaluate
+ * @param item the target item (e.g., Profile, Event, Session)
+ * @param context additional context values available during evaluation
(may be mutated by the implementation)
+ * @return {@code true} if the condition matches, {@code false} otherwise
+ */
boolean eval(Condition condition, Item item, Map<String, Object> context);
}
\ No newline at end of file