跳到主要内容

索引创建

DeepSeek V3 中英对照 Index Creation

Spring Data MongoDB 可以为带有 @Document 注解的实体类型自动创建索引。从 3.0 版本开始,必须显式启用索引创建,以防止对集合生命周期和性能产生不良影响。索引会在应用程序启动时自动为初始实体集创建,并在应用程序运行期间首次访问某个实体类型时自动创建。

我们通常建议显式创建索引,以便基于应用程序控制索引,因为 Spring Data 无法自动为应用程序运行时重新创建的集合创建索引。

IndexResolver 提供了一个抽象层,用于以编程方式创建索引定义,如果您想要使用诸如 @GeoSpatialIndexed@TextIndexed@CompoundIndex@WildcardIndexed 这样的 @Indexed 注解。您可以将索引定义与 IndexOperations 一起使用来创建索引。创建索引的一个合适时机是在应用程序启动时,特别是在应用程序上下文刷新之后,通过监听 ContextRefreshedEvent 来触发。此事件保证了上下文已经完全初始化。需要注意的是,此时其他组件(尤其是 Bean 工厂)可能已经可以访问 MongoDB 数据库。

注意

除非使用 @WildcardIndexed 进行注解,否则 Map 类型的属性会被 IndexResolver 跳过,因为 map 键 必须是索引定义的一部分。由于 map 的目的是使用动态键和值,因此无法从静态映射元数据中解析键。

示例 1. 为单一域类型编程创建索引

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);
}
}
java

示例 2. 为所有初始实体的程序化索引创建

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);
});
}
}
java

或者,如果你想确保在应用程序中的任何组件访问数据库之前,索引和集合已经存在,可以为 MongoTemplate 声明一个 @Bean 方法,并在返回 MongoTemplate 对象之前包含上述代码。

备注

要开启自动索引创建功能,请在您的配置中重写 autoIndexCreation() 方法。

@Configuration
public class Config extends AbstractMongoClientConfiguration {

@Override
public boolean autoIndexCreation() {
return true;
}

// ...
}
java
important

从 3.0 版本开始,默认情况下自动索引创建功能已关闭。

复合索引

复合索引同样被支持。它们是在类级别定义的,而不是在单个属性上。

备注

复合索引对于提高涉及多个字段条件的查询性能非常重要

以下是一个创建复合索引的示例,其中 lastName 按升序排列,age 按降序排列:

示例 3. 复合索引使用示例

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;

}
java
提示

@CompoundIndex 可以通过 @CompoundIndexes 作为其容器来重复使用。

@Document
@CompoundIndex(name = "cmp-idx-one", def = "{'firstname': 1, 'lastname': -1}")
@CompoundIndex(name = "cmp-idx-two", def = "{'address.city': -1, 'address.street': 1}")
public class Person {

String firstname;
String lastname;

Address address;

// ...
}
java

哈希索引

哈希索引允许在分片集群中基于哈希进行分片。使用哈希字段值对集合进行分片可以实现更随机的分布。详情请参阅 MongoDB 文档

以下是一个为 _id 创建哈希索引的示例:

示例 4. 哈希索引的使用示例

@Document
public class DomainType {

@HashIndexed @Id String id;

// ...
}
java

可以在其他索引定义旁边创建哈希索引,如下所示,在这种情况下,两个索引都会被创建:

示例 5. 结合简单索引使用哈希索引的示例

@Document
public class DomainType {

@Indexed
@HashIndexed
String value;

// ...
}
java

如果上面的示例过于冗长,复合注解可以减少需要在属性上声明的注解数量:

示例 6. 组合哈希索引使用示例

@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 "";
}
java
  • 可能为元注解的某些属性注册一个别名。

备注

尽管通过注解创建索引在许多情况下非常方便,但请考虑通过 IndexOperations 手动设置索引以获得更多控制。

mongoOperations.indexOpsFor(Jedi.class)
.ensureIndex(HashedIndex.hashed("useTheForce"));
java

通配符索引

WildcardIndex 是一种索引,可以根据给定的(通配符)模式来包含所有字段或特定字段。有关详细信息,请参阅 MongoDB 文档

可以通过 IndexOperations 使用 WildcardIndex 以编程方式设置索引。

示例 7. 编程式 WildcardIndex 设置

mongoOperations
.indexOps(User.class)
.ensureIndex(new WildcardIndex("userMetadata"));
java
db.user.createIndex({ "userMetadata.$**" : 1 }, {})
javascript

@WildcardIndex 注解允许声明式地设置索引,既可以用于文档类型,也可以用于属性。

如果放在一个根级领域实体类型上(使用 @Document 注解的实体),索引解析器将为其创建一个通配符索引。

示例 8. 域名类型上的通配符索引

@Document
@WildcardIndexed
public class Product {
// …
}
java
db.product.createIndex({ "$**" : 1 },{})
javascript

wildcardProjection 可用于指定在索引中包含或排除的键。

示例 9. 使用 wildcardProjection 的通配符索引

@Document
@WildcardIndexed(wildcardProjection = "{ 'userMetadata.age' : 0 }")
public class User {
private @Id String id;
private UserMetadata userMetadata;
}
java
db.user.createIndex(
{ "$**" : 1 },
{ "wildcardProjection" :
{ "userMetadata.age" : 0 }
}
)
javascript

通配符索引也可以通过直接在字段上添加注解来表示。请注意,wildcardProjection 不允许在嵌套路径(例如属性)上使用。在创建索引时,使用 @WildcardIndexed 注解的类型的投影会被省略。

示例 10. 属性的通配符索引

@Document
public class User {
private @Id String id;

@WildcardIndexed
private UserMetadata userMetadata;
}
java
db.user.createIndex({ "userMetadata.$**" : 1 }, {})
javascript

文本索引

备注

MongoDB v.2.4 默认情况下禁用了文本索引功能。

创建文本索引允许将多个字段累积到可搜索的全文索引中。每个集合只能有一个文本索引,因此所有标记为 @TextIndexed 的字段都会合并到这个索引中。可以对属性进行加权,以影响文档得分,从而影响结果的排名。文本索引的默认语言是英语。要更改默认语言,请将 language 属性设置为您想要的任何语言(例如,@Document(language="spanish"))。使用名为 language@Language 的属性,您可以在每个文档的基础上定义语言覆盖。以下示例展示了如何创建文本索引并将语言设置为西班牙语:

示例 11. 文本索引使用示例

@Document(language = "spanish")
class SomeEntity {

@TextIndexed String foo;

@Language String lang;

Nested nested;
}

class Nested {

@TextIndexed(weight=5) String bar;
String roo;
}
java