Modellera komplexa datatyper i Azure AI Search

Externa datauppsättningar som används för att fylla i ett Azure AI Search-index kan komma i många former. Ibland innehåller de hierarkiska eller kapslade understrukturer. Exempel kan vara flera adresser för en enskild kund, flera färger och storlekar för en enda SKU, flera författare till en enda bok och så vidare. I modelleringstermer kan du se dessa strukturer som kallas komplexa, sammansatta, sammansatta eller aggregerade datatyper. Termen Azure AI Search använder för det här konceptet är komplex typ. I Azure AI Search modelleras komplexa typer med hjälp av komplexa fält. Ett komplext fält är ett fält som innehåller underordnade (underfält) som kan vara av valfri datatyp, inklusive andra komplexa typer. Detta fungerar på ett liknande sätt som strukturerade datatyper på ett programmeringsspråk.

Komplexa fält representerar antingen ett enskilt objekt i dokumentet eller en matris med objekt, beroende på datatypen. Fält av typen Edm.ComplexType representerar enskilda objekt, medan fält av typen Collection(Edm.ComplexType) representerar matriser med objekt.

Azure AI Search har inbyggt stöd för komplexa typer och samlingar. Med de här typerna kan du modellera nästan vilken JSON-struktur som helst i ett Azure AI Search-index. I tidigare versioner av Api:er för Azure AI Search kunde endast utplattade raduppsättningar importeras. I den senaste versionen kan ditt index nu närmare motsvara källdata. Med andra ord, om dina källdata har komplexa typer kan ditt index också ha komplexa typer.

För att komma igång rekommenderar vi datauppsättningen Hotell som du kan läsa in i guiden Importera data i Azure-portalen. Guiden identifierar komplexa typer i källan och föreslår ett indexschema baserat på de identifierade strukturerna.

Kommentar

Stöd för komplexa typer blev allmänt tillgängligt från och med api-version=2019-05-06.

Om din söklösning bygger på tidigare lösningar för utplattade datamängder i en samling bör du ändra ditt index så att det innehåller komplexa typer som stöds i den senaste API-versionen. Mer information om hur du uppgraderar API-versioner finns i Uppgradera till den senaste REST API-versionen eller Uppgradera till den senaste .NET SDK-versionen.

Exempel på en komplex struktur

Följande JSON-dokument består av enkla fält och komplexa fält. Komplexa fält, till exempel Address och Rooms, har underfält. Address har en enda uppsättning värden för dessa underfält, eftersom det är ett enda objekt i dokumentet. Däremot Rooms finns det flera uppsättningar med värden för dess underfält, ett för varje objekt i samlingen.

{
  "HotelId": "1",
  "HotelName": "Secret Point Motel",
  "Description": "Ideally located on the main commercial artery of the city in the heart of New York.",
  "Tags": ["Free wifi", "on-site parking", "indoor pool", "continental breakfast"],
  "Address": {
    "StreetAddress": "677 5th Ave",
    "City": "New York",
    "StateProvince": "NY"
  },
  "Rooms": [
    {
      "Description": "Budget Room, 1 Queen Bed (Cityside)",
      "RoomNumber": 1105,
      "BaseRate": 96.99,
    },
    {
      "Description": "Deluxe Room, 2 Double Beds (City View)",
      "Type": "Deluxe Room",
      "BaseRate": 150.99,
    }
    . . .
  ]
}

Indexering av komplexa typer

Under indexeringen kan du ha högst 3 000 element i alla komplexa samlingar i ett enda dokument. Ett element i en komplex samling är medlem i den samlingen, så när det gäller Rum (den enda komplexa samlingen i hotellexemplet) är varje rum ett element. I exemplet ovan, om "Secret Point Motel" hade 500 rum, skulle hotelldokumentet ha 500 rumselement. För kapslade komplexa samlingar räknas även varje kapslat element, förutom det yttre elementet (överordnat).

Den här gränsen gäller endast för komplexa samlingar och inte komplexa typer (till exempel Adress) eller strängsamlingar (till exempel Taggar).

Skapa komplexa fält

Precis som med alla indexdefinitioner kan du använda portalen, REST API eller .NET SDK för att skapa ett schema som innehåller komplexa typer.

Andra Azure SDK:er innehåller exempel i Python, Java och JavaScript.

  1. Logga in på Azure-portalen.

  2. På sidan Översikt för söktjänsten väljer du fliken Index.

  3. Öppna ett befintligt index eller skapa ett nytt index.

  4. Välj fliken Fält och välj sedan Lägg till fält. Ett tomt fält läggs till. Om du arbetar med en befintlig fältsamling rullar du ned för att konfigurera fältet.

  5. Ge fältet ett namn och ange typen till antingen Edm.ComplexType eller Collection(Edm.ComplexType).

  6. Välj ellipserna längst till höger och välj sedan antingen Lägg till fält eller Lägg till underfält och tilldela sedan attribut.

Uppdatera komplexa fält

Alla omindexeringsregler som gäller för fält i allmänhet gäller fortfarande för komplexa fält. Att upprepa några av huvudreglerna här, att lägga till ett fält i en komplex typ kräver inte att index återskapas, men de flesta ändringar gör det.

Strukturella uppdateringar av definitionen

Du kan lägga till nya underfält i ett komplext fält när som helst utan att behöva återskapa ett index. Till exempel är det tillåtet att lägga till "ZipCode" i Address eller "Bekvämligheter" i Rooms , precis som att lägga till ett fält på toppnivå i ett index. Befintliga dokument har ett null-värde för nya fält tills du uttryckligen fyller i fälten genom att uppdatera dina data.

Observera att inom en komplex typ har varje underfält en typ och kan ha attribut, precis som fält på den översta nivån gör

Datauppdateringar

Att uppdatera befintliga dokument i ett index med åtgärden upload fungerar på samma sätt för komplexa och enkla fält: alla fält ersätts. merge Men (eller mergeOrUpload när det tillämpas på ett befintligt dokument) fungerar inte på samma sätt i alla fält. merge Mer specifikt stöder inte sammanslagning av element i en samling. Den här begränsningen finns för samlingar av primitiva typer och komplexa samlingar. Om du vill uppdatera en samling måste du hämta det fullständiga samlingsvärdet, göra ändringar och sedan inkludera den nya samlingen i index-API-begäran.

Sök i komplexa fält

Sökuttryck i fritt format fungerar som förväntat med komplexa typer. Om något sökbart fält eller underfält någonstans i ett dokument matchar är själva dokumentet en matchning.

Frågor blir mer nyanserade när du har flera termer och operatorer, och vissa termer har fältnamn angivna, vilket är möjligt med Lucene-syntaxen. Den här frågan försöker till exempel matcha två termer, "Portland" och "OR", mot två underfält i fältet Adress:

search=Address/City:Portland AND Address/State:OR

Frågor som dessa är oinkorrigerade för fulltextsökning, till skillnad från filter. I filter korreleras frågor över underfält i en komplex samling med hjälp av intervallvariabler i any eller all. Lucene-frågan ovan returnerar dokument som innehåller både "Portland, Maine" och "Portland, Oregon", tillsammans med andra städer i Oregon. Detta beror på att varje sats gäller för alla värden i fältet i hela dokumentet, så det finns inget begrepp om ett "aktuellt underdokument". Mer information om detta finns i Förstå OData-samlingsfilter i Azure AI Search.

Välj komplexa fält

Parametern $select används för att välja vilka fält som ska returneras i sökresultaten. Om du vill använda den här parametern för att välja specifika underfält i ett komplext fält inkluderar du det överordnade fältet och underfältet avgränsat med ett snedstreck (/).

$select=HotelName, Address/City, Rooms/BaseRate

Fält måste markeras som Hämtningsbara i indexet om du vill ha dem i sökresultaten. Endast fält som har markerats som Hämtningsbara kan användas i en $select -instruktion.

Filtrera, fasettera och sortera komplexa fält

Samma OData-sökvägssyntax som används för filtrering och fältsökningar kan också användas för fasettering, sortering och val av fält i en sökbegäran. För komplexa typer gäller regler som styr vilka underfält som kan markeras som sorterbara eller fasettbara. Mer information om dessa regler finns i referensen skapa index-API.

Fasettering av underfält

Alla underfält kan markeras som fasettbara om det inte är av typen Edm.GeographyPoint eller Collection(Edm.GeographyPoint).

De dokumentantal som returneras i fasetteringsresultatet beräknas för det överordnade dokumentet (ett hotell), inte underdokumenten i en komplex samling (rum). Anta till exempel att ett hotell har 20 rum av typen "suite". Med tanke på den här aspektparametern facet=Rooms/Typeär antalet fasetter ett för hotellet, inte 20 för rummen.

Sortera komplexa fält

Sorteringsåtgärder gäller för dokument (hotell) och inte underdokument (rum). När du har en komplex typsamling, till exempel Rum, är det viktigt att inse att du inte kan sortera på Rum alls. Du kan faktiskt inte sortera på någon samling.

Sorteringsåtgärder fungerar när fält har ett enda värde per dokument, oavsett om fältet är ett enkelt fält eller ett underfält i en komplex typ. Till exempel Address/City kan vara sorterbar eftersom det bara finns en adress per hotell, så $orderby=Address/City sorterar hotell efter stad.

Filtrering på komplexa fält

Du kan referera till underfält i ett komplext fält i ett filteruttryck. Använd bara samma OData-sökvägssyntax som används för fasettering, sortering och val av fält. Följande filter returnerar till exempel alla hotell i Kanada:

$filter=Address/Country eq 'Canada'

Om du vill filtrera på ett komplext samlingsfält kan du använda ett lambda-uttryck med operatorernaany och all . I så fall är intervallvariabeln för lambda-uttrycket ett objekt med underfält. Du kan referera till dessa underfält med standardsyntaxen för OData-sökväg. Följande filter returnerar till exempel alla hotell med minst ett deluxe-rum och alla rökfria rum:

$filter=Rooms/any(room: room/Type eq 'Deluxe Room') and Rooms/all(room: not room/SmokingAllowed)

Precis som med enkla fält på den översta nivån kan enkla underfält för komplexa fält endast ingå i filter om de har det filterbara attributet inställt på true i indexdefinitionen. Mer information finns i referensen skapa index-API.

Azure Search har begränsningen att de komplexa objekten i samlingarna i ett enda dokument inte får överstiga 3 000.

Användarna får felet nedan under indexeringen när komplexa samlingar överskrider gränsen på 3 000.

"En samling i dokumentet överskrider gränsen för maximala element för alla komplexa samlingar. Dokumentet med nyckeln "1052" har 4303-objekt i samlingar (JSON-matriser). Högst 3 000 objekt tillåts finnas i samlingar i hela dokumentet. Ta bort objekt från samlingar och försök indexera dokumentet igen."

I vissa användningsfall kan vi behöva lägga till fler än 3 000 objekt i en samling. I dessa användningsfall kan vi skicka (|) eller använda någon form av avgränsare för att avgränsa värdena, sammanfoga dem och lagra dem som en avgränsad sträng. Det finns ingen begränsning för antalet strängar som lagras i en matris i Azure Search. Om du lagrar dessa komplexa värden som strängar undviks begränsningen. Kunden måste kontrollera om den här lösningen uppfyller deras scenariokrav.

Det skulle till exempel inte vara möjligt att använda komplexa typer om matrisen "searchScope" nedan hade fler än 3 000 element.


"searchScope": [
  {
     "countryCode": "FRA",
     "productCode": 1234,
     "categoryCode": "C100" 
  },
  {
     "countryCode": "USA",
     "productCode": 1235,
     "categoryCode": "C200" 
  }
]

Om du lagrar dessa komplexa värden som strängar med en avgränsare undviker du begränsningen

"searchScope": [
        "|FRA|1234|C100|",
        "|FRA|*|*|",
        "|*|1234|*|",
        "|*|*|C100|",
        "|FRA|*|C100|",
        "|*|1234|C100|"
]

I stället för att lagra dessa med jokertecken kan vi också använda en anpassad analysator som delar upp ordet i | för att minska lagringsstorleken.

Anledningen till att vi har lagrat värdena med jokertecken i stället för att bara lagra dem enligt nedan

|FRA|1234|C100|

är att tillgodose sökscenarier där kunden kanske vill söka efter objekt som har land Frankrike, oavsett produkter och kategorier. På samma sätt kan kunden behöva söka för att se om objektet har produkt 1234, oavsett land eller kategori.

Om vi bara hade lagrat en post

|FRA|1234|C100|

utan jokertecken, om användaren bara vill filtrera på Frankrike, kan vi inte konvertera användarindata så att de matchar matrisen "searchScope" eftersom vi inte vet vilken kombination av Frankrike som finns i vår "searchScope"-matris

Om användaren bara vill filtrera efter land ska vi säga Frankrike. Vi tar användarindata och konstruerar den som en sträng enligt nedan:

|FRA|*|*|

som vi sedan kan använda för att filtrera i Azure Search när vi söker i en matris med objektvärden

foreach (var filterItem in filterCombinations)
        {
            var formattedCondition = $"searchScope/any(s: s eq '{filterItem}')";
            combFilter.Append(combFilter.Length > 0 ? " or (" + formattedCondition + ")" : "(" + formattedCondition + ")");
        }

På samma sätt, om användaren söker efter Frankrike och produktkoden 1234, tar vi användarindata, konstruerar den som en avgränsad sträng enligt nedan och matchar den mot vår sökmatris.

|FRA|1234|*|

Om användaren söker efter 1234-produktkod tar vi användarindata, konstruerar den som en avgränsad sträng enligt nedan och matchar den mot vår sökmatris.

|*|1234|*|

Om användaren söker efter kategorikoden C100 tar vi användarindata, konstruerar den som en avgränsad sträng enligt nedan och matchar den mot vår sökmatris.

|*|*|C100|

Om användaren söker efter Frankrike och produktkoden 1234 och kategorikoden C100 tar vi användarindata, skapar den som en avgränsad sträng enligt nedan och matchar den mot vår sökmatris.

|FRA|1234|C100|

Om en användare försöker söka efter länder som inte finns i listan matchar den inte den avgränsade matrisen "searchScope" som lagras i sökindexet, och inga resultat returneras. En användare söker till exempel efter Kanada och produktkoden 1234. Användarsökningen konverteras till

|CAN|1234|*|

Detta matchar inte någon av posterna i den avgränsade matrisen i vårt sökindex.

Endast ovanstående designval kräver denna jokerteckenpost; Om det hade sparats som ett komplext objekt kunde vi helt enkelt ha utfört en explicit sökning enligt nedan.

           var countryFilter = $"searchScope/any(ss: search.in(countryCode ,'FRA'))";
            var catgFilter = $"searchScope/any(ss: search.in(categoryCode ,'C100'))";
            var combinedCountryCategoryFilter = "(" + countryFilter + " and " + catgFilter + ")";

Vi kan därför uppfylla krav där vi behöver söka efter en kombination av värden genom att lagra den som en avgränsad sträng i stället för en komplex samling om våra komplexa samlingar överskrider gränsen för Azure Search. Detta är en av lösningarna och kunden måste verifiera om detta uppfyller deras scenariokrav.

Nästa steg

Prova datauppsättningen Hotell i guiden Importera data. Du behöver anslutningsinformationen för Azure Cosmos DB som anges i readme för att få åtkomst till data.

Med den informationen i handen är ditt första steg i guiden att skapa en ny Azure Cosmos DB-datakälla. Längre fram i guiden visas ett index med komplexa typer när du kommer till målindexsidan. Skapa och läs in det här indexet och kör sedan frågor för att förstå den nya strukturen.