New .Net client fails to synchronise GeoLocation as geo_point in index

Hi all,

We are currently in the process of migrating away from NEST given the new .Net Client seems to be the way to go going forwards however, the whole process has been nothing but painful so far.

The examples and documentation on the website are very lacking for the new .Net client and only amount to a getting started with no detailed breakdown of available fields and mappings for things like GeoLocation or indeed the deprication of old field types that used to be available in NEST and their new .Net client equivalent.

We have a large collection of objects we are looking to index. Previously we used to the annotations on our .Net class to indicate how each field should be mapped when the index is created but these have now been deprecated as part of the new .Net client. For most fields, the default mappings appear to be correct however, when we use the GeoLocation type provided in the .Net client nugget package in our .net class and map the relevant properties to it, when it get index into Elasticsearch it's not being mapped as a geo_point and is instead mapping the lat and long properties as two double/numeric values.

Can somebody shed some light on why this is the case and why the type provided by the Elastic client library is not mapping to the correct field type? I would have assumed a type provided by the library would have all the correct mappings automatically setup but that appears to not be the case when we view the indexes in Kibana.

We've also tried mapping the fields explicitly after the fact in two ways.

  • Create the index
  • Update the relevant fields to geo_point mapping
  • Index the fields

However this results in our objects not being indexed at all. Or

  • Indexing our objects directly to a non-existent index which creates the index with the documents but all the field mappings are wrong despite using the GeoLocation type.
  • Attempting to retrospectively alter the mappings to apply the geo_point property

The code below is how we are currently trying to achieve this using the .net client

    await _elasticsearchClient.IndexManyAsync<VenueResult>(data, finalIndexName);

            await _elasticsearchClient.Indices.PutMappingAsync<VenueResult>(mappings => mappings
                .Indices(finalIndexName)
                .Properties(properties => properties
                    .GeoPoint("location.coordinate")
                )

Could anyone shed some light on how this should be achieved using the new client? Alternatively, is there some hidden documentation on this I can be directed towards given none of this is mentioned in the documentation? We have a lot of logic on our site that utilises geolocation searches and finding places nearby so getting this to map correctly so we can utilise Elastic's strengths in geosearches is a critical implementation point for us.

Hi @jespin,

could you please share your VenueResult class?

Your code looks fine, except for maybe they way how you refer to the location property.

Sure thing.

public class VenueResult
{
   public string Name { get; set; } = string.Empty;
   public string Type { get; set; } = string.Empty;
   public LocationData Location { get; set; } = new LocationData();
}

public class LocationData
{
   public GeoLocation Coordinate { get; set; }
   public double MapZoomLevel { get; set; }
}

Where GeoLocation is the type from the namespace Elastic.Clients.Elasticsearch

Values assigned as follows:

  var lat  = {latitudeValue} 
  var lon = {longitudeValue}
  GeoLocation? coordinates = null;
  coordinates = GeoLocation.LatitudeLongitude(new LatLonGeoLocation() { Lat = lat, Lon = lon });

If it helps, I've also tried the following:

await _elasticsearchClient.Indices.CreateAsync<VenueResult>(finalIndexName, c => c.Mappings
                (m =>
                    m.Properties(properties => properties
                        .GeoPoint(p => p.Location.Coordinate)
                    ))
            );

This does map a property called Coordinates in the index with the correct type of geo point but it's a completely separate property to the one which we actually want to map "location.coordinates" and I can see that those properties are in there as floats (see screenshot)

Thanks for sharing the code @jespin .

Since you are dealing with a nested object, you have to reflect the same hierarchy in your mappings (read more here).

The correct way of mapping your current class should look like this:

await client.Indices.CreateAsync<VenueResult>("geotest", c => c
    .Mappings(m => m
        .Properties(properties => properties
            .Object(p => p.Location, x => x.Properties(p => p.GeoPoint(x => x.Location.Coordinate)))
        )
    )
);

This correctly creates the desired mappings in my test:

# Response:
{
  "geotest" : {
    "mappings" : {
      "properties" : {
        "location" : {
          "properties" : {
            "coordinate" : {
              "type" : "geo_point"
            },
            "mapZoomLevel" : {
              "type" : "float"
            }
          }
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "type" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

Please let me know if that solved your issue or if you need further assistance :slightly_smiling_face:

Perfect mate. That's sorted it! Thank you very much.