@ -24,13 +24,13 @@ import java.util.Map;
import java.util.Map.Entry ;
import java.util.Map.Entry ;
import java.util.Optional ;
import java.util.Optional ;
import java.util.function.Function ;
import java.util.function.Function ;
import java.util.stream.StreamSupport ;
import org.bson.BsonBinary ;
import org.bson.BsonBinary ;
import org.bson.BsonBinarySubType ;
import org.bson.BsonBinarySubType ;
import org.bson.BsonNull ;
import org.bson.BsonNull ;
import org.bson.Document ;
import org.bson.Document ;
import org.jspecify.annotations.Nullable ;
import org.jspecify.annotations.Nullable ;
import org.springframework.data.mongodb.core.mapping.Field ;
import org.springframework.data.mongodb.core.mapping.Field ;
import org.springframework.data.mongodb.core.query.Collation ;
import org.springframework.data.mongodb.core.query.Collation ;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty ;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty ;
@ -391,6 +391,7 @@ public class CollectionOptions {
*
*
* @param encryptedFieldsOptions must not be { @literal null } .
* @param encryptedFieldsOptions must not be { @literal null } .
* @return new instance of { @link CollectionOptions } .
* @return new instance of { @link CollectionOptions } .
* @since 4 . 5
* /
* /
@Contract ( "_ -> new" )
@Contract ( "_ -> new" )
@CheckReturnValue
@CheckReturnValue
@ -711,7 +712,7 @@ public class CollectionOptions {
/ * *
/ * *
* @return new instance of { @link EncryptedFieldsOptions } .
* @return new instance of { @link EncryptedFieldsOptions } .
* /
* /
public static EncryptedFieldsOptions fromProperties ( List < JsonSchemaProperty > properties ) {
public static EncryptedFieldsOptions fromProperties ( List < ? extends JsonSchemaProperty > properties ) {
return new EncryptedFieldsOptions ( null , List . copyOf ( properties ) ) ;
return new EncryptedFieldsOptions ( null , List . copyOf ( properties ) ) ;
}
}
@ -743,6 +744,7 @@ public class CollectionOptions {
*
*
* @param property must not be { @literal null } .
* @param property must not be { @literal null } .
* @return new instance of { @link EncryptedFieldsOptions } .
* @return new instance of { @link EncryptedFieldsOptions } .
* @since 4 . 5 . 1
* /
* /
@Contract ( "_ -> new" )
@Contract ( "_ -> new" )
@CheckReturnValue
@CheckReturnValue
@ -751,24 +753,26 @@ public class CollectionOptions {
}
}
/ * *
/ * *
* Add a { @link JsonSchemaProperty property } that should not be encrypted but not queryable .
* Add a { @link JsonSchemaProperty property } that should be encrypted but not queryable .
*
*
* @param property must not be { @literal null } .
* @param property must not be { @literal null } .
* @param key can be { @literal null } .
* @param keyId the key identifier to be used , can be { @literal null } .
* @return new instance of { @link EncryptedFieldsOptions } .
* @return new instance of { @link EncryptedFieldsOptions } .
* @since 4 . 5 . 1
* /
* /
@Contract ( "_, _ -> new" )
@Contract ( "_, _ -> new" )
@CheckReturnValue
@CheckReturnValue
public EncryptedFieldsOptions encrypted ( JsonSchemaProperty property , @Nullable Object key ) {
public EncryptedFieldsOptions encrypted ( JsonSchemaProperty property , @Nullable Object keyId ) {
List < JsonSchemaProperty > targetPropertyList = new ArrayList < > ( properties . size ( ) + 1 ) ;
List < JsonSchemaProperty > targetPropertyList = new ArrayList < > ( properties . size ( ) + 1 ) ;
targetPropertyList . addAll ( properties ) ;
targetPropertyList . addAll ( properties ) ;
if ( property instanceof IdentifiableJsonSchemaProperty . EncryptedJsonSchemaProperty ) {
if ( property instanceof IdentifiableJsonSchemaProperty . EncryptedJsonSchemaProperty ) {
targetPropertyList . add ( property ) ;
targetPropertyList . add ( property ) ;
} else {
} else {
EncryptedJsonSchemaProperty encryptedJsonSchemaProperty = new EncryptedJsonSchemaProperty ( property ) ;
EncryptedJsonSchemaProperty encryptedJsonSchemaProperty = new EncryptedJsonSchemaProperty ( property ) ;
if ( key ! = null ) {
if ( keyId ! = null ) {
targetPropertyList . add ( encryptedJsonSchemaProperty . keyId ( key ) ) ;
targetPropertyList . add ( encryptedJsonSchemaProperty . keyId ( keyId ) ) ;
}
}
}
}
@ -799,6 +803,12 @@ public class CollectionOptions {
List < Document > converted = new ArrayList < > ( properties . size ( ) ) ;
List < Document > converted = new ArrayList < > ( properties . size ( ) ) ;
for ( JsonSchemaProperty property : properties ) {
for ( JsonSchemaProperty property : properties ) {
converted . add ( getEncryptedField ( property ) ) ;
}
return converted ;
}
private Document getEncryptedField ( JsonSchemaProperty property ) {
Document field = new Document ( "path" , property . getIdentifier ( ) ) ;
Document field = new Document ( "path" , property . getIdentifier ( ) ) ;
@ -806,18 +816,18 @@ public class CollectionOptions {
field . append ( "bsonType" , property . getTypes ( ) . iterator ( ) . next ( ) . toBsonType ( ) . value ( ) ) ;
field . append ( "bsonType" , property . getTypes ( ) . iterator ( ) . next ( ) . toBsonType ( ) . value ( ) ) ;
}
}
if ( property instanceof QueryableJsonSchemaProperty qproperty & & qproperty
if ( property instanceof QueryableJsonSchemaProperty qp
. getTargetProperty ( ) instanceof IdentifiableJsonSchemaProperty . EncryptedJsonSchemaProperty encrypted ) {
& & qp . getTargetProperty ( ) instanceof IdentifiableJsonSchemaProperty . EncryptedJsonSchemaProperty encrypted
if ( encrypted . getKeyId ( ) ! = null ) {
& & encrypted . getKeyId ( ) ! = null ) {
if ( encrypted . getKeyId ( ) instanceof String stringKey ) {
if ( encrypted . getKeyId ( ) instanceof String stringKey ) {
field . append ( "keyId" ,
field . append ( "keyId" ,
new BsonBinary ( BsonBinarySubType . UUID_STANDARD , stringKey . getBytes ( StandardCharsets . UTF_8 ) ) ) ;
new BsonBinary ( BsonBinarySubType . UUID_STANDARD , stringKey . getBytes ( StandardCharsets . UTF_8 ) ) ) ;
} else {
} else {
field . append ( "keyId" , encrypted . getKeyId ( ) ) ;
field . append ( "keyId" , encrypted . getKeyId ( ) ) ;
}
}
}
} else if ( property instanceof IdentifiableJsonSchemaProperty . EncryptedJsonSchemaProperty encrypted
} else if ( property instanceof IdentifiableJsonSchemaProperty . EncryptedJsonSchemaProperty encrypted ) {
& & encrypted . getKeyId ( ) ! = null ) {
if ( encrypted . getKeyId ( ) ! = null ) {
if ( encrypted . getKeyId ( ) instanceof String stringKey ) {
if ( encrypted . getKeyId ( ) instanceof String stringKey ) {
field . append ( "keyId" ,
field . append ( "keyId" ,
new BsonBinary ( BsonBinarySubType . UUID_STANDARD , stringKey . getBytes ( StandardCharsets . UTF_8 ) ) ) ;
new BsonBinary ( BsonBinarySubType . UUID_STANDARD , stringKey . getBytes ( StandardCharsets . UTF_8 ) ) ) ;
@ -825,19 +835,16 @@ public class CollectionOptions {
field . append ( "keyId" , encrypted . getKeyId ( ) ) ;
field . append ( "keyId" , encrypted . getKeyId ( ) ) ;
}
}
}
}
}
if ( property instanceof QueryableJsonSchemaProperty qproperty ) {
if ( property instanceof QueryableJsonSchemaProperty qp ) {
field . append ( "queries" , StreamSupport . stream ( qproperty . getCharacteristics ( ) . spliterator ( ) , false )
field . append ( "queries" , qp . getCharacteristics ( ) . map ( QueryCharacteristic : : toDocument ) . toList ( ) ) ;
. map ( QueryCharacteristic : : toDocument ) . toList ( ) ) ;
}
}
if ( ! field . containsKey ( "keyId" ) ) {
if ( ! field . containsKey ( "keyId" ) ) {
field . append ( "keyId" , BsonNull . VALUE ) ;
field . append ( "keyId" , BsonNull . VALUE ) ;
}
}
converted . add ( field ) ;
return field ;
}
return converted ;
}
}
private List < Document > fromSchema ( ) {
private List < Document > fromSchema ( ) {
@ -851,20 +858,26 @@ public class CollectionOptions {
collectPaths ( root , null , paths ) ;
collectPaths ( root , null , paths ) ;
List < Document > fields = new ArrayList < > ( ) ;
List < Document > fields = new ArrayList < > ( ) ;
if ( ! paths . isEmpty ( ) ) {
if ( paths . isEmpty ( ) ) {
return fields ;
}
for ( Entry < String , Document > entry : paths . entrySet ( ) ) {
for ( Entry < String , Document > entry : paths . entrySet ( ) ) {
Document field = new Document ( "path" , entry . getKey ( ) ) ;
Document field = new Document ( "path" , entry . getKey ( ) ) ;
field . append ( "keyId" , entry . getValue ( ) . getOrDefault ( "keyId" , BsonNull . VALUE ) ) ;
field . append ( "keyId" , entry . getValue ( ) . getOrDefault ( "keyId" , BsonNull . VALUE ) ) ;
if ( entry . getValue ( ) . containsKey ( "bsonType" ) ) {
if ( entry . getValue ( ) . containsKey ( "bsonType" ) ) {
field . append ( "bsonType" , entry . getValue ( ) . get ( "bsonType" ) ) ;
field . append ( "bsonType" , entry . getValue ( ) . get ( "bsonType" ) ) ;
}
}
if ( entry . getValue ( ) . containsKey ( "queries" ) ) {
if ( entry . getValue ( ) . containsKey ( "queries" ) ) {
field . put ( "queries" , entry . getValue ( ) . get ( "queries" ) ) ;
field . put ( "queries" , entry . getValue ( ) . get ( "queries" ) ) ;
}
}
fields . add ( field ) ;
fields . add ( field ) ;
}
}
}
return fields ;
return fields ;
}
}
@ -873,13 +886,15 @@ public class CollectionOptions {
private static void collectPaths ( Document document , @Nullable 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" ) ) {
if ( document . containsKey ( "type" ) & & document . get ( "type" ) . equals ( "object" ) ) {
Object o = document . get ( "properties" ) ;
Object o = document . get ( "properties" ) ;
if ( o = = null ) {
if ( ! ( o instanceof Document properties ) ) {
return ;
return ;
}
}
if ( o instanceof Document properties ) {
for ( Entry < String , Object > entry : properties . entrySet ( ) ) {
for ( Entry < String , Object > entry : properties . entrySet ( ) ) {
if ( entry . getValue ( ) instanceof Document nested ) {
if ( entry . getValue ( ) instanceof Document nested ) {
String path = currentPath = = null ? entry . getKey ( ) : ( currentPath + "." + entry . getKey ( ) ) ;
String path = currentPath = = null ? entry . getKey ( ) : ( currentPath + "." + entry . getKey ( ) ) ;
@ -899,7 +914,6 @@ public class CollectionOptions {
}
}
}
}
}
}
}
/ * *
/ * *
* Encapsulation of options applied to define collections change stream behaviour .
* Encapsulation of options applied to define collections change stream behaviour .