Search code examples
azureazure-cognitive-searchazure-search-.net-sdk

azure cognitive search - geography point problem


I'm specifying a field as the following:

    [SimpleField(IsFilterable = true, IsSortable = true)]
    public  Microsoft.Spatial.GeographyPoint Location { get; set; }

In my index, I can see it was created successfully and with the right content, however when I'm trying to search using geo.distance, it throws the following error:

$filter=geo.distance(Location, geography'POINT(-82.51571 31.89063)') le 30

error:

"Invalid expression: No function signature for the function with name 'geo.distance' matches the specified arguments. The function signatures considered are: geo.distance(Edm.GeographyPoint Nullable=true, Edm.GeographyPoint Nullable=true); geo.distance(Edm.GeometryPoint Nullable=true, Edm.GeometryPoint Nullable=true).\r\nParameter name: $filter"


Solution

  • The Azure SDK is working on comprehensive spatial types to share across services. For now, a separate package is needed to support Microsoft.Spatial. If you're using System.Text.Json (the default for Azure SDK packages matching "Azure.*"), use https://www.nuget.org/packages/Microsoft.Azure.Core.Spatial/1.0.0-beta.1. If you're using Json.NET (i.e. Newtonsoft.Json), use https://www.nuget.org/packages/Microsoft.Azure.Core.Spatial.NewtonsoftJson/1.0.0-beta.1.

    See https://github.com/Azure/azure-sdk-for-net/blob/Microsoft.Azure.Core.Spatial_1.0.0-beta.1/sdk/core/Microsoft.Azure.Core.Spatial.NewtonsoftJson/README.md for an example for how to use the former, and https://github.com/Azure/azure-sdk-for-net/blob/Microsoft.Azure.Core.Spatial.NewtonsoftJson_1.0.0-beta.1/sdk/core/Microsoft.Azure.Core.Spatial.NewtonsoftJson/README.md for the latter.

    You'll need to use those to generate your SearchIndex and republish so that spatial OData filters will work correctly.

    With a few modifications to the source you sent (sans the resource name and API keys - good idea to use environment variables even if those resources are temporary), you'd use something like this:

                Uri serviceEndpoint = new Uri($"https://{serviceName}.search.windows.net/");
                var credential = new AzureKeyCredential(apiKey);
    
                JsonSerializerOptions serializerOptions = new JsonSerializerOptions
                {
                    Converters =
                    {
                        new MicrosoftSpatialGeoJsonConverter()
                    },
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                };
                SearchClientOptions clientOptions = new SearchClientOptions
                {
                    Serializer = new JsonObjectSerializer(serializerOptions)
                };
    
                var adminClient = new SearchIndexClient(serviceEndpoint, credential, clientOptions);
                var searchClient = new SearchClient(serviceEndpoint, indexName, credential, clientOptions);
    
                FieldBuilder fieldBuilder = new FieldBuilder
                {
                    Serializer = clientOptions.Serializer
                };
    
                var definition = new SearchIndex(indexName)
                {
                    Fields = fieldBuilder.Build(typeof(Sample))
                };
    
                adminClient.CreateOrUpdateIndex(definition);
    
                IndexDocumentsBatch<Sample> batch = IndexDocumentsBatch.Create(
                    new IndexDocumentsAction<Sample>(IndexActionType.MergeOrUpload, new Sample { Id = "1", Location = GeographyPoint.Create(0, 0) }
                ));
    
                try
                {
                    IndexDocumentsResult result = searchClient.IndexDocuments(batch);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);
                    // If for some reason any documents are dropped during indexing, you can compensate by delaying and
                    // retrying. This simple demo just logs the failed document keys and continues.
                    Console.WriteLine("Failed to index some of the documents: {0}");
                }
    
                Console.WriteLine("Hello World!");
    

    And your model:

        public class Sample
        {
            [SimpleField(IsKey = true, IsFilterable = true, IsSortable = true)]
            public string Id { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true)]
            public GeographyPoint Location { get; set; }
        }
    

    Even though you didn't use the FieldBuilder initially, you were specifying camelCase for fields but declaring those fields using PascalCase. Note that Azure Cognitive Search is case-sensitive including field names.