T cond(T left, T right);
+}
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
new file mode 100644
index 00000000000..98e8f005769
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/DateExpression.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * Expresses a date value.
+ */
+public interface DateExpression extends Expression {
+
+}
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
new file mode 100644
index 00000000000..cf1522d1623
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/DocumentExpression.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Expresses a document value. A document is an ordered set of fields, where the
+ * key is a string value, mapping to a value of any other expression type.
+ */
+public interface DocumentExpression extends Expression {
+
+}
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
new file mode 100644
index 00000000000..987ccdd4bfa
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/Expression.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.mongodb.annotations.Evolving;
+
+/**
+ * Expressions express values that may be represented in (or computations that
+ * may be performed within) a MongoDB server. Each expression evaluates to some
+ * value, much like any Java expression evaluates to some value. Expressions may
+ * be thought of as boxed values. Evaluation of an expression will usually occur
+ * on a MongoDB server.
+ *
+ * Users should treat these interfaces as sealed, and must not implement any
+ * expression interfaces.
+ *
+ *
Expressions are typed. It is possible to execute expressions against data
+ * that is of the wrong type, such as by applying the "not" boolean expression
+ * to a document field that is an integer, null, or missing. This API does not
+ * define the output in such cases (though the output may be defined within the
+ * execution context - the server - where the expression is evaluated). Users of
+ * this API must mitigate any risk of applying an expression to some type where
+ * resulting behaviour is not defined by this API (for example, by checking for
+ * null, by ensuring that field types are correctly specified). Likewise, unless
+ * otherwise specified, this API does not define the order of evaluation for all
+ * arguments, and whether all arguments to some expression will be evaluated.
+ *
+ * @see Expressions
+ */
+@Evolving
+public interface Expression {
+
+}
diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/ExpressionCodecProvider.java b/driver-core/src/main/com/mongodb/client/model/expressions/ExpressionCodecProvider.java
new file mode 100644
index 00000000000..60afc3a6874
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/ExpressionCodecProvider.java
@@ -0,0 +1,53 @@
+/*
+ * 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 com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Immutable;
+import com.mongodb.lang.Nullable;
+import org.bson.codecs.Codec;
+import org.bson.codecs.configuration.CodecProvider;
+import org.bson.codecs.configuration.CodecRegistry;
+
+/**
+ * Provides Codec instances for MQL expressions.
+ *
+ *
Responsible for converting values and computations expressed using the
+ * driver's implementation of the {@link Expression} API into the corresponding
+ * values and computations expressed in MQL BSON. Booleans are converted to BSON
+ * booleans, documents to BSON documents, and so on. The specific structure
+ * representing numbers is preserved where possible (that is, number literals
+ * specified as Java longs are converted into BSON int64, and so on).
+ *
+ *
This API is marked Beta because it may be replaced with a generalized
+ * mechanism for converting expressions. This would only affect users who use
+ * MqlExpressionCodecProvider directly in custom codec providers. This Beta
+ * annotation does not imply that the Expressions API in general is Beta.
+ */
+@Beta(Beta.Reason.CLIENT)
+@Immutable
+public final class ExpressionCodecProvider implements CodecProvider {
+ @Override
+ @SuppressWarnings("unchecked")
+ @Nullable
+ public Codec get(final Class clazz, final CodecRegistry registry) {
+ if (MqlExpression.class.equals(clazz)) {
+ return (Codec) new MqlExpressionCodec(registry);
+ }
+ return null;
+ }
+}
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
new file mode 100644
index 00000000000..de96a8f7994
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/Expressions.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.bson.BsonBoolean;
+import org.bson.BsonInt32;
+import org.bson.BsonString;
+
+/**
+ * Convenience methods related to {@link Expression}.
+ */
+public final class Expressions {
+
+ private Expressions() {}
+
+ /**
+ * Returns an expression having the same boolean value as the provided
+ * boolean primitive.
+ *
+ * @param of the boolean primitive
+ * @return the boolean expression
+ */
+ public static BooleanExpression of(final boolean of) {
+ // we intentionally disallow ofBoolean(null)
+ return new MqlExpression<>((codecRegistry) -> new BsonBoolean(of));
+ }
+
+ /**
+ * Returns an expression having the same integer value as the provided
+ * int primitive.
+ *
+ * @param of the int primitive
+ * @return the integer expression
+ */
+ public static IntegerExpression of(final int of) {
+ return new MqlExpression<>((codecRegistry) -> new BsonInt32(of));
+ }
+
+ /**
+ * Returns an expression having the same string value as the provided
+ * string.
+ *
+ * @param of the string
+ * @return the string expression
+ */
+ public static StringExpression of(final String of) {
+ return new MqlExpression<>((codecRegistry) -> new BsonString(of));
+ }
+}
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
new file mode 100644
index 00000000000..6b53b9d62b8
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/IntegerExpression.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * Expresses an integer value.
+ */
+public interface IntegerExpression extends NumberExpression {
+
+}
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
new file mode 100644
index 00000000000..d52926338e5
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpression.java
@@ -0,0 +1,121 @@
+/*
+ * 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 org.bson.BsonArray;
+import org.bson.BsonDocument;
+import org.bson.BsonString;
+import org.bson.BsonValue;
+import org.bson.codecs.configuration.CodecRegistry;
+
+import java.util.function.Function;
+
+final class MqlExpression
+ implements Expression, BooleanExpression, IntegerExpression, NumberExpression,
+ StringExpression, DateExpression, DocumentExpression, ArrayExpression {
+
+ private final Function fn;
+
+ MqlExpression(final Function fn) {
+ this.fn = fn;
+ }
+
+ /**
+ * Exposes the evaluated BsonValue so that expressions may be used in
+ * aggregations. Non-public, as it is intended to be used only by the
+ * {@link MqlExpressionCodec}.
+ */
+ BsonValue toBsonValue(final CodecRegistry codecRegistry) {
+ return fn.apply(codecRegistry);
+ }
+
+ private Function astDoc(final String name, final BsonDocument value) {
+ return (cr) -> new BsonDocument(name, value);
+ }
+
+ private Function ast(final String name) {
+ return (cr) -> new BsonDocument(name, this.toBsonValue(cr));
+ }
+
+ private Function ast(final String name, final Expression param1) {
+ return (cr) -> {
+ BsonArray value = new BsonArray();
+ value.add(this.toBsonValue(cr));
+ value.add(extractBsonValue(cr, param1));
+ return new BsonDocument(name, value);
+ };
+ }
+
+ private Function ast(final String name, final Expression param1, final Expression param2) {
+ return (cr) -> {
+ BsonArray value = new BsonArray();
+ value.add(this.toBsonValue(cr));
+ value.add(extractBsonValue(cr, param1));
+ value.add(extractBsonValue(cr, param2));
+ return new BsonDocument(name, value);
+ };
+ }
+
+ /**
+ * Takes an expression and converts it to a BsonValue. MqlExpression will be
+ * the only implementation of Expression and all subclasses, so this will
+ * not mis-cast an expression as anything else.
+ */
+ private static BsonValue extractBsonValue(final CodecRegistry cr, final Expression expression) {
+ return ((MqlExpression>) expression).toBsonValue(cr);
+ }
+
+ /**
+ * Converts an MqlExpression to any subtype of Expression. Users must not
+ * extend Expression or its subtypes, so MqlExpression will implement any R.
+ */
+ @SuppressWarnings("unchecked")
+ private R assertImplementsAllExpressions() {
+ return (R) this;
+ }
+
+ private static R newMqlExpression(final Function ast) {
+ return new MqlExpression<>(ast).assertImplementsAllExpressions();
+ }
+
+ private R variable(final String variable) {
+ return newMqlExpression((cr) -> new BsonString(variable));
+ }
+
+ /** @see BooleanExpression */
+
+ @Override
+ public BooleanExpression not() {
+ return new MqlExpression<>(ast("$not"));
+ }
+
+ @Override
+ public BooleanExpression or(final BooleanExpression or) {
+ return new MqlExpression<>(ast("$or", or));
+ }
+
+ @Override
+ public BooleanExpression and(final BooleanExpression and) {
+ return new MqlExpression<>(ast("$and", and));
+ }
+
+ @Override
+ public R cond(final R left, final R right) {
+ return newMqlExpression(ast("$cond", left, right));
+ }
+
+}
diff --git a/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpressionCodec.java b/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpressionCodec.java
new file mode 100644
index 00000000000..877d9c8b26b
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/MqlExpressionCodec.java
@@ -0,0 +1,52 @@
+/*
+ * 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 org.bson.BsonReader;
+import org.bson.BsonValue;
+import org.bson.BsonWriter;
+import org.bson.codecs.Codec;
+import org.bson.codecs.DecoderContext;
+import org.bson.codecs.EncoderContext;
+import org.bson.codecs.configuration.CodecRegistry;
+
+@SuppressWarnings("rawtypes")
+final class MqlExpressionCodec implements Codec {
+ private final CodecRegistry codecRegistry;
+
+ MqlExpressionCodec(final CodecRegistry codecRegistry) {
+ this.codecRegistry = codecRegistry;
+ }
+
+ @Override
+ public MqlExpression decode(final BsonReader reader, final DecoderContext decoderContext) {
+ throw new UnsupportedOperationException("Decoding to an expression is not supported");
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked"})
+ public void encode(final BsonWriter writer, final MqlExpression value, final EncoderContext encoderContext) {
+ BsonValue bsonValue = value.toBsonValue(codecRegistry);
+ Codec codec = codecRegistry.get(bsonValue.getClass());
+ codec.encode(writer, bsonValue, encoderContext);
+ }
+
+ @Override
+ public Class getEncoderClass() {
+ return MqlExpression.class;
+ }
+}
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
new file mode 100644
index 00000000000..25084473853
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/NumberExpression.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * Expresses a numeric value.
+ */
+public interface NumberExpression extends Expression {
+
+}
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
new file mode 100644
index 00000000000..de1f5878b96
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/StringExpression.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+/**
+ * Expresses a string value.
+ */
+public interface StringExpression extends Expression {
+
+}
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
new file mode 100644
index 00000000000..596d642e654
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/model/expressions/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/**
+ * API for MQL expressions.
+ *
+ * @see com.mongodb.client.model.expressions.Expression
+ */
+@NonNullApi
+package com.mongodb.client.model.expressions;
+import com.mongodb.lang.NonNullApi;
diff --git a/driver-core/src/test/functional/com/mongodb/client/model/AggregatesTest.java b/driver-core/src/test/functional/com/mongodb/client/model/AggregatesTest.java
index 0afa1a5488a..ae29109a0c6 100644
--- a/driver-core/src/test/functional/com/mongodb/client/model/AggregatesTest.java
+++ b/driver-core/src/test/functional/com/mongodb/client/model/AggregatesTest.java
@@ -20,36 +20,21 @@
import com.mongodb.client.model.geojson.Position;
import org.bson.BsonDocument;
import org.bson.Document;
-
-import java.util.Collections;
-import java.util.List;
-
import org.bson.conversions.Bson;
import org.junit.jupiter.api.Test;
+import java.util.List;
+
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
-import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry;
-import static com.mongodb.client.model.GeoNearOptions.geoNearOptions;
import static com.mongodb.client.model.Aggregates.geoNear;
import static com.mongodb.client.model.Aggregates.unset;
+import static com.mongodb.client.model.GeoNearOptions.geoNearOptions;
import static java.util.Arrays.asList;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
public class AggregatesTest extends OperationTest {
- private List assertPipeline(final String stageAsString, final Bson stage) {
- BsonDocument expectedStage = BsonDocument.parse(stageAsString);
- List pipeline = Collections.singletonList(stage);
- assertEquals(expectedStage, pipeline.get(0).toBsonDocument(BsonDocument.class, getDefaultCodecRegistry()));
- return pipeline;
- }
- private void assertResults(final List pipeline, final String s) {
- List expectedResults = parseToList(s);
- List results = getCollectionHelper().aggregate(pipeline);
- assertEquals(expectedResults, results);
- }
@Test
public void testUnset() {
diff --git a/driver-core/src/test/functional/com/mongodb/client/model/OperationTest.java b/driver-core/src/test/functional/com/mongodb/client/model/OperationTest.java
index ac0558f3c34..885b95aaea5 100644
--- a/driver-core/src/test/functional/com/mongodb/client/model/OperationTest.java
+++ b/driver-core/src/test/functional/com/mongodb/client/model/OperationTest.java
@@ -22,12 +22,13 @@
import com.mongodb.internal.connection.ServerHelper;
import org.bson.BsonArray;
import org.bson.BsonDocument;
-import org.bson.Document;
+import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.DecoderContext;
-import org.bson.codecs.DocumentCodec;
+import org.bson.conversions.Bson;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -36,6 +37,7 @@
import static com.mongodb.ClusterFixture.getBinding;
import static com.mongodb.ClusterFixture.getPrimary;
import static com.mongodb.MongoClientSettings.getDefaultCodecRegistry;
+import static org.junit.jupiter.api.Assertions.assertEquals;
public abstract class OperationTest {
@@ -53,12 +55,12 @@ public void afterEach() {
ServerHelper.checkPool(getPrimary());
}
- CollectionHelper getCollectionHelper() {
+ protected CollectionHelper getCollectionHelper() {
return getCollectionHelper(getNamespace());
}
- private CollectionHelper getCollectionHelper(final MongoNamespace namespace) {
- return new CollectionHelper<>(new DocumentCodec(), namespace);
+ private CollectionHelper getCollectionHelper(final MongoNamespace namespace) {
+ return new CollectionHelper<>(new BsonDocumentCodec(), namespace);
}
private String getDatabaseName() {
@@ -73,11 +75,29 @@ MongoNamespace getNamespace() {
return new MongoNamespace(getDatabaseName(), getCollectionName());
}
- public static List parseToList(final String s) {
- return BsonArray.parse(s).stream().map(v -> toDocument(v.asDocument())).collect(Collectors.toList());
+ private static List parseToList(final String s) {
+ return BsonArray.parse(s).stream().map(v -> toBsonDocument(v.asDocument())).collect(Collectors.toList());
}
- public static Document toDocument(final BsonDocument bsonDocument) {
- return getDefaultCodecRegistry().get(Document.class).decode(bsonDocument.asBsonReader(), DecoderContext.builder().build());
+ public static BsonDocument toBsonDocument(final BsonDocument bsonDocument) {
+ return getDefaultCodecRegistry().get(BsonDocument.class).decode(bsonDocument.asBsonReader(), DecoderContext.builder().build());
+ }
+
+
+ protected List assertPipeline(final String stageAsString, final Bson stage) {
+ List pipeline = Collections.singletonList(stage);
+ return assertPipeline(stageAsString, pipeline);
+ }
+
+ protected List assertPipeline(final String stageAsString, final List pipeline) {
+ BsonDocument expectedStage = BsonDocument.parse(stageAsString);
+ assertEquals(expectedStage, pipeline.get(0).toBsonDocument(BsonDocument.class, getDefaultCodecRegistry()));
+ return pipeline;
+ }
+
+ protected void assertResults(final List pipeline, final String expectedResultsAsString) {
+ List expectedResults = parseToList(expectedResultsAsString);
+ List results = getCollectionHelper().aggregate(pipeline);
+ assertEquals(expectedResults, results);
}
}
diff --git a/driver-core/src/test/functional/com/mongodb/client/model/expressions/AbstractExpressionsFunctionalTest.java b/driver-core/src/test/functional/com/mongodb/client/model/expressions/AbstractExpressionsFunctionalTest.java
new file mode 100644
index 00000000000..59171c6f0b2
--- /dev/null
+++ b/driver-core/src/test/functional/com/mongodb/client/model/expressions/AbstractExpressionsFunctionalTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 com.mongodb.client.model.Field;
+import com.mongodb.client.model.OperationTest;
+import org.bson.BsonArray;
+import org.bson.BsonDocument;
+import org.bson.BsonReader;
+import org.bson.BsonString;
+import org.bson.BsonValue;
+import org.bson.Document;
+import org.bson.codecs.BsonDocumentCodec;
+import org.bson.codecs.BsonValueCodecProvider;
+import org.bson.codecs.DecoderContext;
+import org.bson.conversions.Bson;
+import org.bson.json.JsonReader;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.mongodb.ClusterFixture.serverVersionAtLeast;
+import static com.mongodb.client.model.Aggregates.addFields;
+import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public abstract class AbstractExpressionsFunctionalTest extends OperationTest {
+
+ @BeforeEach
+ public void setUp() {
+ getCollectionHelper().drop();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ getCollectionHelper().drop();
+ }
+
+ protected void assertExpression(final Object expectedResult, final Expression expression, final String expectedMql) {
+ assertEval(expectedResult, expression);
+
+ BsonValue expressionValue = ((MqlExpression>) expression).toBsonValue(fromProviders(new BsonValueCodecProvider()));
+ BsonValue bsonValue = new BsonDocumentFragmentCodec().readValue(
+ new JsonReader(expectedMql),
+ DecoderContext.builder().build());
+ assertEquals(bsonValue, expressionValue, expressionValue.toString().replace("\"", "'"));
+ }
+
+ private void assertEval(final Object expected, final Expression toEvaluate) {
+ BsonValue evaluated = evaluate(toEvaluate);
+ assertEquals(new Document("val", expected).toBsonDocument().get("val"), evaluated);
+ }
+
+ protected BsonValue evaluate(final Expression toEvaluate) {
+ Bson addFieldsStage = addFields(new Field<>("val", toEvaluate));
+ List stages = new ArrayList<>();
+ stages.add(addFieldsStage);
+ List results;
+ if (getCollectionHelper().count() == 0) {
+ BsonDocument document = new BsonDocument("val", new BsonString("#invalid string#"));
+ if (serverVersionAtLeast(5, 1)) {
+ Bson documentsStage = new BsonDocument("$documents", new BsonArray(Arrays.asList(document)));
+ stages.add(0, documentsStage);
+ results = getCollectionHelper().aggregateDb(stages);
+ } else {
+ getCollectionHelper().insertDocuments(document);
+ results = getCollectionHelper().aggregate(stages);
+ getCollectionHelper().drop();
+ }
+ } else {
+ results = getCollectionHelper().aggregate(stages);
+ }
+ BsonValue evaluated = results.get(0).get("val");
+ return evaluated;
+ }
+
+ private static class BsonDocumentFragmentCodec extends BsonDocumentCodec {
+ public BsonValue readValue(final BsonReader reader, final DecoderContext decoderContext) {
+ reader.readBsonType();
+ return super.readValue(reader, decoderContext);
+ }
+ }
+}
+
diff --git a/driver-core/src/test/functional/com/mongodb/client/model/expressions/BooleanExpressionsFunctionalTest.java b/driver-core/src/test/functional/com/mongodb/client/model/expressions/BooleanExpressionsFunctionalTest.java
new file mode 100644
index 00000000000..af0ebdbff37
--- /dev/null
+++ b/driver-core/src/test/functional/com/mongodb/client/model/expressions/BooleanExpressionsFunctionalTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+
+@SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions", "ConstantConditionalExpression"})
+class BooleanExpressionsFunctionalTest extends AbstractExpressionsFunctionalTest {
+ // https://www.mongodb.com/docs/manual/reference/operator/aggregation/#boolean-expression-operators
+ // (Complete as of 6.0)
+
+ private final BooleanExpression tru = Expressions.of(true);
+ private final BooleanExpression fal = Expressions.of(false);
+
+ @Test
+ public void literalsTest() {
+ assertExpression(true, tru, "true");
+ assertExpression(false, fal, "false");
+ }
+
+ @Test
+ public void orTest() {
+ // https://www.mongodb.com/docs/manual/reference/operator/aggregation/or/
+ assertExpression(true || false, tru.or(fal), "{'$or': [true, false]}");
+ assertExpression(false || true, fal.or(tru), "{'$or': [false, true]}");
+ }
+
+ @Test
+ public void andTest() {
+ // https://www.mongodb.com/docs/manual/reference/operator/aggregation/and/
+ assertExpression(true && false, tru.and(fal), "{'$and': [true, false]}");
+ assertExpression(false && true, fal.and(tru), "{'$and': [false, true]}");
+ }
+
+ @Test
+ public void notTest() {
+ // https://www.mongodb.com/docs/manual/reference/operator/aggregation/not/
+ assertExpression(!true, tru.not(), "{'$not': true}");
+ assertExpression(!false, fal.not(), "{'$not': false}");
+ }
+
+ @Test
+ public void condTest() {
+ // https://www.mongodb.com/docs/manual/reference/operator/aggregation/cond/
+ StringExpression abc = Expressions.of("abc");
+ StringExpression xyz = Expressions.of("xyz");
+ NumberExpression nnn = Expressions.of(123);
+ assertExpression(
+ true && false ? "abc" : "xyz",
+ tru.and(fal).cond(abc, xyz),
+ "{'$cond': [{'$and': [true, false]}, 'abc', 'xyz']}");
+ assertExpression(
+ true || false ? "abc" : "xyz",
+ tru.or(fal).cond(abc, xyz),
+ "{'$cond': [{'$or': [true, false]}, 'abc', 'xyz']}");
+ assertExpression(
+ false ? "abc" : 123,
+ fal.cond(abc, nnn),
+ "{'$cond': [false, 'abc', 123]}");
+ }
+}
diff --git a/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java b/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java
index 178c5cbe792..c8478d308b0 100644
--- a/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java
+++ b/driver-core/src/test/functional/com/mongodb/client/test/CollectionHelper.java
@@ -33,6 +33,7 @@
import com.mongodb.internal.bulk.InsertRequest;
import com.mongodb.internal.bulk.UpdateRequest;
import com.mongodb.internal.bulk.WriteRequest;
+import com.mongodb.internal.client.model.AggregationLevel;
import com.mongodb.internal.operation.AggregateOperation;
import com.mongodb.internal.operation.BatchCursor;
import com.mongodb.internal.operation.CommandReadOperation;
@@ -282,12 +283,20 @@ public List aggregate(final List pipeline) {
}
public List aggregate(final List pipeline, final Decoder decoder) {
+ return aggregate(pipeline, decoder, AggregationLevel.COLLECTION);
+ }
+
+ public List aggregateDb(final List pipeline) {
+ return aggregate(pipeline, codec, AggregationLevel.DATABASE);
+ }
+
+ private List aggregate(final List pipeline, final Decoder decoder, final AggregationLevel level) {
List bsonDocumentPipeline = new ArrayList();
for (Bson cur : pipeline) {
bsonDocumentPipeline.add(cur.toBsonDocument(Document.class, registry));
}
- BatchCursor cursor = new AggregateOperation(namespace, bsonDocumentPipeline, decoder)
- .execute(getBinding());
+ BatchCursor cursor = new AggregateOperation(namespace, bsonDocumentPipeline, decoder, level)
+ .execute(getBinding());
List results = new ArrayList();
while (cursor.hasNext()) {
results.addAll(cursor.next());