Spring Data MongoDB可以自动为用@Document注解的实体类型创建索引。从3.0版本起,必须显式启用索引创建,以防止对集合生命周期和性能产生不希望的影响。在应用程序启动时以及在应用程序运行时首次访问实体类型时,会自动为初始实体集创建索引。
我们通常建议为基于应用程序的索引控制创建显式索引,因为Spring Data无法自动为在应用程序运行时重新创建的集合创建索引。
如果您想使用@GeoSpatialIndexed、@TextIndexed,@CompoundIndex和@WildcardIndexed等@Indexed注解,IndexResolver为编程式索引定义创建提供了一个抽象。可以将索引定义与IndexOperations一起使用来创建索引。创建索引的一个好时间点是在应用程序启动时,特别是在刷新应用程序上下文之后,通过观察ContextRefreshedEvent来触发。此事件保证上下文已完全初始化。请注意,此时其他组件,尤其是bean工厂,可能可以访问MongoDB数据库。
IndexResolver将跳过类似映射的属性,除非使用@WildcardIndexed进行注解,因为map key必须是索引定义的一部分。由于映射的目的是使用动态key和value,因此无法从静态映射元数据中解析键。
例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);
}
}
例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);
});
}
}
或者,如果你想在任何组件能够从应用程序访问数据库之前确保索引和集合的存在,请为MongoTemplate声明一个@Bean方法,并在返回MongoTemplate对象之前包含上面的代码。
若要打开自动索引创建,请覆盖配置中的autoIndexCreation()。
@Configuration
public class Config extends AbstractMongoClientConfiguration {
@Override
public boolean autoIndexCreation() {
return true;
}
// ...
}
自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;
}
使用@CompoundIndexes作为容器,@CompoundIndex是可重复的。
@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;
// ...
}
二、哈希索引
哈希索引允许在分片集群中进行基于哈希的分片。使用哈希字段值来分割集合会导致更随机的分布。有关详细信息,请参阅MongoDB文档。
下面是一个为_id创建哈希索引的示例:
例4:哈希索引用法示例
@Document
public class DomainType {
@HashIndexed @Id String id;
// ...
}
哈希索引可以与其他索引一起创建,如下所示,在这种情况下,会创建两个索引:
例5:哈希索引与简单索引一起使用示例
@Document
public class DomainType {
@Indexed
@HashIndexed
String value;
// ...
}
如果上面的示例过于冗长,则复合注解可以减少需要在属性上声明的注解数量:
例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 "";
}
1. 可能会为元注解的某些属性注册别名。
尽管通过注解创建索引在许多情况下都很方便,但可以通过IndexOperations手动设置索引来接管更多的控制权。
mongoOperations.indexOpsFor(Jedi.class)
.ensureIndex(HashedIndex.hashed("useTheForce"));
三、通配符索引
通配符索引是一种索引,可用于包括所有字段或基于给定(通配符)模式(pattern)的特定字段。有关详细信息,请参阅MongoDB文档。
可以通过IndexOperations使用WildcardIndex以编程方式设置索引。
例7:编程式通配符索引设置
mongoOperations
.indexOps(User.class)
.ensureIndex(new WildcardIndex("userMetadata"));
db.user.createIndex({ "userMetadata.$**" : 1 }, {})
@WildcardIndex注解允许声明性索引设置,该设置可以与document类型或属性一起使用。
如果放置在根级域实体的类型(用@Document注解的类型)上,索引解析程序将为其创建通配符索引。
例8:域类型的通配符索引
@Document
@WildcardIndexed
public class Product {
// …
}
db.product.createIndex({ "$**" : 1 },{})
wildcardProjection可用于指定索引中的in-/exclude键。
例9:带通配符投影的通配符索引
@Document
@WildcardIndexed(wildcardProjection = "{ 'userMetadata.age' : 0 }")
public class User {
private @Id String id;
private UserMetadata userMetadata;
}
db.user.createIndex(
{ "$**" : 1 },
{ "wildcardProjection" :
{ "userMetadata.age" : 0 }
}
)
通配符索引也可以通过将注解直接添加到字段来表示。请注意,在嵌套路径(如属性)上不允许使用通配符投影。在创建索引的过程中,会省略对使用@WildcardIndexed注解的类型的投影。
例10:属性的通配符索引
@Document
public class User {
private @Id String id;
@WildcardIndexed
private UserMetadata userMetadata;
}
db.user.createIndex({ "userMetadata.$**" : 1 }, {})
四、文本索引
MongoDB v.2.4默认禁用文本索引功能。
创建文本索引可以将多个字段累积到可搜索的全文索引中。每个集合只能有一个文本索引,因此所有用@TextIndexed标记的字段都会合并到此索引中。可以对属性进行加权,以影响排名结果的文档分数。文本索引的默认语言是英语。要更改默认语言,请将语言属性设置为所需的任何语言(例如,@Document(language=“spanish”))。使用名为language的属性或@Language,可以在每个document的基础上定义语言覆盖。以下示例显示如何创建文本索引并将语言设置为西班牙语:
例11:文本索引用法示例
@Document(language = "spanish")
class SomeEntity {
@TextIndexed String foo;
@Language String lang;
Nested nested;
}
class Nested {
@TextIndexed(weight=5) String bar;
String roo;
}