hougumnaah
hougumnaah

Reputation: 45

Is it possible to populate a collection in an Azure Search index?

I've been currently working with Azure Search, and I stumbled upon a scenario where I have two tables: one describes a place, and the other has multiple photos for each place.

I wanted to use two indexers to populate the same index, where one would populate the place's details (name, coordinates, etc), while the other would get the photos, run a custom WebApiSkill that gets certain details from the photo, and add them to a complex collection on the index.

The issue is, I can't figure out how to populate that photo collection on the index. I have tried defining my outputFieldMappings as Photos/*/Details, or just Photos/Details, but I keep getting the same error:

Microsoft.Rest.Azure.CloudException: 'Output field mapping specifies target field 'Photos/*/PhotoURI' that doesn't exist in the index'

Considering this, I would like to know if it is at all possible to populate a complex collection like this in Azure Search, and if it is, how would I go about doing it?


Tables:

CREATE TABLE Places
(
    Id             bigint         not null  IDENTITY(1,1)  PRIMARY KEY,
    Name           nvarchar(50)   not null,
    Location       nvarchar(50)   not null
);

CREATE TABLE PlacePhotos
(
    PhotoURI  nvarchar(200)  not null  PRIMARY KEY,
    PlaceId   bigint         not null  REFERENCES Places (Id)
);

Classes used for creating the index:

public partial class Photo
{
    [IsSearchable]
    public string PhotoURI { get; set; }

    public List<Details> Details { get; set; }
}
public partial class Place
{
    [System.ComponentModel.DataAnnotations.Key]
    public string Id { get; set; }

    [IsSearchable, IsFilterable, IsSortable, IsFacetable]
    public string Name { get; set; } 

    [IsSearchable, IsFilterable]
    public string Location { get; set; }

    [IsSearchable, IsFilterable, IsSortable]
    public List<Photo> Photos { get; set; }

}

Indexer (with the PlacePhotos table as its data source):

private static void Create_PlacePhotos_Indexer(SearchServiceClient serviceClient)
{
    var indexerDef = new Indexer(
        name: placePhotosIndexer,
        dataSourceName: placePhotosDataSource,
        targetIndexName: index,
        skillsetName: skillset,
        schedule: new IndexingSchedule
        {
            Interval = new TimeSpan(0, 30, 0)
        },
        fieldMappings: new List<FieldMapping>
        {
            new FieldMapping("PlaceId", "Id"),
            new FieldMapping("PhotoURI", "PhotoURI")
        },
        outputFieldMappings: new List<FieldMapping>
        {
            new FieldMapping("/document/Id", "Id"),
            new FieldMapping("/document/PhotoURI", "Photos/*/PhotoURI"),
            new FieldMapping("/document/Details", "Photos/*/Details")
        }
    );
    serviceClient.Indexers.CreateOrUpdate(indexerDef);
}

Upvotes: 3

Views: 643

Answers (1)

8163264128
8163264128

Reputation: 787

Output field mapping target field names must be top-level index field names, so Photos/*/PhotoURI is not valid and you have to target Photos directly.

To "shape" the Photos value to be mapped, you can use the ShaperSkill

{
  "@odata.type": "#Microsoft.Skills.Util.ShaperSkill",
  "context": "/document/photos/*",
  "inputs": [
    {
      "name": "PhotoURI",
      "source": "/document/photos/*/PhotoURI"
    },
    {
      "name": "Details",
      "source": "/document/photos/*/Details"
    }
  ],
  "outputs": [
    {
      "name": "output",
      "targetName": "shapedValue"
    }
  ]
}

and output field mapping

new FieldMapping("/document/photos/*/shapedValue", "Photos")

NOTE - you might have to rearrange your skills that output URI and details, so they share a common context. From your snippet above it looks like each document has only 1 photo, which is different from your index definition.

Upvotes: 3

Related Questions