diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/ArrayExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/ArrayExpression.java index 7b499f7ada0..26186b2b581 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/ArrayExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/ArrayExpression.java @@ -19,37 +19,42 @@ import java.util.function.Function; import static com.mongodb.client.model.expressions.Expressions.of; +import static com.mongodb.client.model.expressions.MqlUnchecked.Unchecked.PRESENT; /** - * Expresses an array value. An array value is a finite, ordered collection of - * elements of a certain type. + * An array {@link Expression value} in the context of the MongoDB Query + * Language (MQL). An array is a finite, ordered collection of elements of a + * certain type. It is also known as a finite mathematical sequence. * - * @param the type of the elements in the array + * @param the type of the elements */ public interface ArrayExpression extends Expression { /** - * Returns an array consisting of those elements in this array that match - * the given predicate condition. Evaluates each expression in this array - * according to the cond function. If cond evaluates to logical true, then - * the element is preserved; if cond evaluates to logical false, the element - * is omitted. + * An array consisting of only those elements in {@code this} array that + * match the provided predicate. * - * @param cond the function to apply to each element - * @return the new array + * @param predicate the predicate to apply to each element to determine if + * it should be included. + * @return the resulting array. */ - ArrayExpression filter(Function cond); + ArrayExpression filter(Function predicate); /** - * Returns an array consisting of the results of applying the given function - * to the elements of this array. + * An array consisting of the results of applying the provided function to + * the elements of {@code this} array. * - * @param in the function to apply to each element - * @return the new array - * @param the type contained in the resulting array + * @param in the function to apply to each element. + * @return the resulting array. + * @param the type of the elements of the resulting array. */ ArrayExpression map(Function in); + /** + * The size of {@code this} array. + * + * @return the size. + */ IntegerExpression size(); BooleanExpression any(Function predicate); @@ -81,6 +86,7 @@ public interface ArrayExpression extends Expression { * @param i * @return */ + @MqlUnchecked(PRESENT) T elementAt(IntegerExpression i); default T elementAt(final int i) { @@ -91,6 +97,7 @@ default T elementAt(final int i) { * user asserts that array is not empty * @return */ + @MqlUnchecked(PRESENT) T first(); /** @@ -113,7 +120,25 @@ default ArrayExpression slice(final int start, final int length) { ArrayExpression distinct(); + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passArrayTo(Function, ? extends R> f); - R switchArrayOn(Function>, ? extends BranchesTerminal, ? extends R>> on); + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchArrayOn(Function>, ? extends BranchesTerminal, ? extends R>> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/BooleanExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/BooleanExpression.java index 00da72a208e..489fecc9a8d 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/BooleanExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/BooleanExpression.java @@ -19,7 +19,8 @@ import java.util.function.Function; /** - * A logical boolean value, either true or false. + * A boolean {@linkplain Expression value} in the context of the + * MongoDB Query Language (MQL). */ public interface BooleanExpression extends Expression { @@ -45,20 +46,37 @@ public interface BooleanExpression extends Expression { * @return the resulting value. */ BooleanExpression and(BooleanExpression other); - // TODO-END check the evaluation semantics of and/or /** - * The {@code left} branch when {@code this} is true, - * and the {@code right} branch otherwise. + * The {@code ifTrue} value when {@code this} is true, + * and the {@code ifFalse} value otherwise. * - * @param left the left branch. - * @param right the right branch. + * @param ifTrue the ifTrue value. + * @param ifFalse the ifFalse value. * @return the resulting value. * @param The type of the resulting expression. */ - T cond(T left, T right); + T cond(T ifTrue, T ifFalse); + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passBooleanTo(Function f); - R switchBooleanOn(Function, ? extends BranchesTerminal> on); + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchBooleanOn(Function, ? extends BranchesTerminal> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/Branches.java b/driver-core/src/main/com/mongodb/client/model/expressions/Branches.java index 40a726b4c9c..fbda31c2e9e 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/Branches.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/Branches.java @@ -20,6 +20,8 @@ import java.util.List; import java.util.function.Function; +import static com.mongodb.client.model.expressions.MqlUnchecked.Unchecked.TYPE_ARGUMENT; + public final class Branches { Branches() { @@ -78,7 +80,7 @@ public BranchesIntermediary isDate(final Function BranchesIntermediary isArray(final Function, ? extends R> r) { + public BranchesIntermediary isArray(final Function, ? extends R> r) { return is(v -> mqlEx(v).isArray(), v -> r.apply((ArrayExpression) v)); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/DateExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/DateExpression.java index be2b838058b..0ddc695bec0 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/DateExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/DateExpression.java @@ -19,8 +19,9 @@ import java.util.function.Function; /** - * An instantaneous date and time. Tracks a UTC datetime, the number of - * milliseconds since the Unix epoch. Does not track the timezone. + * A UTC date-time {@linkplain Expression value} in the context + * of the MongoDB Query Language (MQL). Tracks the number of + * milliseconds since the Unix epoch, and does not track the timezone. */ public interface DateExpression extends Expression { @@ -123,11 +124,30 @@ public interface DateExpression extends Expression { * provided {@code timezone}, and formatted according to the {@code format}. * * @param timezone the UTC Offset or Olson Timezone Identifier. - * @param format the format specifier. TODO-END what standard is this? + * @param format the format specifier. * @return the resulting value. */ StringExpression asString(StringExpression timezone, StringExpression format); + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passDateTo(Function f); - R switchDateOn(Function, ? extends BranchesTerminal> on); + + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchDateOn(Function, ? extends BranchesTerminal> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/DocumentExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/DocumentExpression.java index 4d7d2ef678f..42105b2f213 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/DocumentExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/DocumentExpression.java @@ -23,6 +23,8 @@ import static com.mongodb.client.model.expressions.Expressions.of; import static com.mongodb.client.model.expressions.Expressions.ofMap; +import static com.mongodb.client.model.expressions.MqlUnchecked.Unchecked.PRESENT; +import static com.mongodb.client.model.expressions.MqlUnchecked.Unchecked.TYPE; /** * Expresses a document value. A document is an ordered set of fields, where the @@ -34,8 +36,10 @@ public interface DocumentExpression extends Expression { DocumentExpression unsetField(String fieldName); + @MqlUnchecked(PRESENT) Expression getField(String fieldName); + @MqlUnchecked({PRESENT, TYPE}) BooleanExpression getBoolean(String fieldName); BooleanExpression getBoolean(String fieldName, BooleanExpression other); @@ -102,6 +106,25 @@ default MapExpression getMap(final String fieldName, f MapExpression asMap(); + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passDocumentTo(Function f); - R switchDocumentOn(Function, ? extends BranchesTerminal> on); + + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchDocumentOn(Function, ? extends BranchesTerminal> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/EntryExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/EntryExpression.java index 6fda24588c6..4bafd935ab9 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/EntryExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/EntryExpression.java @@ -18,6 +18,15 @@ import static com.mongodb.client.model.expressions.Expressions.of; +/** + * A map entry {@linkplain Expression value} in the context + * of the MongoDB Query Language (MQL). An entry has a + * {@linkplain StringExpression string} key and some + * {@linkplain Expression value}. Entries are used with + * {@linkplain MapExpression maps}. + * + * @param The type of the value + */ public interface EntryExpression extends Expression { StringExpression getKey(); diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/Expression.java b/driver-core/src/main/com/mongodb/client/model/expressions/Expression.java index fb94262796f..5009cd50e04 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/Expression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/Expression.java @@ -20,21 +20,88 @@ import java.util.function.Function; +import static com.mongodb.client.model.expressions.MqlUnchecked.Unchecked.TYPE_ARGUMENT; + /** - *

Users should treat these interfaces as sealed, and must not implement any - * sub-interfaces. + * A value in the context of the MongoDB Query Language (MQL). + * + *

The API provided by this base type and its subtypes is the Java-native + * variant of MQL. It is used to query the MongoDB server, to perform remote + * computations, to store and retrieve data, or to otherwise work with data on + * a MongoDB server or compatible execution context. Though the methods exposed + * through this API generally correspond to MQL operations, this correspondence + * is not exact. + * + *

The following is an example of usage within an aggregation pipeline. Here, + * the current document value is obtained and its "numberArray" field is + * filtered and summed, in a style similar to that of the Java Stream API: + * + *

{@code
+ * import static com.mongodb.client.model.expressions.Expressions.current;
+ * MongoCollection col = ...;
+ * AggregateIterable result = col.aggregate(Arrays.asList(
+ *     addFields(new Field<>("result", current()
+ *         .getArray("numberArray")
+ *         .filter(v -> v.gt(of(0)))
+ *         .sum(v -> v)))));
+ * }
+ * + *

Values are typically initially obtained via the current document and its + * fields, or specified via statically-imported methods on the + * {@link Expressions} class. + * + *

As with the Java Stream API's terminal operations, corresponding Java + * values are not directly available, but must be obtained indirectly via + * {@code MongoCollection.aggregate} or {@code MongoCollection.find}. + * Certain methods may cause an error, which will be produced + * through these "terminal operations". * - * TODO-END: 'cause an error', 'execution context', wrong types/unsafe operations - * TODO-END: types and how missing/null are not part of any type. + *

The null value is not part of, and cannot be used as if it were part + * of, any explicit type (except the root type {@link Expression} itself). + * See {@link Expressions#ofNull} for more details. + * + *

This API specifies no "missing" or "undefined" value. Users may use + * {@link MapExpression#has} to check whether a value is present. + * + *

This type hierarchy differs from the {@linkplain org.bson} types in that + * they provide computational operations, the numeric types are less granular, + * and it offers multiple abstractions of certain types (document, map, entry). + * It differs from the corresponding Java types (such as {@code int}, + * {@link String}, {@link java.util.Map}) in that the operations + * available differ, and in that an implementation of this API may be used to + * produce MQL in the form of BSON. (This API makes no guarantee regarding the + * BSON output produced by its implementation, which in any case may vary due + * to optimization or other factors.) + * + *

Some methods within the API constitute an assertion by the user that the + * data is of a certain type. For example, {@link DocumentExpression#getArray}} + * requires that the underlying field is both an array, and an array of some + * certain type. If the field is not an array in the underlying data, behaviour + * is undefined by this API (though behaviours may be defined by the execution + * context, users are strongly discouraged from relying on behaviour that is not + * part of this API). + * + * + *

Users should treat these interfaces as sealed, and should not create + * implementations. * * @see Expressions */ @Evolving public interface Expression { + /** + * The method {@link Expression#eq} should be used to compare values for + * equality. This method checks reference equality. + */ + @Override + boolean equals(Object other); + /** * Whether {@code this} value is equal to the {@code other} value. * + *

The result does not correlate with {@link Expression#equals(Object)}. + * * @param other the other value. * @return the resulting value. */ @@ -43,6 +110,8 @@ public interface Expression { /** * Whether {@code this} value is not equal to the {@code other} value. * + *

The result does not correlate with {@link Expression#equals(Object)}. + * * @param other the other value. * @return the resulting value. */ @@ -83,7 +152,7 @@ public interface Expression { BooleanExpression lte(Expression other); /** - * {@code this} value as a {@link BooleanExpression boolean} if + * {@code this} value as a {@linkplain BooleanExpression boolean} if * {@code this} is a boolean, or the {@code other} boolean value if * {@code this} is null, or is missing, or is of any other non-boolean type. * @@ -93,35 +162,29 @@ public interface Expression { BooleanExpression isBooleanOr(BooleanExpression other); /** - * {@code this} value as a {@link NumberExpression number} if + * {@code this} value as a {@linkplain NumberExpression number} if * {@code this} is a number, or the {@code other} number value if * {@code this} is null, or is missing, or is of any other non-number type. * - *

Since integers are a subset of numbers, if a value is an integer, - * this does not imply that it is not a number, and vice-versa. - * * @param other the other value. * @return the resulting value. */ NumberExpression isNumberOr(NumberExpression other); /** - * {@code this} value as an {@link IntegerExpression integer} if + * {@code this} value as an {@linkplain IntegerExpression integer} if * {@code this} is an integer, or the {@code other} integer value if * {@code this} is null, or is missing, or is of any other non-integer type. * - *

Since integers are a subset of numbers, if a value is an integer, - * this does not imply that it is not a number, and vice-versa. - * * @param other the other value. * @return the resulting value. */ IntegerExpression isIntegerOr(IntegerExpression other); /** - * {@code this} value as a {@link BooleanExpression boolean} if - * {@code this} is a boolean, or the {@code other} boolean value if - * {@code this} is null, or is missing, or is of any other non-boolean type. + * {@code this} value as a {@linkplain StringExpression string} if + * {@code this} is a string, or the {@code other} string value if + * {@code this} is null, or is missing, or is of any other non-string type. * * @param other the other value. * @return the resulting value. @@ -129,18 +192,17 @@ public interface Expression { StringExpression isStringOr(StringExpression other); /** - * {@code this} value as a {@link StringExpression string} if - * {@code this} is a string, or the {@code other} string value if - * {@code this} is null, or is missing, or is of any other non-string type. + * {@code this} value as a {@linkplain DateExpression boolean} if + * {@code this} is a date, or the {@code other} date value if + * {@code this} is null, or is missing, or is of any other non-date type. * * @param other the other value. * @return the resulting value. */ DateExpression isDateOr(DateExpression other); - /** - * {@code this} value as a {@link ArrayExpression array} if + * {@code this} value as a {@linkplain ArrayExpression array} if * {@code this} is an array, or the {@code other} array value if * {@code this} is null, or is missing, or is of any other non-array type. * @@ -150,54 +212,91 @@ public interface Expression { * the user is an unchecked assertion that all elements are of that type, * and that no element is null, is missing, or is of some other type. If the * user cannot make such an assertion, some appropriate super-type should be - * chosen, and elements should be individually type-checked. + * chosen, and if necessary the elements should be individually type-checked. * * @param other the other value. * @return the resulting value. * @param the type of the elements of the resulting array. */ - ArrayExpression isArrayOr(ArrayExpression other); + ArrayExpression<@MqlUnchecked(TYPE_ARGUMENT) T> isArrayOr(ArrayExpression other); - // TODO-END doc after Map merged, "record" and "schema objects" are decided + /** + * {@code this} value as a {@linkplain DocumentExpression document} if + * {@code this} is a document or document-like value (such as all + * {@linkplain MapExpression maps} and all + * {@linkplain EntryExpression entries} created using the present API) + * or the {@code other} document value if + * {@code this} is null, or is missing, or is of any other non-document type. + * + * @param other the other value. + * @return the resulting value. + */ T isDocumentOr(T other); + /** + * {@code this} value as a {@linkplain MapExpression map} if + * {@code this} is a map or map-like value (such as all + * {@linkplain DocumentExpression documents} and all + * {@linkplain EntryExpression entries} created using the present API) + * or the {@code other} map value if + * {@code this} is null, or is missing, or is of any other non-map type. + * + *

Warning: this operation does not guarantee type safety. While this + * operation is guaranteed to produce a map, the type of the values of + * that array are not guaranteed by the API. The specification of a type by + * the user is an unchecked assertion that all values are of that type, + * and that no value is null of some other type. If the + * user cannot make such an assertion, some appropriate super-type should be + * chosen, and if necessary the values should be individually type-checked. + * + * @param other the other value. + * @return the resulting value. + * @param the type of the values of the resulting map. + */ MapExpression isMapOr(MapExpression other); /** - * The {@link StringExpression string} value corresponding to this value. - * - *

This may "cause an error" if the type cannot be converted to string, - * as is the case with {@link ArrayExpression arrays}, - * {@link DocumentExpression documents}, and {@link MapExpression maps}. - * TODO-END what about null/missing? - * TODO-END document vs record - * TODO-END "cause an error" above + * The {@linkplain StringExpression string} representation of {@code this} value. * + *

This may cause an error to be produced if the type cannot be converted + * to a {@linkplain StringExpression string}, as is the case with + * {@linkplain ArrayExpression arrays}, + * {@linkplain DocumentExpression documents}, + * {@linkplain MapExpression maps}, + * {@linkplain EntryExpression entries}, and the + * {@linkplain Expressions#ofNull() null value}. * * @see StringExpression#parseDate() * @see StringExpression#parseInteger() - * TODO-END all the others? implement? * @return the resulting value. */ StringExpression asString(); /** - * Applies the provided function to {@code this}. + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + *

The appropriate type-based variant should be used when the type + * of {@code this} is known. * - *

Equivalent to {@code f.apply(this)}, and allows lambdas and static, - * user-defined functions to use the chaining syntax. For example: - * {@code myInteger.apply(isEven)} (here, {@code isEven} is a function - * taking an {@link IntegerExpression integer} and yielding a - * {@link BooleanExpression boolean}). + * @see BooleanExpression#passBooleanTo + * @see IntegerExpression#passIntegerTo + * @see NumberExpression#passNumberTo + * @see StringExpression#passStringTo + * @see DateExpression#passDateTo + * @see ArrayExpression#passArrayTo + * @see MapExpression#passMapTo + * @see DocumentExpression#passDocumentTo * * @param f the function to apply. * @return the resulting value. * @param the type of the resulting value. */ R passTo(Function f); + /** - * The value resulting from applying the provided switch mapping to - * {@code this} value. + * The result of applying the provided switch mapping to {@code this} value. * *

Can be used to perform pattern matching on the type of {@code this} * value, or to perform comparisons, or to perform any arbitrary check on @@ -205,12 +304,29 @@ public interface Expression { * *

The suggested convention is to use "{@code on}" as the name of the * {@code mapping} parameter, for example: - * {@code myValue.switchOn(on -> on.isInteger(...)...)}. + * + *

{@code
+     * myValue.switchOn(on -> on
+     *     .isInteger(...)
+     *     ...
+     *     .defaults(...))
+     * }
+ * + *

The appropriate type-based variant should be used when the type + * of {@code this} is known. + * + * @see BooleanExpression#switchBooleanOn + * @see IntegerExpression#switchIntegerOn + * @see NumberExpression#switchNumberOn + * @see StringExpression#switchStringOn + * @see DateExpression#switchDateOn + * @see ArrayExpression#switchArrayOn + * @see MapExpression#switchMapOn + * @see DocumentExpression#switchDocumentOn * * @param mapping the switch mapping. * @return the resulting value. * @param the type of the resulting value. */ R switchOn(Function, ? extends BranchesTerminal> mapping); - } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/Expressions.java b/driver-core/src/main/com/mongodb/client/model/expressions/Expressions.java index 132d0fa03c1..e1cabfa10d6 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/Expressions.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/Expressions.java @@ -39,31 +39,25 @@ import static com.mongodb.client.model.expressions.MqlExpression.extractBsonValue; /** - * Convenience methods related to {@link Expression}. + * Convenience methods related to {@link Expression}, used primarily to + * produce values in the context of the MongoDB Query Language (MQL). */ public final class Expressions { private Expressions() {} /** - * Returns an expression having the same boolean value as the provided - * boolean primitive. + * Returns a {@linkplain BooleanExpression boolean} value corresponding to + * the provided {@code boolean} primitive. * - * @param of the boolean primitive - * @return the boolean expression + * @param of the {@code boolean} primitive. + * @return the resulting value. */ public static BooleanExpression of(final boolean of) { // we intentionally disallow ofBoolean(null) return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonBoolean(of))); } - /** - * Returns an array expression containing the same boolean values as the - * provided array of booleans. - * - * @param array the array of booleans - * @return the boolean array expression - */ public static ArrayExpression ofBooleanArray(final boolean... array) { Assertions.notNull("array", array); List list = new ArrayList<>(); @@ -74,11 +68,11 @@ public static ArrayExpression ofBooleanArray(final boolean... } /** - * Returns an expression having the same integer value as the provided - * int primitive. + * Returns an {@linkplain IntegerExpression integer} value corresponding to + * the provided {@code int} primitive. * - * @param of the int primitive - * @return the integer expression + * @param of the {@code int} primitive. + * @return the resulting value. */ public static IntegerExpression of(final int of) { return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonInt32(of))); @@ -93,6 +87,13 @@ public static ArrayExpression ofIntegerArray(final int... arr return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list))); } + /** + * Returns an {@linkplain IntegerExpression integer} value corresponding to + * the provided {@code long} primitive. + * + * @param of the {@code long} primitive. + * @return the resulting value. + */ public static IntegerExpression of(final long of) { return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonInt64(of))); } @@ -106,6 +107,13 @@ public static ArrayExpression ofIntegerArray(final long... ar return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list))); } + /** + * Returns a {@linkplain NumberExpression number} value corresponding to + * the provided {@code double} primitive. + * + * @param of the {@code double} primitive. + * @return the resulting value. + */ public static NumberExpression of(final double of) { return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDouble(of))); } @@ -119,6 +127,13 @@ public static ArrayExpression ofNumberArray(final double... ar return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list))); } + /** + * Returns a {@linkplain NumberExpression number} value corresponding to + * the provided {@link Decimal128} + * + * @param of the {@link Decimal128}. + * @return the resulting value. + */ public static NumberExpression of(final Decimal128 of) { Assertions.notNull("Decimal128", of); return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDecimal128(of))); @@ -134,6 +149,14 @@ public static ArrayExpression ofNumberArray(final Decimal128.. return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result))); } + + /** + * Returns a {@linkplain DateExpression date and time} value corresponding to + * the provided {@link Instant}. + * + * @param of the {@link Instant}. + * @return the resulting value. + */ public static DateExpression of(final Instant of) { Assertions.notNull("Instant", of); return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDateTime(of.toEpochMilli()))); @@ -150,18 +173,17 @@ public static ArrayExpression ofDateArray(final Instant... array } /** - * Returns an expression having the same string value as the provided - * string. + * Returns an {@linkplain StringExpression string} value corresponding to + * the provided {@link String}. * - * @param of the string - * @return the string expression + * @param of the {@link String}. + * @return the resulting value. */ public static StringExpression of(final String of) { Assertions.notNull("String", of); return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonString(of))); } - public static ArrayExpression ofStringArray(final String... array) { Assertions.notNull("array", array); List result = new ArrayList<>(); @@ -172,16 +194,41 @@ public static ArrayExpression ofStringArray(final String... ar return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result))); } + /** + * Returns a reference to the "current" + * {@linkplain DocumentExpression document} value. + * The "current" value is the top-level document currently being processed + * in the aggregation pipeline stage. + * + * @return a reference to the current value + */ public static DocumentExpression current() { return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonString("$$CURRENT"))) .assertImplementsAllExpressions(); } + /** + * Returns a reference to the "current" + * value as a {@linkplain MapExpression map} value. + * The "current" value is the top-level document currently being processed + * in the aggregation pipeline stage. + * + * @return a reference to the current value as a map. + * @param the type of the map's values. + */ public static MapExpression currentAsMap() { return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonString("$$CURRENT"))) .assertImplementsAllExpressions(); } + /** + * Returns an {@linkplain DocumentExpression array} value, containing the + * {@linkplain Expression values} provided. + * + * @param array the {@linkplain Expression values}. + * @return the resulting value. + * @param the type of the array elements. + */ @SafeVarargs // nothing is stored in the array public static ArrayExpression ofArray(final T... array) { Assertions.notNull("array", array); @@ -195,6 +242,14 @@ public static ArrayExpression ofArray(final T... array }); } + /** + * Returns an {@linkplain EntryExpression entry} value. + * + * @param k the key. + * @param v the value. + * @return the resulting value. + * @param the type of the key. + */ public static EntryExpression ofEntry(final StringExpression k, final T v) { Assertions.notNull("k", k); Assertions.notNull("v", v); @@ -211,11 +266,13 @@ public static MapExpression ofMap() { } /** - * user asserts type of values is T + * Returns a {@linkplain MapExpression map} value corresponding to the + * provided {@link Bson Bson document}. The user asserts that all values + * in the document are of type {@code T}. * - * @param map - * @return - * @param + * @param map the map as a {@link Bson Bson document}. + * @return the resulting map value. + * @param the type of the resulting map's values. */ public static MapExpression ofMap(final Bson map) { Assertions.notNull("map", map); @@ -223,6 +280,13 @@ public static MapExpression ofMap(final Bson map) { map.toBsonDocument(BsonDocument.class, cr)))); } + /** + * Returns a {@linkplain DocumentExpression document} value corresponding to the + * provided {@link Bson Bson document}. + * + * @param document the {@linkplain Bson BSON document}. + * @return the resulting value. + */ public static DocumentExpression of(final Bson document) { Assertions.notNull("document", document); // All documents are wrapped in a $literal. If we don't wrap, we need to @@ -232,6 +296,21 @@ public static DocumentExpression of(final Bson document) { document.toBsonDocument(BsonDocument.class, cr)))); } + /** + * The null value in the context of the MongoDB Query Language (MQL). + * + *

The null value is not part of, and cannot be used as if it were part + * of, any explicit type (except the root type {@link Expression} itself). + * It has no explicit type of its own. + * + *

Instead of checking that a value is null, users should generally + * check that a value is of their expected type, via methods such as + * {@link Expression#isNumberOr(NumberExpression)}. Where the null value + * must be checked explicitly, users may use {@link Branches#isNull} within + * {@link Expression#switchOn}. + * + * @return the null value + */ public static Expression ofNull() { // There is no specific expression type corresponding to Null, // and Null is not a value in any other expression type. diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/IntegerExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/IntegerExpression.java index 9e7bac51bae..4757e64eab1 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/IntegerExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/IntegerExpression.java @@ -19,9 +19,10 @@ import java.util.function.Function; /** - * An integer value. Integers are a subset of {@link NumberExpression numbers}, - * and so, for example, the integer 0 and the number 0 are the same value, - * and are equal. + * An integer {@linkplain Expression value} in the context of the MongoDB Query + * Language (MQL). Integers are a subset of {@linkplain NumberExpression numbers}, + * and so, for example, the integer 0 and the number 0 are + * {@linkplain #eq(Expression) equal}. */ public interface IntegerExpression extends NumberExpression { @@ -62,7 +63,7 @@ default IntegerExpression add(final int other) { } /** - * The result of subtracting the {@code other} value from {@code this}. + * The difference of subtracting the {@code other} value from {@code this}. * * @param other the other value. * @return the resulting value. @@ -70,7 +71,7 @@ default IntegerExpression add(final int other) { IntegerExpression subtract(IntegerExpression other); /** - * The result of subtracting the {@code other} value from {@code this}. + * The difference of subtracting the {@code other} value from {@code this}. * * @param other the other value. * @return the resulting value. @@ -80,7 +81,8 @@ default IntegerExpression subtract(final int other) { } /** - * The larger value of {@code this} and the {@code other} value. + * The {@linkplain #gt(Expression) larger} value of {@code this} + * and the {@code other} value. * * @param other the other value. * @return the resulting value. @@ -88,7 +90,8 @@ default IntegerExpression subtract(final int other) { IntegerExpression max(IntegerExpression other); /** - * The smaller value of {@code this} and the {@code other} value. + * The {@linkplain #lt(Expression) smaller} value of {@code this} + * and the {@code other} value. * * @param other the other value. * @return the resulting value. @@ -103,14 +106,32 @@ default IntegerExpression subtract(final int other) { IntegerExpression abs(); /** - * The {@link DateExpression date} corresponding to {@code this} value + * The {@linkplain DateExpression date} corresponding to {@code this} value * when taken to be the number of milliseconds since the Unix epoch. * * @return the resulting value. */ DateExpression millisecondsToDate(); - // TODO-END rename integer.utcMillisecondsToDate -- date.asUtcMilliseconds + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passIntegerTo(Function f); - R switchIntegerOn(Function, ? extends BranchesTerminal> on); + + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchIntegerOn(Function, ? extends BranchesTerminal> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/MapExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/MapExpression.java index d5d782c7728..db0d387bb02 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/MapExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/MapExpression.java @@ -19,6 +19,7 @@ import java.util.function.Function; import static com.mongodb.client.model.expressions.Expressions.of; +import static com.mongodb.client.model.expressions.MqlUnchecked.Unchecked.PRESENT; public interface MapExpression extends Expression { @@ -29,6 +30,7 @@ default BooleanExpression has(String key) { } // TODO-END doc "user asserts" + @MqlUnchecked(PRESENT) T get(StringExpression key); // TODO-END doc "user asserts" @@ -60,6 +62,25 @@ default MapExpression unset(final String key) { R asDocument(); + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passMapTo(Function, ? extends R> f); - R switchMapOn(Function>, ? extends BranchesTerminal, ? extends R>> on); + + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchMapOn(Function>, ? extends BranchesTerminal, ? extends R>> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpression.java index a1d923c8be2..69330fc8f54 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpression.java @@ -155,8 +155,8 @@ public BooleanExpression and(final BooleanExpression other) { } @Override - public R cond(final R left, final R right) { - return newMqlExpression(ast("$cond", left, right)); + public R cond(final R ifTrue, final R ifFalse) { + return newMqlExpression(ast("$cond", ifTrue, ifFalse)); } /** @see DocumentExpression */ diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/MqlUnchecked.java b/driver-core/src/main/com/mongodb/client/model/expressions/MqlUnchecked.java new file mode 100644 index 00000000000..4dcdb76308d --- /dev/null +++ b/driver-core/src/main/com/mongodb/client/model/expressions/MqlUnchecked.java @@ -0,0 +1,72 @@ +/* + * Copyright 2008-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mongodb.client.model.expressions; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Documents places where the API relies on a user asserting + * something that is not checked at run-time. + * If the assertion turns out to be false, the API behavior is unspecified. + * + *

This class is not part of the public API and may be removed or changed at any time

+ */ +@Documented +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD, ElementType.TYPE_USE}) +public @interface MqlUnchecked { + /** + * @return A hint on the user assertion the API relies on. + */ + Unchecked[] value(); + + /** + * @see MqlUnchecked#value() + */ + enum Unchecked { + /** + * The API relies on the values it encounters being of the type + * implied/specified by or inferred from the user code. + * For example, {@link com.mongodb.client.model.expressions.DocumentExpression#getBoolean(String)} + * relies on the values of the document field being of the + * {@linkplain com.mongodb.client.model.expressions.BooleanExpression boolean} type. + */ + TYPE, + /** + * The API checks the raw type, but relies on the type argument + * implied/specified by or inferred from the user code being correct. + * For example, {@link com.mongodb.client.model.expressions.Expression#isArrayOr(ArrayExpression)} + * checks that the value is of the + * {@linkplain com.mongodb.client.model.expressions.ArrayExpression array} raw type, + * but relies on the elements of the array being of the type derived from the user code. + * + *

One may think of it as a more specific version of {@link #TYPE}.

+ */ + TYPE_ARGUMENT, + /** + * The API relies on the element identified by index, name, position, etc., being present in the + * {@linkplain com.mongodb.client.model.expressions.DocumentExpression document}, + * {@linkplain com.mongodb.client.model.expressions.MapExpression map}, + * {@linkplain com.mongodb.client.model.expressions.ArrayExpression array} involved. + * For example, {@link com.mongodb.client.model.expressions.DocumentExpression#getField(String)}. + */ + PRESENT, + } +} diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/NumberExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/NumberExpression.java index 52f882ae9f8..1756ed02318 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/NumberExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/NumberExpression.java @@ -19,9 +19,10 @@ import java.util.function.Function; /** - * A number value. {@link IntegerExpression Integers} are a subset of numbers, - * and so, for example, the integer 0 and the number 0 are the same value, - * and are equal. + * A number {@linkplain Expression value} in the context of the MongoDB Query + * Language (MQL). {@linkplain IntegerExpression Integers} are a subset of + * numbers, and so, for example, the integer 0 and the number 0 are + * {@linkplain #eq(Expression) equal}. */ public interface NumberExpression extends Expression { @@ -44,9 +45,9 @@ default NumberExpression multiply(final Number other) { } /** - * The result of dividing {@code this} value by the {@code other} value. - * This is not integer division: dividing {@code 1} by {@code 2} will yield - * {@code 0.5}. + * The quotient of dividing {@code this} value by the {@code other} value. + * This is not integer division: dividing {@code 1} by {@code 2} will + * always yield {@code 0.5}. * * @param other the other value. * @return the resulting value. @@ -54,9 +55,9 @@ default NumberExpression multiply(final Number other) { NumberExpression divide(NumberExpression other); /** - * The result of dividing {@code this} value by the {@code other} value. - * This is not integer division: dividing {@code 1} by {@code 2} will yield - * {@code 0.5}. + * The quotient of dividing {@code this} value by the {@code other} value. + * This is not integer division: dividing {@code 1} by {@code 2} will + * always yield {@code 0.5}. * * @param other the other value. * @return the resulting value. @@ -84,7 +85,7 @@ default NumberExpression add(final Number other) { } /** - * The result of subtracting the {@code other} value from {@code this}. + * The difference of subtracting the {@code other} value from {@code this}. * * @param other the other value. * @return the resulting value. @@ -92,7 +93,7 @@ default NumberExpression add(final Number other) { NumberExpression subtract(NumberExpression other); /** - * The result of subtracting the {@code other} value from {@code this}. + * The difference of subtracting the {@code other} value from {@code this}. * * @param other the other value. * @return the resulting value. @@ -102,7 +103,8 @@ default NumberExpression subtract(final Number other) { } /** - * The larger value of {@code this} and the {@code other} value. + * The {@linkplain #gt(Expression) larger} value of {@code this} + * and the {@code other} value. * * @param other the other value. * @return the resulting value. @@ -110,7 +112,8 @@ default NumberExpression subtract(final Number other) { NumberExpression max(NumberExpression other); /** - * The smaller value of {@code this} and the {@code other} value. + * The {@linkplain #lt(Expression) smaller} value of {@code this} + * and the {@code other} value. * * @param other the other value. * @return the resulting value. @@ -125,7 +128,8 @@ default NumberExpression subtract(final Number other) { IntegerExpression round(); /** - * The result of rounding {@code this} to the nearest even {@code place}. + * The result of rounding {@code this} to {@code place} decimal places + * using the "half to even" approach. * * @param place the decimal place to round to, from -20 to 100, exclusive. * Positive values specify the place to the right of the @@ -141,6 +145,25 @@ default NumberExpression subtract(final Number other) { */ NumberExpression abs(); + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passNumberTo(Function f); - R switchNumberOn(Function, ? extends BranchesTerminal> on); + + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchNumberOn(Function, ? extends BranchesTerminal> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/StringExpression.java b/driver-core/src/main/com/mongodb/client/model/expressions/StringExpression.java index f4990b54949..633a99baba0 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/StringExpression.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/StringExpression.java @@ -55,6 +55,25 @@ default StringExpression substrBytes(final int start, final int length) { DateExpression parseDate(StringExpression timezone, StringExpression format); + /** + * The result of passing {@code this} value to the provided function. + * Equivalent to {@code f.apply(this)}, and allows lambdas and static, + * user-defined functions to use the chaining syntax. + * + * @see Expression#passTo + * @param f the function to apply. + * @return the resulting value. + * @param the type of the resulting value. + */ R passStringTo(Function f); - R switchStringOn(Function, ? extends BranchesTerminal> on); + + /** + * The result of applying the provided switch mapping to {@code this} value. + * + * @see Expression#switchOn + * @param mapping the switch mapping. + * @return the resulting value. + * @param the type of the resulting value. + */ + R switchStringOn(Function, ? extends BranchesTerminal> mapping); } diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/package-info.java b/driver-core/src/main/com/mongodb/client/model/expressions/package-info.java index 596d642e654..f9b52b24897 100644 --- a/driver-core/src/main/com/mongodb/client/model/expressions/package-info.java +++ b/driver-core/src/main/com/mongodb/client/model/expressions/package-info.java @@ -18,6 +18,7 @@ * API for MQL expressions. * * @see com.mongodb.client.model.expressions.Expression + * @see com.mongodb.client.model.expressions.Expressions */ @NonNullApi package com.mongodb.client.model.expressions;