简体   繁体   English

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

[英]Create index with multi field mapping syntax with NEST 2.x

I just can't seem to get the syntax correct for multi field mapping in NEST 2.0--if that's the correct terminology. 我似乎无法在NEST 2.0中获得多字段映射的语法正确 - 如果这是正确的术语。 Every example I've found for mapping seems to be <= the 1.x version of NEST. 我发现的每个映射示例似乎都是<= NEST的1.x版本。 I'm new to Elasticsearch and NEST, and I've been reading their documentation, but the NEST documentation hasn't been completely updated for 2.x. 我是Elasticsearch和NEST的新手,我一直在阅读他们的文档,但NEST文档还没有完全更新2.x.

Basically, I don't need to index or store the entire type. 基本上,我不需要索引或存储整个类型。 Some fields I need for indexing only, some fields I'll need to index and retrieve, and some I don't need for indexing, just for retrieval. 我只需要索引的一些字段,我需要索引和检索的一些字段,以及一些我不需要索引的字段,仅用于检索。

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.
}

I've have been able to index the entire object and child as-is using the following as an example: 已经能够索引整个对象和孩子一样,是使用以下为例:

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

However, the actual object is a lot larger than this, and I really don't need most of it. 但是,实际对象比这个要大很多,我真的不需要大部分。 I've found this, which looks like what I think I want to do, but in an older version: multi field mapping elasticsearch 我发现了这个,看起来像我想要的那样,但是在旧版本中: 多字段映射elasticsearch

I think "mapping" is what I'm going for, but like I said, I'm new to Elasticsearch and NEST and I'm trying to learn the terminology. 我认为“映射”是我想要的,但就像我说的,我是Elasticsearch和NEST的新手,我正在努力学习术语。

Be gentle! 要温柔! :) It's my first time to ask a question on SO. :)这是我第一次在SO上提问。 Thanks! 谢谢!

As far as I can see, you don't have any complex types that you are trying map. 据我所知,你没有任何复杂的类型,你正在尝试映射。 So you can easily use NEST attributes to map your objects. 因此,您可以轻松使用NEST属性来映射对象。

Check this out: 看一下这个:

[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.
}

After this declaration, to create this mapping in elasticsearch you need to make a call similar to: 在此声明之后,要在elasticsearch中创建此映射,您需要进行类似于以下的调用:

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

With AutoMap() call NEST will read your attributes from your POCO and create a mapping request accordingly. 使用AutoMap()调用NEST将从POCO中读取您的属性并相应地创建映射请求。

Also see "Attribute Based Mapping" section from here . 另请参见此处的 “基于属性的映射”部分。

Cheers! 干杯!

In addition to Colin's and Selçuk's answers, you can also fully control the mapping through the fluent (and object initializer syntax) mapping API. 除了ColinSelçuk的答案之外,您还可以通过流畅(和对象初始化语法)映射API完全控制映射。 Here's an example based on your requirements 这是一个基于您的要求的示例

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.
}

This produces the mapping 这产生了映射

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

Calling .AutoMap() causes NEST to infer the mapping based on the property types and any attributes applied to them. 调用.AutoMap()会导致NEST根据属性类型和应用于它们的任何属性推断映射。 Then .Properties() overrides any of the inferred mappings. 然后.Properties()会覆盖任何推断的映射。 For example 例如

  • CompanyName is mapped as a multi_field with the field companyName analyzed using the standard analyzer and companyName.raw not analyzed. CompanyName被映射为multi_field ,其中使用标准分析器分析字段companyName并且未分析companyName.raw You can reference the latter in your queries using .Field(f => f.CompanyName.Suffix("raw")) 您可以使用.Field(f => f.CompanyName.Suffix("raw"))在查询中引用后者
  • Locations is mapped as a nested type (automapping by default would infer this as an object type mapping). Locations被映射为nested类型(默认情况下自动将其推断为object类型映射)。 You can then define any specific mappings for MyChildType using .Properties() inside of the Nested<MyChildType>() call. 然后,您可以使用Nested<MyChildType>()调用内的MyChildType .Properties()MyChildType定义任何特定映射。

At the time of writing, Nest does not offer a way to map a property in your class to multiple fields in your document mapping using built in attributes. 在撰写本文时, Nest没有提供使用内置属性将类中的属性映射到文档映射中的多个字段的方法。 However, it does provide the facilities needed to do anything with your mappings that you could do if you wrote the JSON yourself. 但是,它确实提供了使用映射执行任何操作所需的工具,如果您自己编写JSON,则可以执行此操作。

Here's a solution I've put together for my own needs. 这是我为自己的需求而整理的解决方案。 It shouldn't be hard to use it as the starting point for whatever you need to do. 使用它作为你需要做的任何事情的起点应该不难。

First, here's an example of the mapping I want to generate 首先,这是我想要生成的映射的示例

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

The product document would then have the name field, which is indexed but not analyzed, and the name.standard field, which uses the standard analyzer. 然后, product文档将具有name字段,该字段已编制索引但未进行分析,而name.standard字段则使用标准分析器。

The C# class that I generate the mapping from looks like this 我生成映射的C#类看起来像这样

[ElasticsearchType]
public class Product
{
    [WantsStandardAnalysisField]
    public string Name { get; set; }
}

Note the WantsStandardAnalysisField attribute. 请注意WantsStandardAnalysisField属性。 That's a custom attribute with no special properties added. 这是一个自定义属性,没有添加任何特殊属性。 Literally just: 字面意思是:

public class WantsStandardAnalysisField : Attribute {}

If I were to use AutoMap as-is, my custom attribute would be ignored and I would get a mapping that has the name field, but not name.standard . 如果我按原样使用AutoMap,我的自定义属性将被忽略,我将得到一个具有name字段但不是name.standard的映射。 Luckily, AutoMap accepts an instance of IPropertyVisitor . 幸运的是,AutoMap接受IPropertyVisitor一个实例。 A base class called NoopPropertyVisitor implements the interface and does nothing at all, so you can subclass it and override only the methods you care about. 名为NoopPropertyVisitor的基类实现了接口并且什么都不做,因此您可以对其进行子类化并仅覆盖您关心的方法。 When you use a property visitor with AutoMap, it will generate a document mapping for you but give you a chance to modify it before it gets sent to Elastic Search. 当您使用具有AutoMap的属性访问者时,它将为您生成文档映射,但在将其发送到Elastic Search之前,您有机会对其进行修改。 All we need to do is look for properties marked with our custom attribute and add a field to them. 我们需要做的就是查找使用我们的自定义属性标记的属性并为其添加一个字段。

Here's an example that does that: 这是一个例子:

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

As you can see, we can do pretty much anything we want with the generated property, including turning off analysis for the main property and adding a new field with its own settings. 如您所见,我们可以使用生成的属性执行任何我们想要的任何操作,包括关闭主属性的分析并添加具有自己设置的新字段。 For fun, you could add a couple properties to the custom attribute allowing you to specify the name of the field you want and the analyzer to use. 为了好玩,您可以向自定义属性添加一些属性,以允许您指定所需字段的名称以及要使用的分析器。 You could even modify the code to see if the attribute has been added multiple times, letting you add as many fields as you want. 您甚至可以修改代码以查看属性是否已多次添加,从而可以根据需要添加任意数量的字段。

If you were to run this through any method that generates a mapping using AutoMap, such as: 如果要通过使用AutoMap生成映射的任何方法来运行此操作,例如:

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

You'll get the desired multi-field mapping. 您将获得所需的多字段映射。 Now you can customize mappings to your heart's content. 现在,您可以根据自己的内容自定义映射。 Enjoy! 请享用!

I think you have at least 2 possibilities to solve your problem: 我认为你至少有两种可能性来解决你的问题:

  1. On indexing: Create something like a metadata model, which is stored just for retrieving. 索引:创建类似元数据模型的东西,仅存储用于检索。 See the _source field to limit the return to this field. 请参阅_source字段以限制返回此字段。
  2. On searching: Specify the fields you want to query: if you don`t want to query the CreatedDate, just don't include it in your search. 在搜索时:指定要查询的字段:如果您不想查询CreatedDate,请不要在搜索中包含它。

In my case I am using both of these approaches to get very fast results :-) 在我的情况下,我使用这两种方法来获得非常快的结果:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM