变体类型编辑

Elasticsearch API 拥有许多变体类型:查询、聚合、字段映射、分析器等等。在如此庞大的集合中找到正确的类名可能很困难。

Java API 客户端构建器简化了这一过程:变体类型的构建器,例如 Query,为每个可用实现提供方法。我们在上面的 intervals(一种查询)和 allOfmatchanyOf(各种类型的间隔)中看到了这一点。

这是因为 Java API 客户端中的变体对象是“标记联合”的实现:它们包含它们所持有的变体的标识符(或标记)以及该变体的值。例如,一个 Query 对象可以包含一个带有标记 intervalsIntervalsQuery,一个带有标记 termTermQuery,等等。这种方法允许编写流畅的代码,您可以在其中让 IDE 代码补全功能指导您构建和导航复杂的嵌套结构。

变体构建器为每个可用实现提供 setter 方法。它们使用与常规属性相同的约定,并接受构建器 lambda 表达式和变体实际类型的现成对象。以下是一个构建术语查询的示例。

Query query = new Query.Builder()
    .term(t -> t                          
        .field("name")                    
        .value(v -> v.stringValue("foo"))
    )
    .build();                             

选择 term 变体来构建术语查询。

使用构建器 lambda 表达式构建术语查询。

构建现在包含 term 类型的 TermQuery 对象的 Query

变体对象为每个可用实现提供 getter 方法。这些方法检查对象是否确实包含该类型的变体,并将值向下转换为正确的类型。否则,它们将抛出 IllegalStateException。这种方法允许编写流畅的代码来遍历变体。

assertEquals("foo", query.term().value().stringValue());

变体对象还提供有关它们当前持有的变体类型的信息。

  • 使用每个变体类型的 is 方法:isTerm()isIntervals()isFuzzy() 等等。
  • 使用嵌套的 Kind 枚举,该枚举定义所有变体类型。

然后,可以使用此信息在检查实际类型后导航到特定变体。

if (query.isTerm()) { 
    doSomething(query.term());
}

switch(query._kind()) { 
    case Term:
        doSomething(query.term());
        break;
    case Intervals:
        doSomething(query.intervals());
        break;
    default:
        doSomething(query._kind(), query._get()); 
}

测试变体是否为特定类型。

测试一组更大的变体类型。

获取变体对象持有的类型和值。

Elasticsearch 插件提供的自定义扩展编辑

Elasticsearch 接受可以扩展多种类型可用变体的插件。这包括查询、聚合、文本分析器和标记器、摄取处理器等等。

这些类型的 Java API 客户端类除了内置类型之外,还接受 _custom 变体。这允许您通过在请求中提供任意 JSON 来使用这些插件定义的扩展,以及在响应中接收插件生成的任意 JSON。

在下面的示例中,我们使用了一个假设的插件,该插件添加了一个 sphere-distance 聚合,该聚合根据包含 3D 坐标的文档到参考位置的距离对它们进行分组。

要创建自定义聚合,请使用 _custom() 聚合类型并提供其标识符(由插件定义)和参数。参数可以是任何可以序列化为 JSON 的对象或值。在下面的示例中,我们使用了一个简单的映射。

Map<String, Object> params = new HashMap<>(); 
params.put("interval", 10);
params.put("scale", "log");
params.put("origin", new Double[]{145.0, 12.5, 1649.0});

SearchRequest request = SearchRequest.of(r -> r
    .index("stars")
    .aggregations("neighbors", agg -> agg
        ._custom("sphere-distance", params) 
    )
);

自定义聚合的参数。

创建一个名为 neighbors 的自定义聚合,类型为 sphere-distance,并带有其参数。

自定义变体的结果以 JsonData 对象表示的原始 JSON 返回。然后,您可以遍历 JSON 树以获取数据。由于这并不总是方便,您还可以定义表示该 JSON 数据的类,并从原始 JSON 中反序列化它们。

遍历 JSON 树

SearchResponse<Void> response = esClient.search(request, Void.class); 

JsonData neighbors = response
    .aggregations().get("neighbors")
    ._custom(); 

JsonArray buckets = neighbors.toJson() 
    .asJsonObject()
    .getJsonArray("buckets");

for (JsonValue item : buckets) {
    JsonObject bucket = item.asJsonObject();
    double key = bucket.getJsonNumber("key").doubleValue();
    double docCount = bucket.getJsonNumber("doc_count").longValue();
    doSomething(key, docCount);
}

如果您只对聚合结果感兴趣,而不是搜索命中,请使用 Void(另请参见 聚合)。

获取 neighbors 聚合结果作为自定义 JSON 结果。

遍历 JSON 树以提取结果数据。

使用表示自定义聚合结果的类

SearchResponse<Void> response = esClient.search(request, Void.class);

SphereDistanceAggregate neighbors = response
    .aggregations().get("neighbors")
    ._custom()
    .to(SphereDistanceAggregate.class); 

for (Bucket bucket : neighbors.buckets()) {
    doSomething(bucket.key(), bucket.docCount());
}

将自定义 JSON 反序列化为专用 SphereDistanceAggregate 类。

其中 SphereDistanceAggregate 可以定义如下

public static class SphereDistanceAggregate {
    private final List<Bucket> buckets;
    @JsonCreator
    public SphereDistanceAggregate(
        @JsonProperty("buckets") List<Bucket> buckets
    ) {
        this.buckets = buckets;
    }
    public List<Bucket> buckets() {
        return buckets;
    };
}

public static class Bucket {
    private final double key;
    private final double docCount;
    @JsonCreator
    public Bucket(
        @JsonProperty("key") double key,
        @JsonProperty("doc_count") double docCount) {
        this.key = key;
        this.docCount = docCount;
    }
    public double key() {
        return key;
    }
    public double docCount() {
        return docCount;
    }
}

上面示例的源代码可以在 Java API 客户端测试 中找到。