Browse Source

Polishing.

Fix Javadoc, make QueryCharacteristics a Streamable for easier usage during mapping, reformat code for reduced nesting levels.

See #4988
Original pull request: #4992
pull/4995/head
Mark Paluch 7 months ago
parent
commit
96c2e710e6
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 144
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java
  2. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/schema/QueryCharacteristics.java

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

@ -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,45 +803,48 @@ 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;
}
Document field = new Document("path", property.getIdentifier()); private Document getEncryptedField(JsonSchemaProperty property) {
if (!property.getTypes().isEmpty()) { Document field = new Document("path", property.getIdentifier());
field.append("bsonType", property.getTypes().iterator().next().toBsonType().value());
}
if (property instanceof QueryableJsonSchemaProperty qproperty && qproperty if (!property.getTypes().isEmpty()) {
.getTargetProperty() instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted) { field.append("bsonType", property.getTypes().iterator().next().toBsonType().value());
if (encrypted.getKeyId() != null) { }
if (encrypted.getKeyId() instanceof String stringKey) {
field.append("keyId", if (property instanceof QueryableJsonSchemaProperty qp
new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8))); && qp.getTargetProperty() instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted
} else { && encrypted.getKeyId() != null) {
field.append("keyId", encrypted.getKeyId());
}
}
} else if (property instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted) {
if (encrypted.getKeyId() != null) {
if (encrypted.getKeyId() instanceof String stringKey) {
field.append("keyId",
new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8)));
} else {
field.append("keyId", encrypted.getKeyId());
}
}
}
if (property instanceof QueryableJsonSchemaProperty qproperty) { if (encrypted.getKeyId() instanceof String stringKey) {
field.append("queries", StreamSupport.stream(qproperty.getCharacteristics().spliterator(), false) field.append("keyId",
.map(QueryCharacteristic::toDocument).toList()); new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8)));
} else {
field.append("keyId", encrypted.getKeyId());
} }
if (!field.containsKey("keyId")) { } else if (property instanceof IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty encrypted
field.append("keyId", BsonNull.VALUE); && encrypted.getKeyId() != null) {
if (encrypted.getKeyId() instanceof String stringKey) {
field.append("keyId",
new BsonBinary(BsonBinarySubType.UUID_STANDARD, stringKey.getBytes(StandardCharsets.UTF_8)));
} else {
field.append("keyId", encrypted.getKeyId());
} }
}
converted.add(field); if (property instanceof QueryableJsonSchemaProperty qp) {
field.append("queries", qp.getCharacteristics().map(QueryCharacteristic::toDocument).toList());
} }
return converted;
if (!field.containsKey("keyId")) {
field.append("keyId", BsonNull.VALUE);
}
return field;
} }
private List<Document> fromSchema() { private List<Document> fromSchema() {
@ -851,19 +858,25 @@ 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());
field.append("keyId", entry.getValue().getOrDefault("keyId", BsonNull.VALUE)); Document field = new Document("path", entry.getKey());
if (entry.getValue().containsKey("bsonType")) {
field.append("bsonType", entry.getValue().get("bsonType")); field.append("keyId", entry.getValue().getOrDefault("keyId", BsonNull.VALUE));
}
if (entry.getValue().containsKey("queries")) { if (entry.getValue().containsKey("bsonType")) {
field.put("queries", entry.getValue().get("queries")); field.append("bsonType", entry.getValue().get("bsonType"));
} }
fields.add(field);
if (entry.getValue().containsKey("queries")) {
field.put("queries", entry.getValue().get("queries"));
} }
fields.add(field);
} }
return fields; return fields;
@ -873,28 +886,29 @@ 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());
if (nested.containsKey("encrypt")) { if (nested.containsKey("encrypt")) {
Document target = new Document(nested.get("encrypt", Document.class)); Document target = new Document(nested.get("encrypt", Document.class));
if (nested.containsKey("queries")) { if (nested.containsKey("queries")) {
List<?> queries = nested.get("queries", List.class); List<?> queries = nested.get("queries", List.class);
if (!queries.isEmpty() && queries.iterator().next() instanceof Document qd) { if (!queries.isEmpty() && queries.iterator().next() instanceof Document qd) {
target.putAll(qd); target.putAll(qd);
}
} }
paths.put(path, target);
} else {
collectPaths(nested, path, paths);
} }
paths.put(path, target);
} else {
collectPaths(nested, path, paths);
} }
} }
} }

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

@ -25,6 +25,7 @@ import org.bson.Document;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.data.domain.Range; import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound; import org.springframework.data.domain.Range.Bound;
import org.springframework.data.util.Streamable;
/** /**
* Encapsulation of individual {@link QueryCharacteristic query characteristics} used to define queries that can be * Encapsulation of individual {@link QueryCharacteristic query characteristics} used to define queries that can be
@ -33,7 +34,7 @@ import org.springframework.data.domain.Range.Bound;
* @author Christoph Strobl * @author Christoph Strobl
* @since 4.5 * @since 4.5
*/ */
public class QueryCharacteristics implements Iterable<QueryCharacteristic> { public class QueryCharacteristics implements Streamable<QueryCharacteristic> {
/** /**
* instance indicating none * instance indicating none

Loading…
Cancel
Save