欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

NEST 之旅 · 开启

程序员文章站 2022-03-28 13:49:01
NEST 之旅 · 开启 Version:5.x 英文原文地址: "Getting started" 说实话,这篇文章翻译的很糟糕。但是我的能力有限,目前只有这个水平,还望各位多指导。 NEST 是 Elasticsearch 的一个高层级的 .NET 客户端,但是它仍然非常接近原始的 Elasti ......

NEST 之旅 · 开启

Version:5.x

英文原文地址:Getting started

说实话,这篇文章翻译的很糟糕。但是我的能力有限,目前只有这个水平,还望各位多指导。

NEST 是 Elasticsearch 的一个高层级的 .NET 客户端,但是它仍然非常接近原始的 Elasticsearch API 。NEST 中所有的请求和响应都是通过类型公开的,这使得它可以快速地启动和运行。

在美丽的外表之下,NEST 有着一个强大的低层级的核心 - Elasticsearch.Net 。有了这个核心,NEST 才得以(向 Elasticsearch 本体服务程序)发送请求和接收响应。这个过程中,NEST 使用和扩展了 Elasticsearch.NET 的许多类型。另外,NEST 公开了这个低级客户端,你可以通过 .LowLevel 属性来访问。

Connecting

连接位于 http://localhost:9200 的本地 Elasticsearch 是非常简单的,你只需要实例化一个客户端的实例即可。

var client = new ElasticClient();

通常情况下,你会需要向客户端传递一些额外的配置选项。比如:你的 Elasticsearch 在远程服务器上运行,那你就需要传递它的具体地址。 ConnectionSettings 参数就是来接收这些配置信息的,有了它你就可以根据不同的配置实例化出相应的客户端。

var settings = new ConnectionSettings(new Uri("http://example.com:9200"))
    .DefaultIndex("people");

var client = new ElasticClient(settings);

仔细看上面这个例子,我们设置了一个默认索引。如果请求中没有明确提供其他索引,并且无法从 POCO 泛型参数中推断出索引,就会使用这个默认索引。ConnectionSettings 继承自 ConnnectionConfiguraion ,Elasticsearch.NET (那个低层级的客户端)通过这个对象来接收额外的配置选项。了解更多的配置选项,请访问 Configuration options

注意,如果没有配置默认索引,而 NEST 又无法从请求中推断出索引,那它就会抛出一个异常。

ConnectionSettings 没有严格要求只能向 Elasticsearch 传递一个地址。NEST 中还提供了几种不同类型的连接池用来配置客户端。下面的栗子使用了一个 SniffingConnectionPool ,它包含了集群中 3 个 Elasticsearch 节点的地址。客户端将使用这种连接池来维护一个集群中可用节点的列表,这样便可以用循环的方式发送请求。

var uris = new[]
{
    new Uri("http://localhost:9200"),
    new Uri("http://localhost:9201"),
    new Uri("http://localhost:9202"),
};

var connectionPool = new SniffingConnectionPool(uris);
var settings = new ConnectionSettings(connectionPool)
    .DefaultIndex("people");

var client = new ElasticClient(settings);

Indexing

将客户端配置好并连接上 Elasticsearch 之后,我们需要往集群里存储一些数据。

假设我们有如下简单传统CLR对象(Plain Old CLR Object)

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

使用同步或者异步的方式索引一个 POCO 实例

var person = new Person
{
    Id = 1,
    FirstName = "Martijn",
    LastName = "Laarman"
};

var indexResponse = client.Index(person); (1)

var asyncIndexResponse = await client.IndexAsync(person); (2)

(1) 同步方法返回一个 IIndexResponse 对象

(2) 异步方法返回一个可等待的 Task<IIndexResponse> 对象

注意:NEST 中所有的方法都提供了同步和异步两种版本,异步方法使用 *Async 后缀命名

上述代码会将文档索引到 /people/person/1 。NEST 很聪明,它会根据 Person 的 CLR 类型来推断 type ,并根据 POCO 的 Id 属性来推断文档的 id 。了解 NEST 中推断文档 id 的其他方式,请参阅 Ids inferenceConnectionSettings 中配置的默认索引会被用来当作请求的索引名。

警告:NEST 会将 POCO 序列化为 JSON 文档发送给 Elasticsearch。默认情况下,在执行序列化时,NEST 将 POCO 的属性名转换成驼峰式字符串。你可以使用 ConnectionSettings.DefaultFieldNameInferrer(Func<string,string>) 方法来改变这一行为。

Searching

我们已经索引了一些文档,接下来就可以开始查询了。

var searchResponse = client.Search<Person>(s => s
    .From(0)
    .Size(10)
    .Query(q => q
         .Match(m => m
            .Field(f => f.FirstName)
            .Query("Martijn")
         )
    )
);

var people = searchResponse.Documents;

people 变量现在保存着名字里包含 Martijn 的前十个人的文档。这个查询的搜索终端是 /people/person/_search ,其中索引名由 ConnectionSettings 中的默认索引确定,类型名由搜索时传递的泛型参数 Person 确定。

使用 .AllTypes() 可以搜索索引内的全部类型

var searchResponse = client.Search<Person>(s => s
    .AllTypes()
    .From(0)
    .Size(10)
    .Query(q => q
         .Match(m => m
            .Field(f => f.FirstName)
            .Query("Martijn")
         )
    )
);

请求中可以提供单个或者多个索引和类型名,了解更多请分别参阅 Indices pathsDocument paths

到目前为止,所有的搜索示例都使用了 NEST 的 Fluent API ,它使用 lambda 表达式构造结构化的查询,这一点模仿了 Elasticsearch 中的 Query DSL 的查询结构。

NEST 还公开了一个对象初始值设定语法,对于那些不热衷于深层嵌套子表达式的开发者,可以用这种方式来构造查询(布局是关键 ~)

这里有个和之前的栗子相同的查询,不过改成了使用对象初始值设定语法来构造查询

var searchRequest = new SearchRequest<Person>(Nest.Indices.All, Types.All) (1)
{
    From = 0,
    Size = 10,
    Query = new MatchQuery
    {
        Field = Infer.Field<Person>(f => f.FirstName),
        Query = "Martijn"
    }
};

var searchResponse = await client.SearchAsync<Person>(searchRequest);

(1) 所有的索引和类型,都在构造函数中指定

笔记:本文开始的时候提到过,高层客户端通过 .LowLevel 属性公开了 Elasticsearch.NET 的低层客户端。有些情况下,低层客户端会更合适。例如:你已经有了一个 JSON 对象,想直接发送给服务器,而不希望再把它转换成 Fluent API 或者 Object Initializer 语法。又或者说,高层客户端有一个 bug,让你不得不选择低层客户端。使用低层客户端,可以通过发送字符串或者匿名类型的请求来完成工作。

通过 .LowLevel 来使用低层客户端,你可以使用 NEST 中所有的强类型和序列化功能

这里有个示例:

var searchResponse = client.LowLevel.Search<SearchResponse<Person>>("people", "person", new
{
    from = 0,
    size = 10,
    query = new
    {
        match = new
        {
            field = "firstName",
            query = "Martijn"
        }
    }
});

var responseJson = searchResponse.Body;

可以看到,请求是一个匿名类型,但是响应体是 NEST 返回的响应类型的具体实现。

Aggregations

除了结构化和非结构化的查询,Elasticsearch 还支持聚合数据

var searchResponse = await client.SearchAsync<Person>(s => s
    .Size(0)
    .Query(q => q
         .Match(m => m
            .Field(f => f.FirstName)
            .Query("Martijn")
         )
    )
    .Aggregations(a => a
        .Terms("last_names", ta => ta
            .Field(f => f.LastName)
        )
    )
);

var termsAggregation = searchResponse.Aggs.Terms("last_names");

这个例子里,match 查询搜索出了名字里有 "Martijn" 的人,这和之前的例子是一样的,不同的是:

  1. 将 size 设为 0 ,因为我们不需要查到的前 10 份文档,我们只对聚合结果感兴趣
  2. 指定了一个 terms 聚合,从而将搜索结果按照姓氏分组

termsAggregation 可以用于获取每个桶的文档数量,其中每个桶的键是姓氏。更多信息,请参阅 Writing aggregations