标签:教程

Elasticsearch.Nest 教程系列 9-4 转换:Field inference | 字段推断


不少 Elasticsearch API 接口在使用的时候会希望知道字段在原始文档中的路径(以字符串的形式),NEST 提供了 Field 类来允许你获得这些字段路径字符串。

构造函数

通过使用 Field 的构造函数:

var fieldString = new Field("name");

//使用重载方法
var fieldProperty = new Field(typeof(Project).GetProperty(nameof(Project.Name)));

//使用 lambda 表达式的方式
Expression<Func<Project, object>> expression = p => p.Name;
var fieldExpression = new Field(expression);

你也可以在实例化的时候设定 boost 值。

eg:当你使用如下构造函数时:

Elasticsearch.Nest 教程系列 9-3 转换:Ids inference | Id 文档标识推断


不少 Elasticsearch API 都需要传递一个 Id 类型的对象。这是一种特殊类型,以下几种类型会自动隐式转换为 Id 类型:

  • Int32
  • Int64
  • String
  • Guid

方法签名中,入参如果需要一个 Id 类型,那么传入以上 4 种类型的时候会自动隐式转换为 Id 类型。

Id idFromInt = 1;
Id idFromLong = 2L;
Id idFromString = "hello-world";
Id idFromGuid = new Guid("D70BD3CF-4E38-46F3-91CA-FCBEF29B148E");

从类型中推断 Id

Nest 并无法直接从一个类型对象中推断出 Id,但提供了 Id.From 方法:

假设你有如下类:

Elasticsearch.Nest 教程系列 9-2 转换:Relation names inference | 关系名推断


当你使用子父级关系的时候,需要将子父级文档索引(添加)到同一个索引中,在 ES 5.x 的时候,你可以通过 / 中的 来进行指定,但从 ES7 开始,已经没有 type 一说(type=_doc),这个时候,你需要用 RelationName 来进行代替。

var settings = new ConnectionSettings()
    .DefaultMappingFor<CommitActivity>(m => m
        .IndexName("projects-and-commits")
        .RelationName("commits")
    )
    .DefaultMappingFor<Project>(m => m
        .IndexName("projects-and-commits")
        .RelationName("projects")
    );

var resolver = new RelationNameResolver(settings);
var relation = resolver.Resolve<Project>();
relation.Should().Be("projects");

relation = resolver.Resolve<CommitActivity>();
relation.Should().Be("commits");
  • RelationName 通过 DefaultTypeNameInferrer 使用一个 string 来表示转换后的 CLR 类型。
  • 显式的配置 TypeName 不会影响原始 CLR 类型。

Elasticsearch.Nest 教程系列 8 聚合:Writing Aggregations | 使用聚合


可以简单将 ES 中的聚合和 Sql server 中的“聚合函数(如 SUM,COUNT 等”)相关联。聚合可以嵌套,通过聚合可以找出某个字段的最大值,最小值,平均值,以及对字段进行求和操作等复杂数据的构建。

另外,ES 还提出了 buckets(桶) 这个概念,你可以简单理解为相当于是 Sql server 中的分组(GROUP BY),即在 ES 中的称 GROUP BY 为“分桶”。

关于 Elasticsearch 中的聚合说明,可以见此

编写聚合

Nest 提供了 3 种方式来让你使用聚合:

  • 通过 lambda 表达式的方式。
  • 通过内建的请求对象 AggregationDictionary。
  • 通过结合二元运算符来简化 AggregationDictionary 的使用。

假设有以下 Project 类:

public class Project
{
    public string Name { get; set; }
    public int Quantity { get; set; }
}

三种方式的请求命令见下方:

Elasticsearch.Nest 教程系列 7-3 搜索:Selecting fields to return | 选择需要返回的字段


有的时候你不需要文档中的所有字段进行返回,有 2 种方法可以让你只返回文档中的某些字段。

通过 Stored Fields

索引文档时,默认情况下,Elasticsearch 会将最初发送的 JSON 文档存储在名为 _source 的特殊字段中。对于每个匹配,搜索查询返回的文档都是从 Elasticsearch 返回的 _source 字段中解析的。

注意: 禁用 _source 意味着不存储原始 JSON 文档,虽然可以节约磁盘,但会导致某些功能无法使用,如无法被检索,无法使用 Reindex API 和高亮。因此禁用 _source 需要根据你的应用场景谨慎考虑。

在搜索请求是通过 .StoredFields 指定要返回的字段值:

Elasticsearch.Nest 教程系列 7-2 搜索:Writing bool queries | 编写布尔查询


在使用查询 DSL 的时候,编写布尔查询命令会很冗长,如使用带有 2 个 should 子句的单个布尔查询:

var searchResults = this.Client.Search<Project>(s => s
    .Query(q => q 
        .Bool(b => b
            .Should(
                bs => bs.Term(p => p.Name, "x"),
                bs => bs.Term(p => p.Name, "y")
            )
        )
    )
);

可以相像,如果有多个嵌套布尔查询,最终代码久会长成这样:

为了解决代码冗长的问题,Nest 重写了一元操作符(“!”和“+”)以及二元操作符(“||”和“&&”)

运算符 对应的操作
&& must
\ \ should
+ filter
! must_not

Elasticsearch.Nest 教程系列 6-2 分析:Testing analyzers | 测试分词器


借助 Analyze API,可以方便测试内置/自定义的分词器。

测试内置分词器

通过 Analyze API,可以查看内置分析器是如何分析一段文本。

  • 基于标准分词器

    var analyzeResponse = _client.Indices.Analyze(a => a
      .Analyzer("standard") 
      .Text("F# is THE SUPERIOR language :)")
    );
    

实际发送的请求如下:

POST /_analyze
{
    "analyzer": "standard",
    "text": [
        "F# is THE SUPERIOR language :)"
    ]
}

响应结果如下:

Elasticsearch.Nest 教程系列 6-1 分析:Writing analyzers | 编写分析器


ES 中的“分析”是指将文本(如任何电子邮件的正文)转换为 tokens 或 terms 的过程,这些 tokens 或 terms 被添加到倒排索引中以进行搜索。分析是由分析器执行的,该分析器可以是内置分析器,也可以是自定义的分析器。

分词器由 3 部分组成:

  • Character Filters:处理原始文本,如去除 html。
    • 一般有 0 个或多个
  • Tokenizer:按照规则切分为单词。
    • 一般只有 1 个
  • Token filter:加工切分后的单词,如转小写、删除停用词,增加同义词等。
    • 一般有 0 个或多个

Elasticsearch.Nest 教程系列 5-3 索引:Ingest Pipeline | Ingest 管道


Ingest 管道是一系列按照声明的顺序执行的处理器。

假设有以下 POCO 类:

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

public class GeoIp
{
    public string CityName { get; set; }
    public string ContinentName { get; set; }
    public string CountryIsoCode { get; set; }
    public GeoLocation Location { get; set; }
    public string RegionName { get; set; }
}

创建一个 Ingestion 管道:
eg:假设为 Person 文档建立索引,创建一个 Ingestion 管道,在被索引到 ES 之前对其进行相关操作:

  • 将 LastName 转换为大写
  • 将姓名的首字母索引到其他字段—这里用 initials 字段
  • 将 IpAddress 转换为具体地址。

Elasticsearch.Nest 教程系列 5-2 索引:Ingest Node | 使用 Ingest 节点


Ingest Node 的作用

在文档索引发生之前,会使用 Ingest 节点进行预处理文档。Ingest 节点会拦截批量和索引请求,之后应用转换(mapping),然后将文档传递回索引或 Bulk API。

  • 即:在数据被索引之前,通过预定义好的处理管道对数据进行预处理。

默认情况下,所有节点都启用 Ingest,也就是说任何节点都可以处理 Ingest 任务。

  • 当然,你也可以创建某些业务专用的 Ingest 节点。

Elasticsearch 会将自动将索引请求重新路由到 Ingest 节点,而您可以优化此路径。