Elasticsearch.Nest 教程系列 4-4 映射:through the Visitor Pattern | 通过访问者模式进行映射

  • 本系列博文是“伪”官方文档翻译(更加本土化),并非完全将官方文档进行翻译,而是在查阅、测试原始文档并转换为自己真知灼见后的“准”翻译。有不同见解 / 说明不周的地方,还请海涵、不吝拍砖 :)

  • 官方文档见此:https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html

  • 本系列对应的版本环境:ElasticSearch@7.3.1,NEST@7.3.1,IDE 和开发平台默认为 VS2019,.NET CORE 2.1


通过访问者模式,可以对所有或者特定的属性进行应用转换。

.AutoMap() 方法内部实现了 Visitor Pattern,默认访问者 NoopPropertyVisitor 不执行任何操作,并允许你实现自己的访问方法。

常规使用:

示例:自定义一个访问者,以禁用数字和布尔类型的 doc 值:

1
2
3
4
5
6
7
8
9
10
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Salary { get; set; }
public DateTime Birthday { get; set; }
public bool IsManager { get; set; }
public List<Employee> Employees { get; set; }
public TimeSpan Hours { get; set; }
}

定义一个访问者:最简单的是实现一个 NoopPropertyVisitor 派生类,并重写 Visit 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DisableDocValuesPropertyVisitor : NoopPropertyVisitor
{
public override void Visit(
INumberProperty type,
PropertyInfo propertyInfo,
ElasticsearchPropertyAttributeBase attribute)
{
type.DocValues = false;
}

public override void Visit(
IBooleanProperty type,
PropertyInfo propertyInfo,
ElasticsearchPropertyAttributeBase attribute)
{
type.DocValues = false;
}
}
  • 将 INumberProperty 和 IBooleanProperty 的 DocValues 设置为 false。

使用上面自定义的属性访问者来映射一个索引

1
2
3
var createIndexResponse = client.Indices.Create("myindex", c => c
.Map<Employee>(m => m.AutoMap(new DisableDocValuesPropertyVisitor()))
);

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
"myindex" : {
"mappings" : {
"properties" : {
"birthday" : {
"type" : "date"
},
"employees" : {
"type" : "object"
},
"firstName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"hours" : {
"type" : "long",
"doc_values" : false
},
"isManager" : {
"type" : "boolean",
"doc_values" : false
},
"lastName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"salary" : {
"type" : "integer",
"doc_values" : false
}
}
}
}
}
  • 可以看到数值类型的(如 salary,hours),布尔类型的(如 isManager),它们的 doc_values 均为 false。

在 PropertyInfo 上的应用

  • 基于访问者模式,你可以直接访问 POCO 的 PropertyInfo 属性,而不仅仅只是访问 IProperty 类型。

示例:将所有 CLR 类型映射成 ES 的 text 数据类型

自定义 EverythingIsATextPropertyVisitor

1
2
3
4
public class EverythingIsATextPropertyVisitor : NoopPropertyVisitor
{
public override IProperty Visit(PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute) => new TextProperty();
}

使用:

1
2
3
var createIndexResponse = client.Indices.Create("myindex", c => c
.Map<Employee>(m => m.AutoMap(new EverythingIsATextPropertyVisitor()))
);

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"myindex" : {
"mappings" : {
"properties" : {
"birthday" : {
"type" : "text"
},
"employees" : {
"type" : "text"
},
"firstName" : {
"type" : "text"
},
"hours" : {
"type" : "text"
},
"isManager" : {
"type" : "text"
},
"lastName" : {
"type" : "text"
},
"salary" : {
"type" : "text"
}
}
}
}
}

跳过某些属性

  • 通过在访问者派生类上实现 SkipProperty,可以让你跳过某些属性被映射。

示例:仅包含派生类中的属性(忽略父类中的属性,这里为 SortedDictionary)

1
2
3
4
5
6
7
8
9
10
11
12
public class DictionaryDocument : SortedDictionary<string, dynamic>
{
public int Id { get; set; }
}

public class IgnoreInheritedPropertiesVisitor<T> : NoopPropertyVisitor
{
public override bool SkipProperty(PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute)
{
return propertyInfo?.DeclaringType != typeof(T);
}
}

创建索引:

1
2
3
var createIndexResponse = client.Indices.Create("myindex", c => c
.Map<DictionaryDocument>(m => m.AutoMap(new IgnoreInheritedPropertiesVisitor<DictionaryDocument>()))
);

结果如下:

1
2
3
4
5
6
7
8
9
10
11
{
"myindex" : {
"mappings" : {
"properties" : {
"id" : {
"type" : "integer"
}
}
}
}
}