Elasticsearch.Nest 教程系列 9-6 转换:Document paths | 文档路径


Elasticsearch中的许多API描述了文档的路径。在NEST中,除了生成分别带有 Index,Type 和 Id 类型的构造函数外,还有一个构造函数允许你使用 DocumentPath 类型的实例更简洁地描述文档的路径。

eg:创建一个基于 Project 类的 Document,Id=1

IDocumentPath path = new DocumentPath<Project>(1);

你也可以指定 Index 的名字:

var path = new DocumentPath<Project>(1).Index("project1");

通过 DocumentPath 静态方法来进行指定:

Elasticsearch.Nest 教程系列 9-5 转换:Property name inference | 属性名推断


在 lambda 表达式 “主体” 上追加后缀

可以将后缀追加到 lambda 表达式的主体上,这在以下情况下很有用:

  • 将POCO属性映射为多字段,并且希望对 lambda 表达式使用强类型访问:将后缀追加到生成的字段名称中,以便访问多字段的特定子字段。

.Suffix() 扩展方法可用于此目的,并且当序列化以此方式追加后缀的表达式时,序列化的属性名称将解析为最后一个标记:

Expression<Func<Project, object>> expression = p => p.Name.Suffix("raw");
Expect("raw").WhenSerializing<PropertyName>(expression);

在 lambda 表达式上追加后缀

可以使用.ApplySuffix() 扩展方法将后缀直接应用于 lambda 表达式:

Expression<Func<Project, object>> expression = p => p.Name;
expression = expression.AppendSuffix("raw");
Expect("raw").WhenSerializing<PropertyName>(expression);

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