diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
index f55454ed6..89d769066 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
@@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.List;
import org.bson.Document;
+import org.bson.conversions.Bson;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.aggregation.AddFieldsOperation.AddFieldsOperationBuilder;
@@ -238,6 +239,40 @@ public class Aggregation {
return AddFieldsOperation.builder();
}
+ /**
+ * Creates a new {@link AggregationOperation} taking the given {@link Bson bson value} as is.
+ *
+ *
+ * Aggregation.stage(Aggregates.search(exists(fieldPath("..."))));
+ *
+ *
+ * Field mapping against a potential domain type or previous aggregation stages will not happen.
+ *
+ * @param aggregationOperation the must not be {@literal null}.
+ * @return new instance of {@link AggregationOperation}.
+ * @since 4.0
+ */
+ public static AggregationOperation stage(Bson aggregationOperation) {
+ return new BasicAggregationOperation(aggregationOperation);
+ }
+
+ /**
+ * Creates a new {@link AggregationOperation} taking the given {@link String json value} as is.
+ * Aggregation.stage("{ $search : { near : { path : 'released' , origin : ... } } }");
+ *
+ *
+ * Field mapping against a potential domain type or previous aggregation stages will not happen.
+ *
+ * @param json the JSON representation of the pipeline stage. Must not be {@literal null}.
+ * @return new instance of {@link AggregationOperation}.
+ * @since 4.0
+ */
+ public static AggregationOperation stage(String json) {
+ return new BasicAggregationOperation(json);
+ }
+
/**
* Creates a new {@link ProjectionOperation} including the given fields.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java
index 4ddb04047..83d26576c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationOperationContext.java
@@ -20,12 +20,16 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import org.bson.Document;
+import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.beans.BeanUtils;
+import org.springframework.data.mongodb.CodecRegistryProvider;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
+import com.mongodb.MongoClientSettings;
+
/**
* The context for an {@link AggregationOperation}.
*
@@ -33,7 +37,7 @@ import org.springframework.util.ReflectionUtils;
* @author Christoph Strobl
* @since 1.3
*/
-public interface AggregationOperationContext {
+public interface AggregationOperationContext extends CodecRegistryProvider {
/**
* Returns the mapped {@link Document}, potentially converting the source considering mapping metadata etc.
@@ -114,4 +118,9 @@ public interface AggregationOperationContext {
default AggregationOperationContext continueOnMissingFieldReference() {
return this;
}
+
+ @Override
+ default CodecRegistry getCodecRegistry() {
+ return MongoClientSettings.getDefaultCodecRegistry();
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BasicAggregationOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BasicAggregationOperation.java
new file mode 100644
index 000000000..9f4eeb223
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BasicAggregationOperation.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2022 the original author or authors.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core.aggregation;
+
+import java.util.Map;
+
+import org.bson.Document;
+import org.bson.conversions.Bson;
+import org.springframework.data.mongodb.util.BsonUtils;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * {@link AggregationOperation} implementation that c
+ *
+ * @author Christoph Strobl
+ * @since 4.0
+ */
+class BasicAggregationOperation implements AggregationOperation {
+
+ private final Object value;
+
+ BasicAggregationOperation(Object value) {
+ this.value = value;
+ }
+
+ @Override
+ public Document toDocument(AggregationOperationContext context) {
+
+ if (value instanceof Document document) {
+ return document;
+ }
+
+ if (value instanceof Bson bson) {
+ return BsonUtils.asDocument(bson, context.getCodecRegistry());
+ }
+
+ if (value instanceof Map map) {
+ return new Document(map);
+ }
+
+ if (value instanceof String json && BsonUtils.isJsonDocument(json)) {
+ return BsonUtils.parse(json, context);
+ }
+
+ throw new IllegalStateException(
+ String.format("%s cannot be converted to org.bson.Document.", ObjectUtils.nullSafeClassName(value)));
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java
index 0da9b7c3c..fdf909ac7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ExposedFieldsAggregationOperationContext.java
@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
+import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
@@ -141,4 +142,9 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
AggregationOperationContext getRootContext() {
return rootContext;
}
+
+ @Override
+ public CodecRegistry getCodecRegistry() {
+ return getRootContext().getCodecRegistry();
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java
index 30fdadd7e..2b0ff9d15 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/NestedDelegatingExpressionAggregationOperationContext.java
@@ -19,6 +19,7 @@ import java.util.Collection;
import org.bson.Document;
+import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExpressionFieldReference;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.util.Assert;
@@ -92,4 +93,9 @@ class NestedDelegatingExpressionAggregationOperationContext implements Aggregati
public Fields getFields(Class> type) {
return delegate.getFields(type);
}
+
+ @Override
+ public CodecRegistry getCodecRegistry() {
+ return delegate.getCodecRegistry();
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java
index 500106a27..bf4b0d2dc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/PrefixingDelegatingAggregationOperationContext.java
@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.Set;
import org.bson.Document;
+import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.lang.Nullable;
@@ -80,6 +81,11 @@ public class PrefixingDelegatingAggregationOperationContext implements Aggregati
return delegate.getFields(type);
}
+ @Override
+ public CodecRegistry getCodecRegistry() {
+ return delegate.getCodecRegistry();
+ }
+
@SuppressWarnings("unchecked")
private Document doPrefix(Document source) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java
index c190c5948..7dd07e594 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContext.java
@@ -22,6 +22,7 @@ import java.util.List;
import org.bson.Document;
+import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
@@ -147,4 +148,9 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
public Class> getType() {
return type;
}
+
+ @Override
+ public CodecRegistry getCodecRegistry() {
+ return this.mapper.getConverter().getCodecRegistry();
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index 9cdb5d9ce..e0f5b53ea 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -26,6 +26,7 @@ import org.apache.commons.logging.LogFactory;
import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
+import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.bson.json.JsonReader;
import org.bson.types.ObjectId;
@@ -1793,6 +1794,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return conversions.getCustomWriteTarget(source).orElse(source);
}
+ @Override
+ public CodecRegistry getCodecRegistry() {
+ return codecRegistryProvider != null ? codecRegistryProvider.getCodecRegistry() : super.getCodecRegistry();
+ }
+
/**
* Create a new {@link MappingMongoConverter} using the given {@link MongoDatabaseFactory} when loading {@link DBRef}.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
index f88e3195b..dca4f5324 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
@@ -15,16 +15,18 @@
*/
package org.springframework.data.mongodb.core.convert;
+import com.mongodb.MongoClientSettings;
import org.bson.BsonValue;
import org.bson.Document;
+import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
-
import org.springframework.core.convert.ConversionException;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.EntityConverter;
import org.springframework.data.convert.EntityReader;
import org.springframework.data.convert.TypeMapper;
+import org.springframework.data.mongodb.CodecRegistryProvider;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.util.BsonUtils;
@@ -48,7 +50,7 @@ import com.mongodb.DBRef;
*/
public interface MongoConverter
extends EntityConverter