1

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"

Thiago Custodio
  • 17,332
  • 6
  • 45
  • 90
  • I think I might know what the problem is, but first I need to know -- what's the actual type of the field in your Azure Cognitive Search index? Is it Edm.GeographyPoint, Edm.ComplexType, or something else? – Bruce Johnston Nov 30 '20 at 18:04
  • Hi @BruceJohnston I've sent a detailed email about the issue. I've manually created a field as Edm.GeographyPoint in order to support geo.distance function but I can't index the content. Seems that it's an issue with the serializer that is ommiting the Type property – Thiago Custodio Nov 30 '20 at 18:20

1 Answers1

5

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.

Heath
  • 2,986
  • 18
  • 21
  • Thanks Heath, but the problem is related to push data to Azure Cognitive Search index. The request is invalid. Details: parameters : A resource without a type name was found, but no expected type was specified. To allow entries without type information, the expected type must also be specified when the model is specified. – Thiago Custodio Nov 30 '20 at 20:25
  • You need to pass the serializer to FieldBuilder and regenerate your index. It seems I'm missing that as an example in the README but can add something. Effectively, create a new instance of FieldBuilder and set it's Serializer property to your serializer you created like in the examples. – Heath Nov 30 '20 at 21:39
  • I gave a wrong link above, as well. The first link in the second paragraph should be: https://github.com/Azure/azure-sdk-for-net/blob/Microsoft.Azure.Core.Spatial_1.0.0-beta.1/sdk/core/Microsoft.Azure.Core.Spatial/README.md (I changed the tag but not the path). Sorry. Still is basically the same, though, and I will update to include how to use it with a FieldBuilder. – Heath Nov 30 '20 at 21:46
  • I just tried that too: FieldBuilder fieldBuilder = new FieldBuilder(); fieldBuilder.Serializer = new JsonObjectSerializer(serializerOptions); I've sent a sample app in the DL. Could you please take a look and check what I'm missing? PS: the previous code I added after I sent the email – Thiago Custodio Nov 30 '20 at 21:46