Polishing.
Reformat code. Tweak javadoc. Reject wildcard projection usage on properties with a MappingException. Omit wildcard projections when declared on document types that are used as subdocument. See #3225 Original pull request: #3671.
This commit is contained in:
@@ -115,7 +115,7 @@ abstract class IndexConverters {
|
||||
ops = ops.collation(fromDocument(indexOptions.get("collation", Document.class)));
|
||||
}
|
||||
|
||||
if(indexOptions.containsKey("wildcardProjection")) {
|
||||
if (indexOptions.containsKey("wildcardProjection")) {
|
||||
ops.wildcardProjection(indexOptions.get("wildcardProjection", Document.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,17 @@ import org.springframework.util.ObjectUtils;
|
||||
public final class IndexField {
|
||||
|
||||
enum Type {
|
||||
GEO, TEXT, DEFAULT, HASH, WILDCARD;
|
||||
GEO, TEXT, DEFAULT,
|
||||
|
||||
/**
|
||||
* @since 2.2
|
||||
*/
|
||||
HASH,
|
||||
|
||||
/**
|
||||
* @since 3.3
|
||||
*/
|
||||
WILDCARD;
|
||||
}
|
||||
|
||||
private final String key;
|
||||
@@ -78,7 +88,8 @@ public final class IndexField {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@literal wildcard} {@link IndexField} for the given key.
|
||||
* Creates a {@literal wildcard} {@link IndexField} for the given key. The {@code key} must follow the
|
||||
* {@code fieldName.$**} notation.
|
||||
*
|
||||
* @param key must not be {@literal null} or empty.
|
||||
* @return new instance of {@link IndexField}.
|
||||
|
||||
@@ -100,7 +100,7 @@ public class IndexInfo {
|
||||
|
||||
if (ObjectUtils.nullSafeEquals("hashed", value)) {
|
||||
indexFields.add(IndexField.hashed(key));
|
||||
} else if (key.contains("$**")) {
|
||||
} else if (key.endsWith("$**")) {
|
||||
indexFields.add(IndexField.wildcard(key));
|
||||
} else {
|
||||
|
||||
|
||||
@@ -119,6 +119,8 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
Assert.notNull(document, () -> String
|
||||
.format("Entity %s is not a collection root. Make sure to annotate it with @Document!", root.getName()));
|
||||
|
||||
verifyWildcardIndexedProjection(root);
|
||||
|
||||
List<IndexDefinitionHolder> indexInformation = new ArrayList<>();
|
||||
String collection = root.getCollection();
|
||||
indexInformation.addAll(potentiallyCreateCompoundIndexDefinitions("", collection, root));
|
||||
@@ -133,6 +135,24 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
return indexInformation;
|
||||
}
|
||||
|
||||
private void verifyWildcardIndexedProjection(MongoPersistentEntity<?> entity) {
|
||||
|
||||
entity.doWithAll(it -> {
|
||||
|
||||
if (it.isAnnotationPresent(WildcardIndexed.class)) {
|
||||
|
||||
WildcardIndexed indexed = it.getRequiredAnnotation(WildcardIndexed.class);
|
||||
|
||||
if (!ObjectUtils.isEmpty(indexed.wildcardProjection())) {
|
||||
|
||||
throw new MappingException(String.format(
|
||||
"WildcardIndexed.wildcardProjection cannot be used on nested paths. Offending property: %s.%s",
|
||||
entity.getName(), it.getName()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void potentiallyAddIndexForProperty(MongoPersistentEntity<?> root, MongoPersistentProperty persistentProperty,
|
||||
List<IndexDefinitionHolder> indexes, CycleGuard guard) {
|
||||
|
||||
@@ -257,7 +277,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
private List<IndexDefinitionHolder> potentiallyCreateWildcardIndexDefinitions(String dotPath, String collection,
|
||||
MongoPersistentEntity<?> entity) {
|
||||
|
||||
if (entity.findAnnotation(WildcardIndexed.class) == null) {
|
||||
if (!entity.isAnnotationPresent(WildcardIndexed.class)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -429,7 +449,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver {
|
||||
|
||||
WildcardIndex indexDefinition = new WildcardIndex(dotPath);
|
||||
|
||||
if (StringUtils.hasText(index.wildcardProjection())) {
|
||||
if (StringUtils.hasText(index.wildcardProjection()) && ObjectUtils.isEmpty(dotPath)) {
|
||||
indexDefinition.wildcardProjection(evaluateWildcardProjection(index.wildcardProjection(), entity));
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ import org.springframework.util.StringUtils;
|
||||
public class WildcardIndex extends Index {
|
||||
|
||||
private @Nullable String fieldName;
|
||||
private Map<String, Object> wildcardProjection = new LinkedHashMap<>();
|
||||
private final Map<String, Object> wildcardProjection = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link WildcardIndex} using {@code $**}.
|
||||
@@ -97,7 +97,7 @@ public class WildcardIndex extends Index {
|
||||
/**
|
||||
* Unique option is not supported.
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* @throws UnsupportedOperationException not supported for wildcard indexes.
|
||||
*/
|
||||
@Override
|
||||
public Index unique() {
|
||||
@@ -107,7 +107,7 @@ public class WildcardIndex extends Index {
|
||||
/**
|
||||
* ttl option is not supported.
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* @throws UnsupportedOperationException not supported for wildcard indexes.
|
||||
*/
|
||||
@Override
|
||||
public Index expire(long seconds) {
|
||||
@@ -117,7 +117,7 @@ public class WildcardIndex extends Index {
|
||||
/**
|
||||
* ttl option is not supported.
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* @throws UnsupportedOperationException not supported for wildcard indexes.
|
||||
*/
|
||||
@Override
|
||||
public Index expire(long value, TimeUnit timeUnit) {
|
||||
@@ -127,7 +127,7 @@ public class WildcardIndex extends Index {
|
||||
/**
|
||||
* ttl option is not supported.
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* @throws UnsupportedOperationException not supported for wildcard indexes.
|
||||
*/
|
||||
@Override
|
||||
public Index expire(Duration duration) {
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.lang.annotation.Target;
|
||||
*
|
||||
* db.product.createIndex({ "$**" : 1 } , {})
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* {@literal wildcardProjection} can be used to specify keys to in-/exclude in the index.
|
||||
*
|
||||
* <pre class="code">
|
||||
@@ -65,7 +65,7 @@ import java.lang.annotation.Target;
|
||||
* <pre class="code">
|
||||
* @Document
|
||||
* public class User {
|
||||
*
|
||||
*
|
||||
* private @Id String id;
|
||||
*
|
||||
* @WildcardIndexed
|
||||
@@ -89,9 +89,9 @@ public @interface WildcardIndexed {
|
||||
* expression}. <br />
|
||||
* <br />
|
||||
* The name will only be applied as is when defined on root level. For usage on nested or embedded structures the
|
||||
* provided name will be prefixed with the path leading to the entity. <br />
|
||||
*
|
||||
* @return
|
||||
* provided name will be prefixed with the path leading to the entity.
|
||||
*
|
||||
* @return empty by default.
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
@@ -115,8 +115,8 @@ public @interface WildcardIndexed {
|
||||
/**
|
||||
* Explicitly specify sub fields to be in-/excluded as a {@link org.bson.Document#parse(String) prasable} String.
|
||||
* <br />
|
||||
* <strong>NOTE: </strong>Can only be done on root level documents.
|
||||
*
|
||||
* <strong>NOTE: </strong>Can only be applied on root level documents.
|
||||
*
|
||||
* @return empty by default.
|
||||
*/
|
||||
String wildcardProjection() default "";
|
||||
|
||||
@@ -32,10 +32,12 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolver.IndexDefinitionHolder;
|
||||
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexResolverUnitTests.CompoundIndexResolutionTests;
|
||||
@@ -1333,6 +1335,20 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
assertThat(indices).hasSize(1);
|
||||
assertThat(indices.get(0)).satisfies(it -> {
|
||||
assertThat(it.getIndexKeys()).containsEntry("$**", 1);
|
||||
assertThat(it.getIndexOptions()).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test // GH-3225
|
||||
public void resolvesWildcardWithProjectionOnRoot() {
|
||||
|
||||
List<IndexDefinitionHolder> indices = prepareMappingContextAndResolveIndexForType(
|
||||
WithWildCardIndexHavingProjectionOnEntity.class);
|
||||
assertThat(indices).hasSize(1);
|
||||
assertThat(indices.get(0)).satisfies(it -> {
|
||||
assertThat(it.getIndexKeys()).containsEntry("$**", 1);
|
||||
assertThat(it.getIndexOptions()).containsEntry("wildcardProjection",
|
||||
org.bson.Document.parse("{'_id' : 1, 'value' : 0}"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1365,6 +1381,15 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
assertThat(indices).hasSize(1);
|
||||
assertThat(indices.get(0)).satisfies(it -> {
|
||||
assertThat(it.getIndexKeys()).containsEntry("value.$**", 1);
|
||||
assertThat(it.getIndexOptions()).hasSize(1).containsKey("name");
|
||||
});
|
||||
}
|
||||
|
||||
@Test // GH-3225
|
||||
public void rejectsWildcardProjectionOnNestedPaths() {
|
||||
|
||||
assertThatExceptionOfType(MappingException.class).isThrownBy(() -> {
|
||||
prepareMappingContextAndResolveIndexForType(WildcardIndexedProjectionOnNestedPath.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1647,10 +1672,16 @@ public class MongoPersistentEntityIndexResolverUnitTests {
|
||||
|
||||
}
|
||||
|
||||
@Document
|
||||
class WildcardIndexedProjectionOnNestedPath {
|
||||
|
||||
@WildcardIndexed(wildcardProjection = "{}") String foo;
|
||||
}
|
||||
|
||||
@Document
|
||||
class WithWildCardOnEntityOfNested {
|
||||
|
||||
WithWildCardIndexOnEntity value;
|
||||
WithWildCardIndexHavingProjectionOnEntity value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
* Extended support for <<mapping-usage.document-references, referencing>> entities.
|
||||
* Include/exclude `null` properties on write to `Document` through `@Field(write=…)`.
|
||||
* Support for <<mapping-usage-indexes.wildcard-index>>.
|
||||
|
||||
[[new-features.3.2]]
|
||||
== What's New in Spring Data MongoDB 3.2
|
||||
|
||||
@@ -782,9 +782,9 @@ db.user.createIndex({ "userMetadata.$**" : 1 }, {})
|
||||
----
|
||||
====
|
||||
|
||||
The `@WildcardIndex` annotation allows a declarative index setup an can be added on either a type or property.
|
||||
The `@WildcardIndex` annotation allows a declarative index setup that can used either with a document type or property.
|
||||
|
||||
If placed on a type that is a root level domain entity (one having an `@Document` annotation) will advise the index creator to create a
|
||||
If placed on a type that is a root level domain entity (one annotated with `@Document`) , the index resolver will create a
|
||||
wildcard index for it.
|
||||
|
||||
.Wildcard index on domain type
|
||||
@@ -794,7 +794,7 @@ wildcard index for it.
|
||||
@Document
|
||||
@WildcardIndexed
|
||||
public class Product {
|
||||
...
|
||||
// …
|
||||
}
|
||||
----
|
||||
[source,javascript]
|
||||
@@ -828,7 +828,8 @@ db.user.createIndex(
|
||||
====
|
||||
|
||||
Wildcard indexes can also be expressed by adding the annotation directly to the field.
|
||||
Please note that `wildcardProjection` is not allowed on nested paths.
|
||||
Please note that `wildcardProjection` is not allowed on nested paths such as properties.
|
||||
Projections on types annotated with `@WildcardIndexed` are omitted during index creation.
|
||||
|
||||
.Wildcard index on property
|
||||
====
|
||||
|
||||
Reference in New Issue
Block a user