Browse Source

Polishing.

Original Pull Request: #4885
pull/4955/head
Mark Paluch 8 months ago committed by Christoph Strobl
parent
commit
e89548a846
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 50
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
  2. 17
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java
  3. 31
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
  4. 37
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java
  5. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  6. 27
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java
  7. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/Encryption.java
  8. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Queryable.java
  9. 11
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/RangeEncrypted.java
  10. 20
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/QueryCharacteristics.java
  11. 10
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java
  12. 37
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java

50
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java

@ -24,12 +24,12 @@ import java.util.Map; @@ -24,12 +24,12 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.bson.BsonBinary;
import org.bson.BsonBinarySubType;
import org.bson.BsonNull;
import org.bson.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty;
@ -43,7 +43,6 @@ import org.springframework.data.mongodb.core.validation.Validator; @@ -43,7 +43,6 @@ import org.springframework.data.mongodb.core.validation.Validator;
import org.springframework.data.util.Optionals;
import org.springframework.lang.CheckReturnValue;
import org.springframework.lang.Contract;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@ -241,11 +240,11 @@ public class CollectionOptions { @@ -241,11 +240,11 @@ public class CollectionOptions {
* Create new {@link CollectionOptions} with already given settings and {@code validationOptions} set to given
* {@link MongoJsonSchema}.
*
* @param schema can be {@literal null}.
* @param schema must not be {@literal null}.
* @return new {@link CollectionOptions}.
* @since 2.1
*/
public CollectionOptions schema(@Nullable MongoJsonSchema schema) {
public CollectionOptions schema(MongoJsonSchema schema) {
return validator(Validator.schema(schema));
}
@ -473,7 +472,7 @@ public class CollectionOptions { @@ -473,7 +472,7 @@ public class CollectionOptions {
* Get the {@code encryptedFields} if available.
*
* @return {@link Optional#empty()} if not specified.
* @since 4.5.0
* @since 4.5
*/
public Optional<EncryptedFieldsOptions> getEncryptedFieldsOptions() {
return Optional.ofNullable(encryptedFieldsOptions);
@ -552,7 +551,8 @@ public class CollectionOptions { @@ -552,7 +551,8 @@ public class CollectionOptions {
private final @Nullable ValidationLevel validationLevel;
private final @Nullable ValidationAction validationAction;
public ValidationOptions(Validator validator, ValidationLevel validationLevel, ValidationAction validationAction) {
public ValidationOptions(@Nullable Validator validator, @Nullable ValidationLevel validationLevel,
@Nullable ValidationAction validationAction) {
this.validator = validator;
this.validationLevel = validationLevel;
@ -677,8 +677,19 @@ public class CollectionOptions { @@ -677,8 +677,19 @@ public class CollectionOptions {
private static final EncryptedFieldsOptions NONE = new EncryptedFieldsOptions();
private @Nullable MongoJsonSchema schema;
private List<QueryableJsonSchemaProperty> queryableProperties;
private final @Nullable MongoJsonSchema schema;
private final List<QueryableJsonSchemaProperty> queryableProperties;
EncryptedFieldsOptions() {
this(null, List.of());
}
private EncryptedFieldsOptions(@Nullable MongoJsonSchema schema,
List<QueryableJsonSchemaProperty> queryableProperties) {
this.schema = schema;
this.queryableProperties = queryableProperties;
}
/**
* @return {@link EncryptedFieldsOptions#NONE}
@ -701,17 +712,6 @@ public class CollectionOptions { @@ -701,17 +712,6 @@ public class CollectionOptions {
return new EncryptedFieldsOptions(null, List.copyOf(properties));
}
EncryptedFieldsOptions() {
this(null, List.of());
}
private EncryptedFieldsOptions(@Nullable MongoJsonSchema schema,
List<QueryableJsonSchemaProperty> queryableProperties) {
this.schema = schema;
this.queryableProperties = queryableProperties;
}
/**
* Add a new {@link QueryableJsonSchemaProperty queryable property} for the given source property.
* <p>
@ -739,7 +739,6 @@ public class CollectionOptions { @@ -739,7 +739,6 @@ public class CollectionOptions {
return new Document("fields", selectPaths());
}
@NonNull
private List<Document> selectPaths() {
Map<String, Document> fields = new LinkedHashMap<>();
@ -760,10 +759,13 @@ public class CollectionOptions { @@ -760,10 +759,13 @@ public class CollectionOptions {
List<Document> converted = new ArrayList<>(queryableProperties.size());
for (QueryableJsonSchemaProperty property : queryableProperties) {
Document field = new Document("path", property.getIdentifier());
if (!property.getTypes().isEmpty()) {
field.append("bsonType", property.getTypes().iterator().next().toBsonType().value());
}
if (property
.getTargetProperty() instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted) {
if (encrypted.getKeyId() != null) {
@ -775,11 +777,13 @@ public class CollectionOptions { @@ -775,11 +777,13 @@ public class CollectionOptions {
}
}
}
field.append("queries", property.getCharacteristics().getCharacteristics().stream()
.map(QueryCharacteristic::toDocument).collect(Collectors.toList()));
field.append("queries", property.getCharacteristics().map(QueryCharacteristic::toDocument).toList());
if (!field.containsKey("keyId")) {
field.append("keyId", BsonNull.VALUE);
}
converted.add(field);
}
return converted;
@ -813,7 +817,7 @@ public class CollectionOptions { @@ -813,7 +817,7 @@ public class CollectionOptions {
}
}
private static void collectPaths(Document document, String currentPath, Map<String, Document> paths) {
private static void collectPaths(Document document, @Nullable String currentPath, Map<String, Document> paths) {
if (document.containsKey("type") && document.get("type").equals("object")) {
Object o = document.get("properties");

17
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java

@ -22,6 +22,7 @@ import java.util.LinkedHashMap; @@ -22,6 +22,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import org.bson.BsonNull;
import org.bson.Document;
@ -377,14 +378,16 @@ class EntityOperations { @@ -377,14 +378,16 @@ class EntityOperations {
result.timeSeriesOptions(options);
});
collectionOptions.getChangeStreamOptions().ifPresent(it -> result
.changeStreamPreAndPostImagesOptions(new ChangeStreamPreAndPostImagesOptions(it.getPreAndPostImages())));
collectionOptions.getChangeStreamOptions() //
.map(CollectionOptions.CollectionChangeStreamOptions::getPreAndPostImages) //
.map(ChangeStreamPreAndPostImagesOptions::new) //
.ifPresent(result::changeStreamPreAndPostImagesOptions);
collectionOptions.getEncryptedFieldsOptions() //
.map(EncryptedFieldsOptions::toDocument) //
.filter(Predicate.not(Document::isEmpty)) //
.ifPresent(result::encryptedFields);
collectionOptions.getEncryptedFieldsOptions().map(EncryptedFieldsOptions::toDocument).ifPresent(encryptedFields -> {
if (!encryptedFields.isEmpty()) {
result.encryptedFields(encryptedFields);
}
});
return result;
}

31
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java

@ -24,6 +24,7 @@ import java.util.function.Predicate; @@ -24,6 +24,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@ -32,7 +33,6 @@ import org.springframework.data.mongodb.core.mapping.Field; @@ -32,7 +33,6 @@ import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.Queryable;
import org.springframework.data.mongodb.core.mapping.RangeEncrypted;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ArrayJsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ObjectJsonSchemaProperty;
@ -126,9 +126,18 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { @@ -126,9 +126,18 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
MongoPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(type);
MongoJsonSchemaBuilder schemaBuilder = MongoJsonSchema.builder();
{
Encrypted encrypted = entity.findAnnotation(Encrypted.class);
if (encrypted != null) {
schemaBuilder.encryptionMetadata(getEncryptionMetadata(entity, encrypted));
}
List<JsonSchemaProperty> schemaProperties = computePropertiesForEntity(Collections.emptyList(), entity);
schemaBuilder.properties(schemaProperties.toArray(new JsonSchemaProperty[0]));
return schemaBuilder.build();
}
private static Document getEncryptionMetadata(MongoPersistentEntity<?> entity, Encrypted encrypted) {
Document encryptionMetadata = new Document();
@ -141,14 +150,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { @@ -141,14 +150,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
encryptionMetadata.append("algorithm", encrypted.algorithm());
}
schemaBuilder.encryptionMetadata(encryptionMetadata);
}
}
List<JsonSchemaProperty> schemaProperties = computePropertiesForEntity(Collections.emptyList(), entity);
schemaBuilder.properties(schemaProperties.toArray(new JsonSchemaProperty[0]));
return schemaBuilder.build();
return encryptionMetadata;
}
private List<JsonSchemaProperty> computePropertiesForEntity(List<MongoPersistentProperty> path,
@ -190,8 +192,8 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { @@ -190,8 +192,8 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
Class<?> rawTargetType = computeTargetType(property); // target type before conversion
Class<?> targetType = converter.getTypeMapper().getWriteTargetTypeFor(rawTargetType); // conversion target type
if ((rawTargetType.isPrimitive() || ClassUtils.isPrimitiveArray(rawTargetType)) && targetType == Object.class || ClassUtils.isAssignable(targetType, rawTargetType) ) {
if ((rawTargetType.isPrimitive() || ClassUtils.isPrimitiveArray(rawTargetType)) && targetType == Object.class
|| ClassUtils.isAssignable(targetType, rawTargetType)) {
targetType = rawTargetType;
}
@ -317,14 +319,15 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator { @@ -317,14 +319,15 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
if (queryable.contentionFactor() >= 0) {
options.put("contention", queryable.contentionFactor());
}
if (!queryable.queryAttributes().isEmpty()) {
if (StringUtils.hasText(queryable.queryAttributes())) {
options.putAll(Document.parse(queryable.queryAttributes()));
}
return options;
}
};
return new QueryableJsonSchemaProperty(enc, QueryCharacteristics.of(List.of(characteristic)));
return new QueryableJsonSchemaProperty(enc, QueryCharacteristics.of(characteristic));
}
private JsonSchemaProperty createObjectSchemaPropertyForEntity(List<MongoPersistentProperty> path,

37
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConversionContext.java

@ -79,7 +79,6 @@ public class MongoConversionContext implements ValueConversionContext<MongoPersi @@ -79,7 +79,6 @@ public class MongoConversionContext implements ValueConversionContext<MongoPersi
}
/**
*
* @param operatorContext
* @return new instance of {@link MongoConversionContext}.
* @since 4.5
@ -126,35 +125,27 @@ public class MongoConversionContext implements ValueConversionContext<MongoPersi @@ -126,35 +125,27 @@ public class MongoConversionContext implements ValueConversionContext<MongoPersi
/**
* The operator the conversion is used in.
*
* @return {@literal write} for simple write operations during save, or a query operator.
*/
String getOperator();
String operator();
/**
* The context path the operator is used in.
*
* @return never {@literal null}.
*/
String getPath();
String path();
boolean isWriteOperation();
}
public static class WriteOperatorContext implements OperatorContext {
private final String path;
public WriteOperatorContext(String path) {
this.path = path;
}
@Override
public String getOperator() {
return "write";
}
record WriteOperatorContext(String path) implements OperatorContext {
@Override
public String getPath() {
return path;
public String operator() {
return "write";
}
@Override
@ -163,27 +154,17 @@ public class MongoConversionContext implements ValueConversionContext<MongoPersi @@ -163,27 +154,17 @@ public class MongoConversionContext implements ValueConversionContext<MongoPersi
}
}
public static class QueryOperatorContext implements OperatorContext {
private final String operator;
private final String path;
record QueryOperatorContext(String operator, String path) implements OperatorContext {
public QueryOperatorContext(@Nullable String operator, String path) {
this.operator = operator != null ? operator : "$eq";
this.path = path;
}
public String getOperator() {
return operator;
}
public String getPath() {
return path;
}
@Override
public boolean isWriteOperation() {
return false;
}
}
}

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

@ -711,7 +711,8 @@ public class QueryMapper { @@ -711,7 +711,8 @@ public class QueryMapper {
return BsonUtils.mapValues(document, (key, val) -> {
if (isKeyword(key)) {
return convertValueWithConversionContext(documentField, val, val, valueConverter, conversionContext.forOperator(new QueryOperatorContext(key, conversionContext.getOperatorContext().getPath())));
return convertValueWithConversionContext(documentField, val, val, valueConverter, conversionContext
.forOperator(new QueryOperatorContext(key, conversionContext.getOperatorContext().path())));
}
return val;
});

27
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/encryption/MongoEncryptionConverter.java

@ -15,9 +15,9 @@ @@ -15,9 +15,9 @@
*/
package org.springframework.data.mongodb.core.convert.encryption;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.springframework.data.mongodb.core.encryption.EncryptionOptions.QueryableEncryptionOptions;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static org.springframework.data.mongodb.core.encryption.EncryptionOptions.*;
import java.util.Collection;
import java.util.LinkedHashMap;
@ -32,6 +32,7 @@ import org.bson.BsonDocument; @@ -32,6 +32,7 @@ import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.types.Binary;
import org.springframework.core.CollectionFactory;
import org.springframework.data.mongodb.core.convert.MongoConversionContext;
import org.springframework.data.mongodb.core.convert.MongoConversionContext.OperatorContext;
@ -59,8 +60,8 @@ import org.springframework.util.StringUtils; @@ -59,8 +60,8 @@ import org.springframework.util.StringUtils;
public class MongoEncryptionConverter implements EncryptingConverter<Object, Object> {
private static final Log LOGGER = LogFactory.getLog(MongoEncryptionConverter.class);
private static final String EQUALITY_OPERATOR = "$eq";
private static final List<String> RANGE_OPERATORS = asList("$gt", "$gte", "$lt", "$lte");
public static final String AND_OPERATOR = "$and";
private final Encryption<BsonValue, BsonBinary> encryption;
private final EncryptionKeyResolver keyResolver;
@ -189,7 +190,7 @@ public class MongoEncryptionConverter implements EncryptingConverter<Object, Obj @@ -189,7 +190,7 @@ public class MongoEncryptionConverter implements EncryptingConverter<Object, Obj
}
private static @Nullable QueryableEncryptionOptions getEQOptions(MongoPersistentProperty persistentProperty,
OperatorContext operatorContext) {
@Nullable OperatorContext operatorContext) {
Queryable queryableAnnotation = persistentProperty.findAnnotation(Queryable.class);
if (queryableAnnotation == null || !StringUtils.hasText(queryableAnnotation.queryType())) {
@ -248,29 +249,29 @@ public class MongoEncryptionConverter implements EncryptingConverter<Object, Obj @@ -248,29 +249,29 @@ public class MongoEncryptionConverter implements EncryptingConverter<Object, Obj
* The mongodb-crypt {@code encryptExpression} has strict formatting requirements so this method ensures these
* requirements are met and then picks out and returns just the value for use with a range query.
*
* @param fieldNameAndQueryOperator field name and query operator
* @param value the value of the expression to be encrypted
* @param encryptionOptions the options
* @return the encrypted range value for use in a range query
* @param operatorContext field name and query operator.
* @param value the value of the expression to be encrypted.
* @param encryptionOptions the options.
* @return the encrypted range value for use in a range query.
*/
private BsonValue encryptExpression(OperatorContext operatorContext, Object value,
EncryptionOptions encryptionOptions) {
BsonValue doc = BsonUtils.simpleToBsonValue(value);
String fieldName = operatorContext.getPath();
String queryOperator = operatorContext.getOperator();
String fieldName = operatorContext.path();
String queryOperator = operatorContext.operator();
if (!RANGE_OPERATORS.contains(queryOperator)) {
throw new AssertionError(String.format("Not a valid range query. Querying a range encrypted field but the "
+ "query operator '%s' for field path '%s' is not a range query.", queryOperator, fieldName));
}
BsonDocument encryptExpression = new BsonDocument("$and",
BsonDocument encryptExpression = new BsonDocument(AND_OPERATOR,
new BsonArray(singletonList(new BsonDocument(fieldName, new BsonDocument(queryOperator, doc)))));
BsonDocument result = encryption.encryptExpression(encryptExpression, encryptionOptions);
return result.getArray("$and").get(0).asDocument().getDocument(fieldName).getBinary(queryOperator);
return result.getArray(AND_OPERATOR).get(0).asDocument().getDocument(fieldName).getBinary(queryOperator);
}
private BsonValue collectionLikeToBsonValue(Object value, MongoPersistentProperty property,

8
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/encryption/Encryption.java

@ -20,11 +20,13 @@ import org.bson.BsonDocument; @@ -20,11 +20,13 @@ import org.bson.BsonDocument;
/**
* Component responsible for encrypting and decrypting values.
*
* @param <P> plaintext type.
* @param <C> ciphertext type.
* @author Christoph Strobl
* @author Ross Lawley
* @since 4.1
*/
public interface Encryption<S, T> {
public interface Encryption<P, C> {
/**
* Encrypt the given value.
@ -33,7 +35,7 @@ public interface Encryption<S, T> { @@ -33,7 +35,7 @@ public interface Encryption<S, T> {
* @param options must not be {@literal null}.
* @return the encrypted value.
*/
T encrypt(S value, EncryptionOptions options);
C encrypt(P value, EncryptionOptions options);
/**
* Decrypt the given value.
@ -41,7 +43,7 @@ public interface Encryption<S, T> { @@ -41,7 +43,7 @@ public interface Encryption<S, T> {
* @param value must not be {@literal null}.
* @return the decrypted value.
*/
S decrypt(T value);
P decrypt(C value);
/**
* Encrypt the given expression.

5
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Queryable.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2025. the original author or authors.
* Copyright 2025 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.
@ -30,13 +30,11 @@ public @interface Queryable { @@ -30,13 +30,11 @@ public @interface Queryable {
/**
* @return empty {@link String} if not set.
* @since 4.5
*/
String queryType() default "";
/**
* @return empty {@link String} if not set.
* @since 4.5
*/
String queryAttributes() default "";
@ -46,4 +44,5 @@ public @interface Queryable { @@ -46,4 +44,5 @@ public @interface Queryable {
* @return the contention factor
*/
long contentionFactor() default -1;
}

11
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/RangeEncrypted.java

@ -34,7 +34,7 @@ import org.springframework.core.annotation.AliasFor; @@ -34,7 +34,7 @@ import org.springframework.core.annotation.AliasFor;
public @interface RangeEncrypted {
/**
* Set the contention factor
* Set the contention factor.
*
* @return the contention factor
*/
@ -42,15 +42,16 @@ public @interface RangeEncrypted { @@ -42,15 +42,16 @@ public @interface RangeEncrypted {
long contentionFactor() default -1;
/**
* Set the {@literal range} options
* Set the {@literal range} options.
* <p>
* Should be valid extended json representing the range options and including the following values: {@code min},
* {@code max}, {@code trimFactor} and {@code sparsity}.
* Should be valid extended {@link org.bson.Document#parse(String) JSON} representing the range options and including
* the following values: {@code min}, {@code max}, {@code trimFactor} and {@code sparsity}.
* <p>
* Please note that values are data type sensitive and may require proper identification via eg. {@code $numberLong}.
*
* @return the json representation of range options
* @return the {@link org.bson.Document#parse(String) JSON} representation of range options.
*/
@AliasFor(annotation = Queryable.class, value = "queryAttributes")
String rangeOptions() default "";
}

20
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/QueryCharacteristics.java

@ -16,21 +16,25 @@ @@ -16,21 +16,25 @@
package org.springframework.data.mongodb.core.schema;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.bson.BsonNull;
import org.bson.Document;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
* @since 4.5
*/
public class QueryCharacteristics {
public class QueryCharacteristics implements Streamable<QueryCharacteristic> {
private static final QueryCharacteristics NONE = new QueryCharacteristics(List.of());
private static final QueryCharacteristics NONE = new QueryCharacteristics(Collections.emptyList());
private final List<QueryCharacteristic> characteristics;
@ -46,14 +50,19 @@ public class QueryCharacteristics { @@ -46,14 +50,19 @@ public class QueryCharacteristics {
return new QueryCharacteristics(List.copyOf(characteristics));
}
QueryCharacteristics(QueryCharacteristic... characteristics) {
this.characteristics = Arrays.asList(characteristics);
public static QueryCharacteristics of(QueryCharacteristic... characteristics) {
return new QueryCharacteristics(Arrays.asList(characteristics));
}
public List<QueryCharacteristic> getCharacteristics() {
return characteristics;
}
@Override
public Iterator<QueryCharacteristic> iterator() {
return this.characteristics.iterator();
}
public static <T> RangeQuery<T> range() {
return new RangeQuery<>();
}
@ -96,7 +105,8 @@ public class QueryCharacteristics { @@ -96,7 +105,8 @@ public class QueryCharacteristics {
this(Range.unbounded(), null, null, null);
}
public RangeQuery(Range<T> valueRange, Integer trimFactor, Long sparsity, Long contention) {
public RangeQuery(@Nullable Range<T> valueRange, @Nullable Integer trimFactor, @Nullable Long sparsity,
@Nullable Long contention) {
this.valueRange = valueRange;
this.trimFactor = trimFactor;
this.sparsity = sparsity;

10
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/RangeEncryptionTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2024-2025 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.
@ -15,9 +15,8 @@ @@ -15,9 +15,8 @@
*/
package org.springframework.data.mongodb.core.encryption;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import java.security.SecureRandom;
import java.util.LinkedHashMap;
@ -39,6 +38,7 @@ import org.junit.jupiter.api.BeforeAll; @@ -39,6 +38,7 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@ -369,7 +369,7 @@ class RangeEncryptionTests { @@ -369,7 +369,7 @@ class RangeEncryptionTests {
path = StringUtils.arrayToDelimitedString(ctx.getProperty().getMongoField().getName().parts(), ".");
}
if (ctx.getOperatorContext() != null) {
path = ctx.getOperatorContext().getPath();
path = ctx.getOperatorContext().path();
}
return EncryptionKey.keyId(keyHolder.getEncryptionKey(path));
}));

37
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/schema/MongoJsonSchemaUnitTests.java

@ -15,13 +15,10 @@ @@ -15,13 +15,10 @@
*/
package org.springframework.data.mongodb.core.schema;
import static org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty.rangeEncrypted;
import static org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty.*;
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.*;
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.encrypted;
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.number;
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.queryable;
import static org.springframework.data.mongodb.core.schema.JsonSchemaProperty.string;
import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
import static org.springframework.data.mongodb.test.util.Assertions.assertThatIllegalArgumentException;
import static org.springframework.data.mongodb.test.util.Assertions.*;
import java.util.Arrays;
import java.util.Collections;
@ -119,13 +116,27 @@ class MongoJsonSchemaUnitTests { @@ -119,13 +116,27 @@ class MongoJsonSchemaUnitTests {
List.of(QueryCharacteristics.range().contention(0).trimFactor(1).sparsity(1).min(0).max(200))))
.build();
assertThat(schema.toDocument()).isEqualTo(new Document("$jsonSchema",
new Document("type", "object").append("properties",
new Document("ssn",
new Document("encrypt",
new Document("bsonType", "long").append("algorithm", "Range").append("queries",
List.of(new Document("contention", 0L).append("trimFactor", 1).append("sparsity", 1L)
.append("queryType", "range").append("min", 0).append("max", 200))))))));
assertThat(schema.toDocument().get("$jsonSchema", Document.class)).isEqualTo("""
{
"type": "object",
"properties": {
"ssn": {
"encrypt": {
"bsonType": "long",
"algorithm": "Range",
"queries": [{
"queryType": "range",
"contention": {$numberLong: "0"},
"trimFactor": 1,
"sparsity": {$numberLong: "1"},
"min": 0,
"max": 200
}]
}
}
}
}
""");
}
@Test // DATAMONGO-1835

Loading…
Cancel
Save