Eric Phillips
Eric Phillips

Reputation: 920

Copying a nested property to parent object in elasticsearch with Nest client

How can I copy a nested property into a field of the containing POCO in an index mapping definition?

I am able to successfully copy a property into another property with .CopyTo when both fields are at the same object level.

However I am struggling with copying a property on a nested object into a property on the parent object.

Given the objects below, I would like to copy 'Street' from the Address property on a Person into the 'Search' property on Person

Person
{
    public string Search { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}

Address 
{
    public string Street { get; set; }
}

Mapping 'LastName' into 'Search' is simple, as below.

.Map<Person>(map => map
                .AutoMap()
                .Properties(properties => properties
                .Text(text => 
                    text.Name(name => name.LastName)
                        .CopyTo(copyTo => 
                            copyTo.Field(field => field.Search)
                        )
                    )
                )

However I cannot figure out the Nest syntax to copy 'Person.Address.Street' into 'Person.Search'

Upvotes: 1

Views: 976

Answers (1)

Russ Cam
Russ Cam

Reputation: 125488

Here's how you can do it

private static void Main()
{
    var defaultIndex = "my_index";
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

    var settings = new ConnectionSettings(pool)
        .DefaultIndex(defaultIndex);

    var client = new ElasticClient(settings);

    if (client.IndexExists(defaultIndex).Exists)
        client.DeleteIndex(defaultIndex);

    var createIndexResponse = client.CreateIndex(defaultIndex, c => c
        .Settings(s => s
            .NumberOfShards(1)
            .NumberOfReplicas(0)
        )
        .Mappings(m => m
            .Map<Person>(mm => mm
                .AutoMap()
                .Properties(p => p
                    .Object<Address>(o => o
                        .Name(n => n.Address)
                        .AutoMap()
                        .Properties(pp => pp
                            .Text(t => t
                                .Name(nn => nn.Street)
                                .CopyTo(co => co
                                    .Field(Infer.Field<Person>(ff => ff.Search))
                                )
                            )
                        )
                    )
                )
            )
        )
    );

    var indexResponse = client.Index(new Person
        {
            LastName = "foo",
            Address = new Address
            {
                Street = "bar"
            }
        } , i => i
        .Refresh(Refresh.WaitFor)
    );

    var searchResponse = client.Search<Person>(s => s
        .Query(q => q
            .Match(m => m
                .Field(f => f.Search)
                .Query("bar")
            )
        )
    );
}

public class Person
{
    public string Search { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
}

Essentially

  1. Automap the Person properties
  2. Explicitly map the Address property on Person
  3. Automap the Address properties
  4. Explicitly map the Street property and set up CopyTo(...). At this point, the generic type parameter is the Address type, so you either need to use Nest.Infer.Field<T> to access the Search property of Person, or use a string.

The search returns the expected document

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "person",
        "_id" : "5tQDEWgBrKRHlz9qAve8",
        "_score" : 0.2876821,
        "_source" : {
          "lastName" : "foo",
          "address" : {
            "street" : "bar"
          }
        }
      }
    ]
  }
}

copy_to fields in Elasticsearch don't necessarily need to be exposed as a property on the C# POCO, since the _source won't contain a value for them. Exposing as a property however may be useful for strong typing field access.

Upvotes: 3

Related Questions