Geo spatial query - Using NEST (version 7) to Query WKT Field

We have an index with a GeoShape field that contains boundaries of counties in WKT format such as MULTIPOLYGON (((-9.50189821688122 51.6790656404768, -9.52975113548365 51.6846028881403, -9.49436480898325 51.7002020861983, -9.501898216881 etc. etc.

We also have an index that has properties each with a Lat/Lng co-ordinates in another index.

How can we find which properties are within a particular county using the NEST syntax

Many thanks

Here's an example with Elasticsearch 7.3.0 and NEST 7.1.0

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

    var client = new ElasticClient(settings);
	
	var shapeIndex = "shapes";
	var pointsIndex = "points";

    var createIndexResponse = client.Indices.Create(shapeIndex, c => c
		.Map(m => m
			.Properties(p => p
				.GeoShape(g => g
					.Name("geo_shape")
				)
			)
		)
	);
	
	var indexResponse = client.Index(new {
		geo_shape = @"POLYGON(
			(150.6190465104703 -33.56599572510962,
			 150.71655017257967 -33.500746335741475,
			 150.83190661789217 -33.49960117013259,
			 150.93627673507967 -33.52937055164989,
			 150.91842395187655 -33.64491590448187,
			 150.9815953385953 -33.71462572482956,
			 151.06673938156405 -33.708913931968546,
			 151.10107165695467 -33.72947460810994,
			 151.19170886398592 -33.70662910845036,
			 151.26998645187655 -33.664348911935804,
			 151.2837193620328 -33.64377264974756,
			 151.3056920182828 -33.659776834387586,
			 151.31805163742342 -33.62090436726444,
			 151.32629138351717 -33.57858204255641,
			 151.34689074875155 -33.63576944153703,
			 151.30294543625155 -33.72490599453071,
			 151.29882556320467 -33.79112708083764,
			 151.30706530929842 -33.83904833451899,
			 151.24664050461092 -34.00542268051012,
			 151.16561633468905 -34.08621324029038,
			 150.9870885026578 -34.07483894596873,
			 151.00494128586092 -33.96898487914849,
			 150.83602649093905 -33.94962094389403,
			 150.82641345382967 -33.81166804830615,
			 150.63689929367342 -33.783137595510226,
			 150.6574986589078 -33.633482673997584,
			 150.6190465104703 -33.56599572510962))"
	}, i => i.Index(shapeIndex).Id("sydney"));
	
	var shapeId = indexResponse.Id;

	createIndexResponse = client.Indices.Create(pointsIndex, c => c
		.Map(m => m
			.Properties(p => p
				.GeoShape(g => g
					.Name("geo_shape")
				)
			)
		)
	);
	
	indexResponse = client.Index(new {
		geo_shape = new PointGeoShape(new GeoCoordinate(-33.85705389650999, 151.21507894264175))
	}, i => i.Index(pointsIndex).Id("sydney_opera_house")
	);
	
	var refreshResponse = client.Indices.Refresh(new [] { shapeIndex, pointsIndex });
	
	var searchResponse = client.Search<dynamic>(s => s
		.Index(pointsIndex)
		.Query(q => q
			.GeoShape(g => g
				.Field("geo_shape")
				.Relation(GeoShapeRelation.Within)
				.IndexedShape(f => f
					.Id(shapeId)
					.Index(shapeIndex)
					.Path("geo_shape")
				)
			)
		)
	);
	
	foreach(var hit in searchResponse.Hits)
	{
		Console.WriteLine($"doc with id {hit.Id} found within shape with id {shapeId}");
	}
}

which prints

doc with id sydney_opera_house found within shape with id sydney

Your points will also need to be mapped as point geo_shapes to perform this query. Check out the .NET client documentation for an example.

Many thanks for your reply.
To save us re-indexing:
1-Do we have to make our location (points i.e. lat/lng) as GeoShape?
2-Is there anyway to keep points(lat/lng) as GeoPoints?

Many thanks

Yes, if you want to use them with a geo_shape query

I would suggest keeping points as geo_point if you need to run queries and aggregations that accept geo_point, and also indexing as geo_shape for geo_shape queries.

If you have already indexed the data, you can

  1. Add a new field to the mapping of type geo_shape e.g. field named shape

  2. Run an update_by_query to index the geo_point data into the new geo_shape field, using a script. Something like

    var putMappingResponse = client.Map<object>(c => c
       .Index(pointsIndex)
       .Properties(p => p
       	.GeoShape(g => g
       		.Name("geo_shape")
       	)
       )		
    );
    
    var updateByQueryResponse = client.UpdateByQuery<object>(u => u
       .Index(pointsIndex)
       .Script(@"
       	ctx._source['geo_shape'] = [
       	  'type': 'point',
       	  'coordinates': ctx._source.geo_point
       	]
       ")
       .Refresh(true)
    );
    

    This assumes an existing geo_point field called "geo_point". It updates the mapping to add a new geo_shape field called "geo_shape", then runs an update_by_query to create the geo shape for each document from the existing geo point in _source.

1 Like

Perfect.
many thanks.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.