索引创建
Spring Data MongoDB 可以自动为使用 @Document
注释的实体类型创建索引。自版本 3.0 起,必须明确启用索引创建,以防止对集合生命周期和性能产生不良影响。在应用程序启动时以及在应用程序运行时首次访问实体类型时,将自动为初始实体集创建索引。
我们通常建议显式创建索引以对索引进行基于应用程序的控制,因为 Spring Data 无法自动为应用程序运行时重新创建的集合创建索引。
IndexResolver
提供了一个程序化索引定义创建的抽象,如果你想使用 @Indexed
注解,例如 @GeoSpatialIndexed
、@TextIndexed
、@CompoundIndex
和 @WildcardIndexed
。你可以将索引定义与 IndexOperations
一起使用来创建索引。索引创建的一个好时机是在应用程序启动时,特别是在应用程序上下文刷新后,由观察 ContextRefreshedEvent
触发。此事件保证上下文已完全初始化。请注意,此时其他组件,特别是 bean 工厂可能可以访问 MongoDB 数据库。
|
class MyListener {
@EventListener(ContextRefreshedEvent.class)
public void initIndicesAfterStartup() {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = mongoTemplate
.getConverter().getMappingContext();
IndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext);
IndexOperations indexOps = mongoTemplate.indexOps(DomainType.class);
resolver.resolveIndexFor(DomainType.class).forEach(indexOps::ensureIndex);
}
}
class MyListener{
@EventListener(ContextRefreshedEvent.class)
public void initIndicesAfterStartup() {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = mongoTemplate
.getConverter().getMappingContext();
// consider only entities that are annotated with @Document
mappingContext.getPersistentEntities()
.stream()
.filter(it -> it.isAnnotationPresent(Document.class))
.forEach(it -> {
IndexOperations indexOps = mongoTemplate.indexOps(it.getType());
resolver.resolveIndexFor(it.getType()).forEach(indexOps::ensureIndex);
});
}
}
或者,如果你希望在任何组件能够从应用程序访问你的数据库之前确保索引和集合的存在,请为 MongoTemplate
声明一个 @Bean
方法,并在返回 MongoTemplate
对象之前包含以上代码。
要将自动索引创建打开,请在你的配置中覆盖
|
从 3.0 版开始,自动索引创建默认关闭。 |
复合索引
复合索引也受支持。它们在类级别定义,而不是在单个属性上定义。
复合索引对于提高涉及多个字段条件的查询性能非常重要 |
以下是一个创建按升序排列的 lastName
和按降序排列的 age
的复合索引的示例
package com.mycompany.domain;
@Document
@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")
public class Person {
@Id
private ObjectId id;
private Integer age;
private String firstName;
private String lastName;
}
|
哈希索引
哈希索引允许在分片集群中基于哈希进行分片。使用哈希字段值对集合进行分片会产生更随机的分布。有关详细信息,请参阅MongoDB 文档。
以下是一个为_id
创建哈希索引的示例
@Document
public class DomainType {
@HashIndexed @Id String id;
// ...
}
哈希索引可以在其他索引定义旁边创建,如下所示,在这种情况下,两个索引都会被创建
@Document
public class DomainType {
@Indexed
@HashIndexed
String value;
// ...
}
如果上面的示例过于冗长,复合注释可以减少需要在属性上声明的注释数量
@Document
public class DomainType {
@IndexAndHash(name = "idx...") (1)
String value;
// ...
}
@Indexed
@HashIndexed
@Retention(RetentionPolicy.RUNTIME)
public @interface IndexAndHash {
@AliasFor(annotation = Indexed.class, attribute = "name") (1)
String name() default "";
}
1 | 可能为元注释的某些属性注册一个别名。 |
虽然通过注释创建索引在许多情况下很方便,但可以通过
|
通配符索引
WildcardIndex
是一个索引,可用于基于给定的(通配符)模式包括所有字段或特定字段。有关详细信息,请参阅MongoDB 文档。
可以使用IndexOperations
通过WildcardIndex
以编程方式设置索引。
mongoOperations
.indexOps(User.class)
.ensureIndex(new WildcardIndex("userMetadata"));
db.user.createIndex({ "userMetadata.$**" : 1 }, {})
@WildcardIndex
注释允许声明性索引设置,可与文档类型或属性一起使用。
如果放置在根级别域实体类型(用@Document
注释的类型)上,索引解析器将为其创建一个通配符索引。
@Document
@WildcardIndexed
public class Product {
// …
}
db.product.createIndex({ "$**" : 1 },{})
wildcardProjection
可用于指定要包含/排除在索引中的键。
wildcardProjection
的通配符索引@Document
@WildcardIndexed(wildcardProjection = "{ 'userMetadata.age' : 0 }")
public class User {
private @Id String id;
private UserMetadata userMetadata;
}
db.user.createIndex(
{ "$**" : 1 },
{ "wildcardProjection" :
{ "userMetadata.age" : 0 }
}
)
还可以通过将注释直接添加到字段来表示通配符索引。请注意,wildcardProjection
不允许在嵌套路径(如属性)上使用。在索引创建期间,将省略使用@WildcardIndexed
注释的类型上的投影。
@Document
public class User {
private @Id String id;
@WildcardIndexed
private UserMetadata userMetadata;
}
db.user.createIndex({ "userMetadata.$**" : 1 }, {})
文本索引
对于 MongoDB v.2.4,文本索引功能默认禁用。 |
创建文本索引允许将多个字段累积到可搜索的全文本索引中。每个集合只能有一个文本索引,因此所有用@TextIndexed
标记的字段都将合并到此索引中。可以对属性进行加权,以影响文档评分以对结果进行排名。文本索引的默认语言为英语。要更改默认语言,请将language
属性设置为所需的任何语言(例如,@Document(language="spanish")
)。使用名为language
或@Language
的属性允许您为每个文档定义语言覆盖。以下示例显示了如何创建文本索引并将其语言设置为西班牙语
@Document(language = "spanish")
class SomeEntity {
@TextIndexed String foo;
@Language String lang;
Nested nested;
}
class Nested {
@TextIndexed(weight=5) String bar;
String roo;
}