索引创建
Spring Data MongoDB 可以为标注了 @Document 的实体类型自动创建索引。从 3.0 版起,必须显式启用索引创建,以避免对集合生命周期产生意外影响及性能影响。应用启动时会为初始实体集自动创建索引,应用运行期间首次访问某个实体类型时也会自动创建索引。
我们通常建议显式创建索引,以便在应用层面控制索引,因为对于应用运行期间被重建的集合,Spring Data 无法自动为其创建索引。
如果您希望利用 @Indexed 注解(如 @GeoSpatialIndexed、@TextIndexed、@CompoundIndex 和 @WildcardIndexed),IndexResolver 为编程式索引定义创建提供了一个抽象。您可以将索引定义与 IndexOperations 配合使用来创建索引。索引创建的一个合适时机是应用启动时,特别是在应用上下文刷新之后——由观察 ContextRefreshedEvent 触发。该事件保证上下文已完全初始化。请注意,此时其他组件(尤其是 Bean 工厂)可能已经可以访问 MongoDB 数据库。
Map 类属性会被 IndexResolver 跳过,除非标注了 @WildcardIndexed,因为 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);
}
}
示例 2. 为所有初始实体编程式创建索引
class MyListener {
@EventListener(ContextRefreshedEvent.class)
public void initIndicesAfterStartup() {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext = mongoTemplate.
getConverter().getMappingContext();
IndexResolver resolver = new MongoPersistentEntityIndexResolver(mappingContext);
// 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. 复合索引用法示例
@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;
}
@CompoundIndex 是可重复注解,可使用 @CompoundIndexes 作为其容器。
@Document
@CompoundIndexes({
@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 "";
}
可以为元注解的某些属性注册别名。
尽管通过注解创建索引在许多场景下很方便,但请考虑通过 IndexOperations 手动设置索引以获得更多控制权。
mongoOperations.indexOpsFor(Jedi.class)
.ensureIndex(HashedIndex.hashed("useTheForce"));
通配符索引
WildcardIndex 是一种索引,它可以基于给定的(通配符)模式包含所有字段或特定字段。有关详细信息,请参阅 MongoDB 文档。
可以通过 IndexOperations 使用 WildcardIndex 以编程方式设置索引。
示例 7. 编程式 WildcardIndex 设置
mongoOperations
.indexOps(User.class)
.ensureIndex(new WildcardIndex("userMetadata"));
db.user.createIndex({ "userMetadata.$**" : 1 }, {})
@WildcardIndex 注解支持声明式索引设置,既可用于文档类型,也可用于属性。
如果放置在根级领域实体(标注了 @Document 的实体)类型上,索引解析器将为其创建一个通配符索引。
示例 8. 领域类型上的通配符索引
@Document
@WildcardIndexed
public class Product {
// …
}
db.product.createIndex({ "$**" : 1 },{})
wildcardProjection 可用于指定在索引中包含或排除的键。
示例 9. 带 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 的类型上的投影会被忽略。
示例 10. 属性上的通配符索引
@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 可以在每个文档的基础上定义语言覆盖。以下示例展示了如何创建文本索引并将语言设置为西班牙语:
示例 11. 文本索引用法示例
@Document(language = "spanish")
class SomeEntity {
@TextIndexed String foo;
@Language String lang;
Nested nested;
}
class Nested {
@TextIndexed(weight=5) String bar;
String roo;
}