Index Creation

Spring Data MongoDB 可以自动为使用 @Document 做了注解的实体类型创建索引。自 3.0 版本起必须显式启用索引创建,以防集合生命周期和性能影响带来不良影响。在应用程序启动时和在应用程序运行时首次访问实体类型时,将为最初的实体集自动创建索引。 我们通常建议显式创建索引以基于应用程序的方式控制索引,因为 Spring Data 无法自动为应用程序正在运行时重建的集合创建索引。 如果你要利用诸如 @GeoSpatialIndexed@TextIndexed@CompoundIndex@WildcardIndexed@Indexed 注解,IndexResolver 会提供一个抽象用于编程索引定义创建。你可以使用 IndexOperations 和索引定义来创建索引。索引创建的最理想时间点是在应用程序启动时,特别是在刷新了应用程序上下文之后,这可以通过观察 ContextRefreshedEvent 来触发。此事件可保证上下文完全初始化。请注意,此时其他组件(尤其是 Bean 工厂)可能已访问 MongoDB 数据库。

IndexResolver 会跳过“Map”类属性,除非使用 @WildcardIndexed 进行了注解,因为_map 键_必须是索引定义的一部分。由于地图的目的是使用动态键和值,因此无法从静态映射元数据中解析出这些键。

Example 1. Programmatic Index Creation for a single Domain Type
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);
  }
}
Example 2. Programmatic Index Creation for all Initial Entities
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 版中默认启用了 OFF 自动索引创建。

Compound Indexes

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

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

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

Example 3. Example Compound Index Usage
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;

}

@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;

  // ...
}

Hashed Indexes

哈希索引允许在分片集群内基于哈希进行分片。使用哈希字段值对集合进行分片会导致更加随机的分布。有关详细信息,请参阅 MongoDB Documentation

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

Example 4. Example Hashed Index Usage
@Document
public class DomainType {

  @HashIndexed @Id String id;

  // ...
}

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

Example 5. Example Hashed Index Usage togehter with simple index
@Document
public class DomainType {

  @Indexed
  @HashIndexed
  String value;

  // ...
}

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

Example 6. Example Composed Hashed Index Usage
@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"));

Wildcard Indexes

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

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

Example 7. Programmatic WildcardIndex setup
mongoOperations
    .indexOps(User.class)
    .ensureIndex(new WildcardIndex("userMetadata"));
db.user.createIndex({ "userMetadata.$**" : 1 }, {})

@WildcardIndex 注释允许声明性索引设置,该索引设置既可与文档类型一起使用,也可与属性一起使用。

如果放置在根级别域实体(一个带有 @Document 注释的实体)类型上,则索引解析器将为其创建通配符索引。

Example 8. Wildcard index on domain type
@Document
@WildcardIndexed
public class Product {
	// …
}
db.product.createIndex({ "$**" : 1 },{})

wildcardProjection 可用于指定索引中要包括/排除的键。

Example 9. Wildcard index with 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 注释的类型上的投影。

Example 10. Wildcard index on property
@Document
public class User {
    private @Id String id;

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

Text Indexes

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

创建文本索引允许将几个字段累积到可搜索全文索引中。每个集合只能有一个文本索引,因此标有 @TextIndexed 的所有字段都合并到此索引中。可以对属性进行加权以影响文档的排名结果分数。文本索引的默认语言为英语。要更改默认语言,请将 language 属性设置为所需的任何语言(例如,@Document(language="spanish"))。使用名为 language@Language 的属性允许为每个文档定义语言替代。以下示例显示了如何创建文本索引并将语言设置为西班牙语:

Example 11. Example Text Index Usage
@Document(language = "spanish")
class SomeEntity {

    @TextIndexed String foo;

    @Language String lang;

    Nested nested;
}

class Nested {

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