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


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

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

常规使用:

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

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 方法:

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。

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

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

结果如下:

{
  "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

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

使用:

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

结果如下:

{
  "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)

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);
    }
}

创建索引:

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

结果如下:

{
  "myindex" : {
    "mappings" : {
      "properties" : {
        "id" : {
          "type" : "integer"
        }
      }
    }
  }
}