使用 NEST 2.x 使用多字段映射语法创建索引

Posted

技术标签:

【中文标题】使用 NEST 2.x 使用多字段映射语法创建索引【英文标题】:Create index with multi field mapping syntax with NEST 2.x 【发布时间】:2016-02-11 21:33:32 【问题描述】:

我似乎无法在 NEST 2.0 中获得正确的多字段映射语法——如果这是正确的术语。我发现的每个映射示例似乎都是

基本上,我不需要索引或存储整个类型。有些字段我只需要索引,有些字段我需要索引和检索,还有一些我不需要索引,只是为了检索。

MyType

    // Index this & allow for retrieval.
    int Id  get; set;  

    // Index this & allow for retrieval.
    // **Also**, in my searching & sorting, I need to sort on this **entire** field, not just individual tokens.
    string CompanyName  get; set;  

    // Don't index this for searching, but do store for display.
    DateTime CreatedDate  get; set; 

    // Index this for searching BUT NOT for retrieval/displaying.
    string CompanyDescription  get; set;  

    // Nest this.
    List<MyChildType> Locations  get; set; 


MyChildType

    // Index this & allow for retrieval.
    string LocationName  get; set; 

    // etc. other properties.

已经能够使用以下示例按原样索引整个对象和子对象:

client.Index(item, i => i.Index(indexName));

但是,实际的对象比这个大很多,我真的不需要大部分。我找到了这个,这看起来像是我想做的,但在旧版本中:multi field mapping elasticsearch

我认为“映射”是我想要的,但就像我说的,我是 Elasticsearch 和 NEST 的新手,我正在努力学习这些术语。

要温柔! :) 这是我第一次就 SO 提出问题。谢谢!

【问题讨论】:

【参考方案1】:

除了Colin's 和Selçuk's 答案之外,您还可以通过流畅(和对象初始化器语法)映射API 完全控制映射。这是一个基于您的要求的示例

void Main()

    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    var connectionSettings = new ConnectionSettings(pool);

    var client = new ElasticClient(connectionSettings);

    client.Map<MyType>(m => m
        .Index("index-name")
        .AutoMap()
        .Properties(p => p
            .String(s => s
                .Name(n => n.CompanyName)
                .Fields(f => f
                    .String(ss => ss
                        .Name("raw")
                        .NotAnalyzed()
                    )
                )
            )
            .Date(d => d
                .Name(n => n.CreatedDate)
                .Index(NonStringIndexOption.No)         
            )
            .String(s => s
                .Name(n => n.CompanyDescription)
                .Store(false)
            )
            .Nested<MyChildType>(n => n
                .Name(nn => nn.Locations.First())
                .AutoMap()
                .Properties(pp => pp
                    /* properties of MyChildType */
                )
            )
        )
    );


public class MyType

    // Index this & allow for retrieval.
    public int Id  get; set; 

    // Index this & allow for retrieval.
    // **Also**, in my searching & sorting, I need to sort on this **entire** field, not just individual tokens.
    public string CompanyName  get; set; 

    // Don't index this for searching, but do store for display.
    public DateTime CreatedDate  get; set; 

    // Index this for searching BUT NOT for retrieval/displaying.
    public string CompanyDescription  get; set; 

    // Nest this.
    public List<MyChildType> Locations  get; set; 


public class MyChildType

    // Index this & allow for retrieval.
    public string LocationName  get; set; 

    // etc. other properties.

这会产生映射


  "properties": 
    "id": 
      "type": "integer"
    ,
    "companyName": 
      "type": "string",
      "fields": 
        "raw": 
          "type": "string",
          "index": "not_analyzed"
        
      
    ,
    "createdDate": 
      "type": "date",
      "index": "no"
    ,
    "companyDescription": 
      "type": "string",
      "store": false
    ,
    "locations": 
      "type": "nested",
      "properties": 
        "locationName": 
          "type": "string"
        
      
    
  

调用.AutoMap() 会使NEST 根据属性类型和应用于它们的任何属性来推断映射。然后.Properties() 覆盖任何推断的映射。例如

CompanyName 映射为 multi_field,其中字段 companyName 使用标准分析器进行分析,companyName.raw 未分析。您可以使用.Field(f =&gt; f.CompanyName.Suffix("raw")) 在查询中引用后者 Locations 被映射为 nested 类型(默认情况下,自动映射会将此推断为 object 类型映射)。然后,您可以在 Nested&lt;MyChildType&gt;() 调用中使用 .Properties()MyChildType 定义任何特定映射。

【讨论】:

非常好的答案。似乎主要对我有用,但是,我在 Nest 2.4.2 中使用 .Suffix("raw") 时遇到了问题(它只是没有用)。最后我只用了+ ".raw"【参考方案2】:

据我所知,您没有任何要尝试映射的复杂类型。因此,您可以轻松地使用 NEST 属性来映射您的对象。

看看这个:

[Nest.ElasticsearchType]
public class MyType

    // Index this & allow for retrieval.
    [Nest.Number(Store=true)]
    int Id  get; set; 

    // Index this & allow for retrieval.
    // **Also**, in my searching & sorting, I need to sort on this **entire** field, not just individual tokens.
    [Nest.String(Store = true, Index=Nest.FieldIndexOption.Analyzed, TermVector=Nest.TermVectorOption.WithPositionsOffsets)]
    string CompanyName  get; set; 

    // Don't index this for searching, but do store for display.
    [Nest.Date(Store=true, Index=Nest.NonStringIndexOption.No)]
    DateTime CreatedDate  get; set; 

    // Index this for searching BUT NOT for retrieval/displaying.
    [Nest.String(Store=false, Index=Nest.FieldIndexOption.Analyzed)]
    string CompanyDescription  get; set; 

    [Nest.Nested(Store=true, IncludeInAll=true)]
    // Nest this.
    List<MyChildType> Locations  get; set; 


[Nest.ElasticsearchType]
public class MyChildType

    // Index this & allow for retrieval.
    [Nest.String(Store=true, Index = Nest.FieldIndexOption.Analyzed)]
    string LocationName  get; set; 

    // etc. other properties.

在此声明之后,要在 elasticsearch 中创建此映射,您需要进行类似以下的调用:

var mappingResponse = elasticClient.Map<MyType>(m => m.AutoMap());

使用 AutoMap() 调用 NEST 将从您的 POCO 中读取您的属性并相应地创建映射请求。

另请参阅here 中的“基于属性的映射”部分。

干杯!

【讨论】:

【参考方案3】:

在撰写本文时,Nest 不提供使用内置属性将类中的属性映射到文档映射中的多个字段的方法。但是,它确实提供了对映射执行任何操作所需的工具,如果您自己编写 JSON,则可以这样做。

这是我根据自己的需要整理的解决方案。将它用作您需要做的任何事情的起点应该不难。

首先,这是我要生成的映射示例


   "product":
      "properties":
         "name":
            "type":"string",
            "index":"not_analyzed",
            "fields":
               "standard":
                  "type":"string",
                  "analyzer":"standard"
               
            
         
      
   

product 文档将包含 name 字段(已编入索引但未分析)和 name.standard 字段(使用标准分析器)。

我从中生成映射的 C# 类如下所示

[ElasticsearchType]
public class Product

    [WantsStandardAnalysisField]
    public string Name  get; set; 

注意WantsStandardAnalysisField 属性。这是一个没有添加特殊属性的自定义属性。从字面上看:

public class WantsStandardAnalysisField : Attribute 

如果我按原样使用 AutoMap,我的自定义属性将被忽略,我将获得一个包含 name 字段但不包含 name.standard 的映射。幸运的是,AutoMap 接受IPropertyVisitor 的实例。一个名为NoopPropertyVisitor 的基类实现了该接口并且什么都不做,因此您可以将其子类化并仅覆盖您关心的方法。当您将属性访问者与 AutoMap 结合使用时,它会为您生成一个文档映射,但您可以在它被发送到 Elastic Search 之前对其进行修改。我们需要做的就是寻找用我们的自定义属性标记的属性并向它们添加一个字段。

这是一个例子:

public class ProductPropertyVisitor : NoopPropertyVisitor

    public override void Visit(IStringProperty type, PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute)
    
        base.Visit(type, propertyInfo, attribute);

        var wsaf = propertyInfo.GetCustomAttribute<WantsStandardAnalysisField>();
        if (wsaf != null)
        
            type.Index = FieldIndexOption.NotAnalyzed;
            type.Fields = new Properties
            
                
                    "standard",
                    new StringProperty
                    
                        Index = FieldIndexOption.Analyzed,
                        Analyzer = "standard"
                    
                
            ;
        
    

如您所见,我们几乎可以对生成的属性做任何我们想做的事情,包括关闭对主属性的分析以及添加一个具有自己设置的新字段。为了好玩,您可以向自定义属性添加几个属性,允许您指定所需字段的名称和要使用的分析器。您甚至可以修改代码以查看该属性是否已被多次添加,让您添加任意数量的字段。

如果您要通过任何使用 AutoMap 生成映射的方法来运行此操作,例如:

new TypeMappingDescriptor<Product>().AutoMap(new ProductPropertyVisitor())

您将获得所需的多字段映射。现在您可以自定义映射到您的心脏内容。尽情享受吧!

【讨论】:

【参考方案4】:

我认为您至少有 2 种可能性来解决您的问题:

    关于索引:创建类似于元数据模型的东西,存储它只是为了检索。请参阅_source field 以限制返回此字段。 搜索时:指定要查询的字段:如果您不想查询 CreatedDate,请不要将其包含在搜索中。

在我的情况下,我使用这两种方法来获得非常快的结果:-)

【讨论】:

以上是关于使用 NEST 2.x 使用多字段映射语法创建索引的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NEST 更新 ElasticSearch 索引中的现有文档?

ElasticsearchNEST高级客户端--Mapping映射

使用 NEST 字段提升的弹性搜索

如何将单个 .NET 类型映射到 ElasticSearch/NEST 中的多个嵌套对象类型?

Nest SuggestCompletion 用法,抛出“不是完成建议字段”异常

ElasticSearch 5.x 上下文建议器 NEST .Net