Search code examples
c#elasticsearchnest

How to reference multi fields when adding a document to elasticsearch


I created an index in my Elasticsearch server and I'm using .Net client NEST to connect to it. Some of the index properties have multiple fields and I want to fill just the correct field.

I created the class 'document' to this mappaing. but I do not know how to access fields of property.

this is the mapping I have (summarized):

"mappings": {
      "document": {
        "properties": {

          "baseUniqueID": {
            "type": "keyword"
          },
          "description": {
            "type": "text",
            "fields": {
              "en": {
                "type": "text",
                "analyzer": "english"
              },
              "fa": {
                "type": "text",
                "analyzer": "nofapersian"
              },
              "fr": {
                "type": "text",
                "analyzer": "french"
              }
            }
          },
          "documentDate": {
            "type": "date"
          },
          "documentType_Id": {
            "type": "keyword"
          },
          "id": {
            "type": "long"
          }

        }
      }
    }

and the document class:

public class Document : BaseInt32KeyEntity
    {
        public string BaseUniqueID{ get; set; }

        public int? Weight { get; set; }

        public DateTime DocumentDate { get; set; }

        public string Description { get; set; }

        public int DocumentType_Id { get; set; }
    }
}

How can I make an object of Document to fill just the field I want (here in this example description.en) and then use IndexDocument to add it to Elasticsearch? something like this:

Document doc = new Document();
doc.Description.en = "This is some description";
ElasticClient.IndexDocument(doc);


Solution

  • You can update an individual field with the Update API

    var client = new ElasticClient();
    
    var documentId = 1;
    
    var partial = new 
    {
        Description = "This is some description"
    };
    
    var updateResponse = client.Update<Document, object>(documentId, u => u
        .Index("your_index")
        .Doc(partial)
    );
    

    The .Index() is only needed if you haven't set up an index convention for the Document type. The document to update is modelled with a partial document because using Document would result in sending default values for value types like the DocumentDate and DocumentType_Id properties.

    doc.Description.en = "This is some description";

    It's not possible to do this as this is not how multi-fields work. With multi-fields, a single document field input can be analyzed in numerous different ways to serve different search needs. In your example, Description property value will be analyzed 4 different ways:

    1. by the standard analyzer with the base text mapping
    2. by the english analyzer with the .en multi-field mapping
    3. by the nofapersian analyzer with the .fa multi-field mapping
    4. by the french analyzer with the .fr multi-field mapping

    The results of analysis will be indexed into the inverted index to allow you to search and query on them, but the original JSON document sent to Elasticsearch will only contain the one "description" field, which is what you will get back when you retrieve the _source for the document (if the _source is stored, which by default it is).

    If you wanted to model these as separate fields on the document, the you could introduce a Description type that has the necessary properties

    public class Description
    {
        public string Standard { get;set; }
        public string English { get;set; }
        public string NoFaPersian{ get;set; }
        public string French{ get;set; }
    }
    

    and then index it as an object type mapping, configuring the analyzer for each

    public class Document
    {
        public string BaseUniqueID { get; set; }
        public int? Weight { get; set; }
        public DateTime DocumentDate { get; set; }
        public Description Description { get; set; }
        public int DocumentType_Id { get; set; }
    }
    
    var indexResponse = client.CreateIndex("your_index", c => c
        .Mappings(m => m
            .Map<Document>(mm => mm
                .AutoMap()
                .Properties(p => p
                    .Object<Description>(o => o
                        .Name(n => n.Description)
                        .AutoMap()
                        .Properties(pp => pp
                            .Text(t => t.Name(n => n.Standard).Analyzer("standard"))
                            .Text(t => t.Name(n => n.English).Analyzer("english"))
                            .Text(t => t.Name(n => n.NoFaPersian).Analyzer("nofapersian"))
                            .Text(t => t.Name(n => n.French).Analyzer("french"))
                        )
                    )
                )
            )
        )       
    );
    

    which produces the following create index request

    PUT http://localhost:9200/your_index?pretty=true 
    {
      "mappings": {
        "document": {
          "properties": {
            "baseUniqueID": {
              "type": "text",
              "fields": {
                "keyword": {
                  "type": "keyword",
                  "ignore_above": 256
                }
              }
            },
            "weight": {
              "type": "integer"
            },
            "documentDate": {
              "type": "date"
            },
            "description": {
              "type": "object",
              "properties": {
                "standard": {
                  "type": "text",
                  "analyzer": "standard"
                },
                "english": {
                  "type": "text",
                  "analyzer": "english"
                },
                "noFaPersian": {
                  "type": "text",
                  "analyzer": "nofapersian"
                },
                "french": {
                  "type": "text",
                  "analyzer": "french"
                }
              }
            },
            "documentType_Id": {
              "type": "integer"
            }
          }
        }
      }
    }