Elasticsearch(十二)【NEST高级客户端--规范】

来源:互联网 发布:淘宝q币充值改金额 编辑:程序博客网 时间:2024/05/18 00:42

规范

NEST有一些规范用于推理

文档路径API功能字段IDS索引名称索引路径属性

文档路径

Elasticsearch中的许多API描述了一个文档的路径。 在NEST中,除了生成一个构造函数之外,还分别创建了Index,Type和Id,我们还生成一个构造函数,可以使用DocumentPath<T>类型的实例更简洁地描述文档的路径。

创建新实例

这里我们创建一个基于项目的id为1的新文档路径

IDocumentPath path = new DocumentPath<Project>(1);Expect("project").WhenSerializing(path.Index);Expect("project").WhenSerializing(path.Type);Expect(1).WhenSerializing(path.Id);

您仍然可以覆盖推断索引和类型名称

path = new DocumentPath<Project>(1).Type("project1");Expect("project1").WhenSerializing(path.Type);path = new DocumentPath<Project>(1).Index("project1");Expect("project1").WhenSerializing(path.Index);

并且还有一种静态方式来描述这样的路径

path = DocumentPath<Project>.Id(1);Expect("project").WhenSerializing(path.Index);Expect("project").WhenSerializing(path.Type);Expect(1).WhenSerializing(path.Id);

从文档类型实例创建

如果您有一个文档的实例,您也可以使用它来生成文档路径

var project = new Project { Name = "hello-world" };

这里我们根据Project,project的实例创建一个新的文档路径

IDocumentPath path = new DocumentPath<Project>(project);Expect("project").WhenSerializing(path.Index);Expect("project").WhenSerializing(path.Type);Expect("hello-world").WhenSerializing(path.Id);

您仍然可以覆盖推断索引和类型名称

path = new DocumentPath<Project>(project).Type("project1");Expect("project1").WhenSerializing(path.Type);path = new DocumentPath<Project>(project).Index("project1");Expect("project1").WhenSerializing(path.Index);

而且,还有一种描述这种路径的静态方式

path = DocumentPath<Project>.Id(project);Expect("project").WhenSerializing(path.Index);Expect("project").WhenSerializing(path.Type);Expect("hello-world").WhenSerializing(path.Id);DocumentPath<Project> p = project;

具有请求的示例

var project = new Project { Name = "hello-world" };

我们可以看到DocumentPath如何帮助您更简单地描述您的请求的示例

var request = new IndexRequest<Project>(2) { Document = project };request = new IndexRequest<Project>(project) { };

当与完整的构造函数和手动通过文档进行比较时,DocumentPath的优点变得明显。 将以下不使用DocumentPath<T>的请求与前面的示例进行比较

request = new IndexRequest<Project>(IndexName.From<Project>(), TypeName.From<Project>(), 2){    Document = project};

特征推论

Elasticsearch中的一些URI采用了功能枚举。 在NEST中,URI上的路由值表示为实现接口IUrlParameter的类。 由于枚举不能在C#中实现接口,所以将使用Feature枚举隐式转换为的Features类来表示Feature类型的路由参数。

构造函数

直接使用Features构造函数是可行的,而是涉及到

Features fieldString = Feature.Mappings | Feature.Aliases;Expect("_mappings,_aliases")    .WhenSerializing(fieldString);

这里我们新建一个GET索引elasticsearch 请求,它需要Indices和Features。 注意我们如何直接使用Feature枚举。

var request = new GetIndexRequest(All, Feature.Settings);

字段推测

Elasticsearch API中的几个地方期望从原始源文档中的字段的路径作为字符串值。 NEST允许您使用C#表达式来强烈地键入这些字段路径字符串。

这些表达式分配给一个名为Field的类型,并且有几种方法来创建一个实例

构造函数

直接使用构造函数是可行的,但是当从成员访问lambda表达式解析时可以相当的参与

var fieldString = new Field("name");var fieldProperty = new Field(typeof(Project).GetProperty(nameof(Project.Name)));Expression<Func<Project, object>> expression = p => p.Name;var fieldExpression = new Field(expression);Expect("name")    .WhenSerializing(fieldExpression)    .WhenSerializing(fieldString)    .WhenSerializing(fieldProperty);When using the constructor and passing a value for Name, Property or Expression, ComparisonValue is also set on the Field instance; this is used when    determining Field equality    getting the hash code for a Field instance ImportantBoost values are not taken into account when determining equality.var fieldStringWithBoostTwo = new Field("name^2");var fieldStringWithBoostThree = new Field("name^3");Expression<Func<Project, object>> expression = p => p.Name;var fieldExpression = new Field(expression);var fieldProperty = new Field(typeof(Project).GetProperty(nameof(Project.Name)));fieldStringWithBoostTwo.GetHashCode().Should().NotBe(0);fieldStringWithBoostThree.GetHashCode().Should().NotBe(0);fieldExpression.GetHashCode().Should().NotBe(0);fieldProperty.GetHashCode().Should().NotBe(0);fieldStringWithBoostTwo.Should().Be(fieldStringWithBoostThree);

字段名称与Boost

当指定Field名称时,该名称可以包括boost值; NEST将拆分名称和提升值并设置Boost属性; 作为字符串一部分的boost值优先于可以作为第二个构造函数参数传递的boost值

Field fieldString = "name^2";Field fieldStringConstructor = new Field("name^2");Field fieldStringCreate = new Field("name^2", 3); fieldString.Name.Should().Be("name");fieldStringConstructor.Name.Should().Be("name");fieldStringCreate.Name.Should().Be("name");fieldString.Boost.Should().Be(2);fieldStringConstructor.Boost.Should().Be(2);fieldStringCreate.Boost.Should().Be(2);

隐性转换

除了使用构造函数,您还可以将stringPropertyInfo和成员访问lambda表达式隐式转换为Field。 然而,对于表达式,这仍然是相当重要的,因为表达式首先需要分配给明确指定表达式委托类型的变量。

Field fieldString = "name";Field fieldProperty = typeof(Project).GetProperty(nameof(Project.Name));Expression<Func<Project, object>> expression = p => p.Name;Field fieldExpression = expression;Expect("name")    .WhenSerializing(fieldString)    .WhenSerializing(fieldProperty)    .WhenSerializing(fieldExpression);

使用Nest.Infer方法

为了简化从表达式创建一个Field实例,可以使用一个静态Infer类

此示例使用静态导入using static Nest.Infer; 在使用指令中将Nest.Infer.Field<T>()简化为Field<T>()。 如果复制任何这些示例,请确保包含此静态导入

Field fieldString = "name";

但是对于表达式来说,这仍然是相当涉及的

var fieldExpression = Infer.Field<Project>(p => p.Name);

这甚至可以使用静态导入进一步缩短。 现在我们第一个使用构造函数的例子更简单了!

fieldExpression = Field<Project>(p => p.Name);Expect("name")    .WhenSerializing(fieldString)    .WhenSerializing(fieldExpression);

您可以使用字符串以及使用Nest.Infer.Field在字段中指定boosts

fieldString = "name^2.1";fieldString.Boost.Should().Be(2.1);fieldExpression = Field<Project>(p => p.Name, 2.1);fieldExpression.Boost.Should().Be(2.1);Expect("name^2.1")    .WhenSerializing(fieldString)    .WhenSerializing(fieldExpression);

字段命名风格

默认情况下,NEST可以使所有字段名称更好地与典型的JavaScript和JSON约定相一致

ConnectionSettings上使用DefaultFieldNameInferrer(),可以更改此行为

var setup = WithConnectionSettings(s => s.DefaultFieldNameInferrer(p => p.ToUpper()));setup.Expect("NAME").WhenSerializing(Field<Project>(p => p.Name));

然而,string类型始终是逐字逐行传递的

setup.Expect("NaMe").WhenSerializing<Field>("NaMe");

您希望表达式具有相同的行为,只需将Func<string,string>传递给DefaultFieldNameInferrer即可更改名称

setup = WithConnectionSettings(s => s.DefaultFieldNameInferrer(p => p));setup.Expect("Name").WhenSerializing(Field<Project>(p => p.Name));

复杂字段名称表达式

您可以按照您的属性表达任何深度。 这里我们正在遍历LeadDeveloper FirstName

Expect("leadDeveloper.firstName").WhenSerializing(Field<Project>(p => p.LeadDeveloper.FirstName));

处理集合索引器时,索引器访问被忽略,允许您遍历集合的属性

Expect("curatedTags").WhenSerializing(Field<Project>(p => p.CuratedTags[0]));

同样,LINQ的.First()方法也可以

Expect("curatedTags").WhenSerializing(Field<Project>(p => p.CuratedTags.First()));Expect("curatedTags.added").WhenSerializing(Field<Project>(p => p.CuratedTags[0].Added));Expect("curatedTags.name").WhenSerializing(Field<Project>(p => p.CuratedTags.First().Name));

记住,这些是表达式,而不是将被执行的实际代码

假设字典上的索引器描述属性名称

Expect("metadata.hardcoded").WhenSerializing(Field<Project>(p => p.Metadata["hardcoded"]));Expect("metadata.hardcoded.created").WhenSerializing(Field<Project>(p => p.Metadata["hardcoded"].Created));

这里的一个很酷的功能是NEST将评估传递给索引器的变量

var variable = "var";Expect("metadata.var").WhenSerializing(Field<Project>(p => p.Metadata[variable]));Expect("metadata.var.created").WhenSerializing(Field<Project>(p => p.Metadata[variable].Created));

如果您使用Elasticearch的多字段,那么您真的应该使用它们,因为它们允许您以多种不同的方式分析字符串,这些”virtual”子字段并不总是映射到您的POCO。 通过在表达式上调用.Suffix(),可以描述应该映射的子字段以及它们的映射方式

Expect("leadDeveloper.firstName.raw").WhenSerializing(    Field<Project>(p => p.LeadDeveloper.FirstName.Suffix("raw")));Expect("curatedTags.raw").WhenSerializing(    Field<Project>(p => p.CuratedTags[0].Suffix("raw")));Expect("curatedTags.raw").WhenSerializing(    Field<Project>(p => p.CuratedTags.First().Suffix("raw")));Expect("curatedTags.added.raw").WhenSerializing(    Field<Project>(p => p.CuratedTags[0].Added.Suffix("raw")));Expect("metadata.hardcoded.raw").WhenSerializing(    Field<Project>(p => p.Metadata["hardcoded"].Suffix("raw")));Expect("metadata.hardcoded.created.raw").WhenSerializing(    Field<Project>(p => p.Metadata["hardcoded"].Created.Suffix("raw")));

你甚至可以链接.Suffix()调用任何深度!

Expect("curatedTags.name.raw.evendeeper").WhenSerializing(    Field<Project>(p => p.CuratedTags.First().Name.Suffix("raw").Suffix("evendeeper")));

传递给后缀的变量也将被评估

var suffix = "unanalyzed";Expect("metadata.var.unanalyzed").WhenSerializing(    Field<Project>(p => p.Metadata[variable].Suffix(suffix)));Expect("metadata.var.created.unanalyzed").WhenSerializing(    Field<Project>(p => p.Metadata[variable].Created.Suffix(suffix)));

后缀也可以使用.AppendSuffix()附加到表达式。 在您要将相同后缀应用于字段列表的情况下,这是非常有用的。

这里我们有一个表达式列表

var expressions = new List<Expression<Func<Project, object>>>{    p => p.Name,    p => p.Description,    p => p.CuratedTags.First().Name,    p => p.LeadDeveloper.FirstName,    p => p.Metadata["hardcoded"]};

并且我们要为每个添加后缀“raw”

var fieldExpressions =    expressions.Select<Expression<Func<Project, object>>, Field>(e => e.AppendSuffix("raw")).ToList();Expect("name.raw").WhenSerializing(fieldExpressions[0]);Expect("description.raw").WhenSerializing(fieldExpressions[1]);Expect("curatedTags.name.raw").WhenSerializing(fieldExpressions[2]);Expect("leadDeveloper.firstName.raw").WhenSerializing(fieldExpressions[3]);Expect("metadata.hardcoded.raw").WhenSerializing(fieldExpressions[4]);

或者我们甚至可能想链接多个.AppendSuffix()调用

var multiSuffixFieldExpressions =    expressions.Select<Expression<Func<Project, object>>, Field>(e => e.AppendSuffix("raw").AppendSuffix("evendeeper")).ToList();Expect("name.raw.evendeeper").WhenSerializing(multiSuffixFieldExpressions[0]);Expect("description.raw.evendeeper").WhenSerializing(multiSuffixFieldExpressions[1]);Expect("curatedTags.name.raw.evendeeper").WhenSerializing(multiSuffixFieldExpressions[2]);Expect("leadDeveloper.firstName.raw.evendeeper").WhenSerializing(multiSuffixFieldExpressions[3]);Expect("metadata.hardcoded.raw.evendeeper").WhenSerializing(multiSuffixFieldExpressions[4]);

基于属性的命名

使用NEST的属性特性可以为属性指定一个新名称

public class BuiltIn{    [Text(Name = "naam")]    public string Name { get; set; }}Expect("naam").WhenSerializing(Field<BuiltIn>(p => p.Name));

从NEST 2.x开始,我们还要求序列化程序是否可以将属性解析为名称。 在这里,我们要求默认的JsonNetSerializer解析一个属性名称,并将JsonPropertyAttribute考虑在内

public class SerializerSpecific{    [JsonProperty("nameInJson")]    public string Name { get; set; }}Expect("nameInJson").WhenSerializing(Field<SerializerSpecific>(p => p.Name));

如果属性中都存在NEST属性特性和序列化器特定属性,则NEST属性优先

public class Both{    [Text(Name = "naam")]    [JsonProperty("nameInJson")]    public string Name { get; set; }}Expect("naam").WhenSerializing(Field<Both>(p => p.Name));Expect(new    {        naam = "Martijn Laarman"    }).WhenSerializing(new Both { Name = "Martijn Laarman" });

字段推测缓存

每个连接设置实例缓存字段名称的解析。 为了演示,请采取以下简单的POCO

class A { public C C { get; set; } }class B { public C C { get; set; } }class C{    public string Name { get; set; }}var client = TestClient.Default;var fieldNameOnA = client.Infer.Field(Field<A>(p => p.C.Name));var fieldNameOnB = client.Infer.Field(Field<B>(p => p.C.Name));

这里我们有两个类似形状的表达式,一个来自A,一个来自B,将按照预期解析为相同的字段名称

fieldNameOnA.Should().Be("c.name");fieldNameOnB.Should().Be("c.name");

现在,当我们在A上解析属性C的字段路径时,现在我们创建一个新的连接设置,使用A类的C映射到"d",这与B上的属性C不同

var newConnectionSettings = TestClient.CreateSettings(modifySettings: s => s    .InferMappingFor<A>(m => m        .Rename(p => p.C, "d")    ));var newClient = new ElasticClient(newConnectionSettings);fieldNameOnA = newClient.Infer.Field(Field<A>(p => p.C.Name));fieldNameOnB = newClient.Infer.Field(Field<B>(p => p.C.Name));fieldNameOnA.Should().Be("d.name");fieldNameOnB.Should().Be("c.name");

然而,我们并没有使用其单独的连接设置来破坏第一个客户端实例的推断

fieldNameOnA = client.Infer.Field(Field<A>(p => p.C.Name));fieldNameOnB = client.Infer.Field(Field<B>(p => p.C.Name));fieldNameOnA.Should().Be("c.name");fieldNameOnB.Should().Be("c.name");

推理优先

要包装,推断字段名称的优先级为:

 使用`.Rename()`连接设置上的属性的硬编码重命名 NEST属性映射 询问序列化器是否具有逐字数值,例如它具有显式的`JsonProperty`属性。 将MemberInfo的名称传递给`DefaultFieldNameInferrer`,默认情况下为camelCases

以下示例类将演示此优先级

class Precedence{    [Text(Name = "renamedIgnoresNest")]    [JsonProperty("renamedIgnoresJsonProperty")]    public string RenamedOnConnectionSettings { get; set; }     [Text(Name = "nestAtt")]    [JsonProperty("jsonProp")]    public string NestAttribute { get; set; }     [JsonProperty("jsonProp")]    public string JsonProperty { get; set; }     [JsonProperty("dontaskme")]    public string AskSerializer { get; set; }     public string DefaultFieldNameInferrer { get; set; } }

在这里,我们创建一个自定义序列化器,重命名任何名为AskSerializer的属性为ask

class CustomSerializer : JsonNetSerializer{    public CustomSerializer(IConnectionSettingsValues settings) : base(settings) { }    public override IPropertyMapping CreatePropertyMapping(MemberInfo memberInfo)    {        return memberInfo.Name == nameof(Precedence.AskSerializer)            ? new PropertyMapping { Name = "ask" }            : base.CreatePropertyMapping(memberInfo);    }}

在这里,我们使用.Rename()ConnectionSettings上的属性进行显式重命名,并且不会逐字地映射的所有属性应该是大写

var usingSettings = WithConnectionSettings(s => s    .InferMappingFor<Precedence>(m => m        .Rename(p => p.RenamedOnConnectionSettings, "renamed")    )    .DefaultFieldNameInferrer(p => p.ToUpperInvariant())).WithSerializer(s => new CustomSerializer(s));usingSettings.Expect("renamed").ForField(Field<Precedence>(p => p.RenamedOnConnectionSettings));usingSettings.Expect("nestAtt").ForField(Field<Precedence>(p => p.NestAttribute));usingSettings.Expect("jsonProp").ForField(Field<Precedence>(p => p.JsonProperty));usingSettings.Expect("ask").ForField(Field<Precedence>(p => p.AskSerializer));usingSettings.Expect("DEFAULTFIELDNAMEINFERRER").ForField(Field<Precedence>(p => p.DefaultFieldNameInferrer));

索引文档时也适用相同的命名规则

usingSettings.Expect(new []{    "ask",    "DEFAULTFIELDNAMEINFERRER",    "jsonProp",    "nestAtt",    "renamed"}).AsPropertiesOf(new Precedence{    RenamedOnConnectionSettings = "renamed on connection settings",    NestAttribute = "using a nest attribute",    JsonProperty = "the default serializer resolves json property attributes",    AskSerializer = "serializer fiddled with this one",    DefaultFieldNameInferrer = "shouting much?"});public class Parent        {public int Id { get; set; }public string Description { get; set; }public string IgnoreMe { get; set; }        }public class Child : Parent { }

继承的属性可以像预期一样被忽略和重命名

var usingSettings = WithConnectionSettings(s => s    .InferMappingFor<Child>(m => m        .Rename(p => p.Description, "desc")        .Ignore(p => p.IgnoreMe)    ));usingSettings.Expect(new []{    "id",    "desc",}).AsPropertiesOf(new Child{    Id = 1,    Description = "using a nest attribute",    IgnoreMe = "the default serializer resolves json property attributes",});
0 0
原创粉丝点击