路由值

当 Elasticsearch 在一个具有多个分片的索引中存储文档时,它会根据文档的 id 来确定使用哪个分片。有时需要预先定义多个文档应索引在同一个分片上(join-types,用于更快地搜索相关数据)。为此,Elasticsearch 提供了定义路由的可能性,路由是一个值,用于计算分片而不是使用 id

Spring Data Elasticsearch 通过以下方式支持数据存储和检索时的路由定义

基于 join-types 的路由

使用 join-types(参见 Join-Type 实现)时,Spring Data Elasticsearch 会自动使用实体的 JoinField 属性中的 parent 属性作为路由的值。

这适用于所有 parent-child 关系只有一层的用例。如果层次更深,比如 child-parent-grandparent 关系 - 就像上面 vote → answer → question 的例子 - 那么需要使用下一节描述的技术显式指定路由(vote 需要使用 question.id 作为路由值)。

自定义路由值

为了给实体定义自定义路由,Spring Data Elasticsearch 提供了一个 @Routing 注解(重用了上面的 Statement 类)

@Document(indexName = "statements")
@Routing("routing")                  (1)
public class Statement {
    @Id
    private String id;

    @Field(type = FieldType.Text)
    private String text;

    @JoinTypeRelations(
        relations =
            {
                @JoinTypeRelation(parent = "question", children = {"answer", "comment"}),
                @JoinTypeRelation(parent = "answer", children = "vote")
            }
    )
    private JoinField<String> relation;

    @Nullable
    @Field(type = FieldType.Keyword)
    private String routing;          (2)

    // getter/setter...
}
1 这将 "routing" 定义为路由规范
2 一个名为 *routing* 的属性

如果注解的路由规范是一个普通字符串而不是 SpEL 表达式,它将被解释为实体的属性名称,示例中它是 *routing* 属性。该属性的值将被用作使用该实体的所有请求的路由值。

也可以在 @Document 注解中使用 SpEL 表达式,如下所示

@Document(indexName = "statements")
@Routing("@myBean.getRouting(#entity)")
public class Statement{
    // all the needed stuff
}

在这种情况下,用户需要提供一个名为 *myBean* 的 bean,它有一个 String getRouting(Object) 方法。在 SpEL 表达式中引用实体时必须使用 "#entity",并且返回值必须为 null 或路由值(作为 String)。

如果普通属性名称和 SpEL 表达式不足以自定义路由定义,则可以实现 RoutingResolver 接口。然后可以将其设置到 ElasticOperations 实例上

RoutingResolver resolver = ...;

ElasticsearchOperations customOperations= operations.withRouting(resolver);

withRouting() 函数返回原始 ElasticsearchOperations 实例的一个副本,其中设置了自定义路由。

当一个实体在存储到 Elasticsearch 时定义了路由,那么在执行 *get* 或 *delete* 操作时必须提供相同的值。对于不使用实体的方法 - 例如 get(ID)delete(ID) - 可以使用 ElasticsearchOperations.withRouting(RoutingResolver) 方法,如下所示

String id = "someId";
String routing = "theRoutingValue";

// get an entity
Statement s = operations
                .withRouting(RoutingResolver.just(routing))       (1)
                .get(id, Statement.class);

// delete an entity
operations.withRouting(RoutingResolver.just(routing)).delete(id);
1 RoutingResolver.just(s) 返回一个解析器,它将只返回给定的字符串。