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 enskild produkt, flera författare av 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 Portal. 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": "Stay-Kay City Hotel",
"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,
}
. . .
]
}
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.
Logga in på Azure-portalen.
På sidan Översikt för söktjänsten väljer du fliken Index.
Öppna ett befintligt index eller skapa ett nytt index.
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.
Ge fältet ett namn och ange typen till antingen
Edm.ComplexType
ellerCollection(Edm.ComplexType)
.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.
Komplexa insamlingsgränser
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. För Rum (den enda komplexa samlingen i hotellexemplet) är varje rum ett element. I exemplet ovan, om "Stay-Kay City Hotel" 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).
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 lägga till ett nytt fält i en komplex typ kräver inte att indexet återskapas, men de flesta andra ändringar kräver återskapande.
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öka i komplexa fält i textfrågor
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.
Söka i komplexa fält i RAG-frågor
Ett RAG-mönster skickar sökresultat till en chattmodell för generativ AI och konversationssökning. Som standard är sökresultat som skickas till en LLM en utplattad raduppsättning. Men om ditt index har komplexa typer kan frågan ange dessa fält om du först konverterar sökresultaten till JSON och sedan skickar JSON till LLM.
Ett partiellt exempel illustrerar tekniken:
- Ange de fält som du vill ha i prompten eller i frågan
- Kontrollera att fälten är sökbara och kan hämtas i indexet
- Välj fälten för sökresultaten
- Formatera resultatet som JSON
- Skicka begäran om att chatten ska slutföras till modellprovidern
import json
# Query is the question being asked. It's sent to the search engine and the LLM.
query="Can you recommend a few hotels that offer complimentary breakfast? Tell me their description, address, tags, and the rate for one room they have which sleep 4 people."
# Set up the search results and the chat thread.
# Retrieve the selected fields from the search index related to the question.
selected_fields = ["HotelName","Description","Address","Rooms","Tags"]
search_results = search_client.search(
search_text=query,
top=5,
select=selected_fields,
query_type="semantic"
)
sources_filtered = [{field: result[field] for field in selected_fields} for result in search_results]
sources_formatted = "\n".join([json.dumps(source) for source in sources_filtered])
response = openai_client.chat.completions.create(
messages=[
{
"role": "user",
"content": GROUNDED_PROMPT.format(query=query, sources=sources_formatted)
}
],
model=AZURE_DEPLOYMENT_MODEL
)
print(response.choices[0].message.content)
I exemplet från slutpunkt till slutpunkt finns snabbstart: Generativ sökning (RAG) med grunddata från 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.
Lösning för den komplexa insamlingsgränsen
Kom ihåg att Azure AI Search begränsar komplexa objekt i en samling till 3 000 objekt per dokument. Om du överskrider den här gränsen visas följande meddelande:
A collection in your document exceeds the maximum elements across all complex collections limit.
The document with key '1052' has '4303' objects in collections (JSON arrays).
At most '3000' objects are allowed to be in collections across the entire document.
Remove objects from collections and try indexing the document again."
Om du behöver fler än 3 000 objekt kan du 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. Om du lagrar komplexa värden som strängar kringgås den komplexa insamlingsbegränsningen.
Anta att du har en "searchScope
matris med fler än 3 000 element för att illustrera:
"searchScope": [
{
"countryCode": "FRA",
"productCode": 1234,
"categoryCode": "C100"
},
{
"countryCode": "USA",
"productCode": 1235,
"categoryCode": "C200"
}
. . .
]
Lösningen för att lagra värdena som en avgränsad sträng kan se ut så här:
"searchScope": [
"|FRA|1234|C100|",
"|FRA|*|*|",
"|*|1234|*|",
"|*|*|C100|",
"|FRA|*|C100|",
"|*|1234|C100|"
]
Att lagra alla sökvarianter i den avgränsade strängen är användbart i sökscenarier där du vill söka efter objekt som bara har "FRA" eller "1234" eller någon annan kombination i matrisen.
Här är ett filterformateringsfragment i C# som konverterar indata till sökbara strängar:
foreach (var filterItem in filterCombinations)
{
var formattedCondition = $"searchScope/any(s: s eq '{filterItem}')";
combFilter.Append(combFilter.Length > 0 ? " or (" + formattedCondition + ")" : "(" + formattedCondition + ")");
}
Följande lista innehåller indata och söksträngar (utdata) sida vid sida:
För "FRA"-länskoden och produktkoden "1234" är
|FRA|1234|*|
de formaterade utdata .För produktkoden "1234" är
|*|1234|*|
de formaterade utdata .För kategorikoden "C100" är
|*|*|C100|
de formaterade utdata .
Ange endast jokertecknet (*
) om du implementerar en lösning för strängmatrisen. Om du använder en komplex typ kan filtret se ut så här:
var countryFilter = $"searchScope/any(ss: search.in(countryCode ,'FRA'))";
var catgFilter = $"searchScope/any(ss: search.in(categoryCode ,'C100'))";
var combinedCountryCategoryFilter = "(" + countryFilter + " and " + catgFilter + ")";
Om du implementerar lösningen måste du testa omfångsrikt.
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.