Indexering av data med hjälp av Push-API:et för Azure AI Search
REST-API:et är det mest flexibla sättet att skicka data till ett Azure AI Search-index. Du kan använda valfritt programmeringsspråk eller interaktivt med alla appar som kan skicka JSON-begäranden till en slutpunkt.
Här får du se hur du använder REST-API:et effektivt och utforskar de tillgängliga åtgärderna. Sedan tittar du på .NET Core-kod och ser hur du optimerar tillägg av stora mängder data via API:et.
REST API-åtgärder som stöds
Det finns två REST API:er som stöds av AI Search. API:er för sökning och hantering. Den här modulen fokuserar på rest-API:er för sökning som tillhandahåller åtgärder för fem sökfunktioner:
Funktion | Operations |
---|---|
Index | Skapa, ta bort, uppdatera och konfigurera. |
Dokument | Hämta, lägga till, uppdatera och ta bort. |
Indexerare | Konfigurera datakällor och schemaläggning på begränsade datakällor. |
Skillset | Hämta, skapa, ta bort, lista och uppdatera. |
Synonymkarta | Hämta, skapa, ta bort, lista och uppdatera. |
Anropa rest-API:et för sökning
Om du vill anropa någon av sök-API:erna måste du:
- Använd HTTPS-slutpunkten (via standardporten 443) som tillhandahålls av söktjänsten. Du måste inkludera en API-version i URI:n.
- Begärandehuvudet måste innehålla ett api-nyckelattribut .
Om du vill hitta slutpunkten, api-versionen och api-key går du till Azure-portalen.
I portalen navigerar du till söktjänsten och väljer sedan Sökutforskaren. REST API-slutpunkten finns i fältet Begärande-URL . Den första delen av URL:en är slutpunkten (till exempel https://azsearchtest.search.windows.net) och frågesträngen api-version
visar (till exempel api-version=2023-07-01-Preview).
Om du vill hitta till api-key
vänster väljer du Nycklar. Den primära eller sekundära administratörsnyckeln kan användas om du använder REST-API:et för att göra mer än att bara köra frågor mot indexet. Om allt du behöver är att söka i ett index kan du skapa och använda frågenycklar.
Om du vill lägga till, uppdatera eller ta bort data i ett index måste du använda en administratörsnyckel.
Lägga till data i ett index
Använd en HTTP POST-begäran med hjälp av indexfunktionen i det här formatet:
POST https://[service name].search.windows.net/indexes/[index name]/docs/index?api-version=[api-version]
Brödtexten i din begäran måste låta REST-slutpunkten veta vilken åtgärd som ska vidtas för dokumentet, vilket dokument som ska tillämpas på åtgärden också och vilka data som ska användas.
JSON måste ha följande format:
{
"value": [
{
"@search.action": "upload (default) | merge | mergeOrUpload | delete",
"key_field_name": "unique_key_of_document", (key/value pair for key field from index schema)
"field_name": field_value (key/value pairs matching index schema)
...
},
...
]
}
Åtgärd | beskrivning |
---|---|
ladda upp | På samma sätt som en upsert i SQL skapas eller ersätts dokumentet. |
Sammanfoga | Sammanslagning uppdaterar ett befintligt dokument med de angivna fälten. Sammanfogning misslyckas om inget dokument kan hittas. |
mergeOrUpload | Sammanslagning uppdaterar ett befintligt dokument med de angivna fälten och laddar upp det om dokumentet inte finns. |
ta bort | Tar bort hela dokumentet. Du behöver bara ange key_field_name. |
Om din begäran lyckas returnerar API:et en 200-statuskod.
Kommentar
En fullständig lista över alla svarskoder och felmeddelanden finns i Lägga till, uppdatera eller ta bort dokument (AZURE AI Search REST API)
I det här exemplet laddar JSON upp kundposten i föregående enhet:
{
"value": [
{
"@search.action": "upload",
"id": "5fed1b38309495de1bc4f653",
"firstName": "Sims",
"lastName": "Arnold",
"isAlive": false,
"age": 35,
"address": {
"streetAddress": "Sumner Place",
"city": "Canoochee",
"state": "Palau",
"postalCode": "1558"
},
"phoneNumbers": [
{
"phoneNumber": {
"type": "home",
"number": "+1 (830) 465-2965"
}
},
{
"phoneNumber": {
"type": "home",
"number": "+1 (889) 439-3632"
}
}
]
}
]
}
Du kan lägga till så många dokument i värdematrisen som du vill. För optimala prestanda bör du dock överväga att batcha dokumenten i dina begäranden upp till högst 1 000 dokument eller 16 MB i total storlek.
Använda .NET Core för att indexering av data
För bästa prestanda använder du det senaste Azure.Search.Document
klientbiblioteket, för närvarande version 11. Du kan installera klientbiblioteket med NuGet:
dotnet add package Azure.Search.Documents --version 11.4.0
Hur ditt index presterar baseras på sex nyckelfaktorer:
- Söktjänstnivån och hur många repliker och partitioner du har aktiverat.
- Indexschemats komplexitet. Minska hur många egenskaper (sökbara, fasettbara, sorterbara) varje fält har.
- Antalet dokument i varje batch, den bästa storleken beror på indexschemat och dokumentstorleken.
- Hur mångtrådad din metod är.
- Hantera fel och begränsning. Använd en återförsöksstrategi för exponentiell backoff.
- Där dina data finns kan du försöka indexera dina data så nära ditt sökindex. Kör till exempel uppladdningar inifrån Azure-miljön.
Räkna ut din optimala batchstorlek
Eftersom det är viktigt att ta reda på den bästa batchstorleken för att förbättra prestandan ska vi titta på en metod i kod.
public static async Task TestBatchSizesAsync(SearchClient searchClient, int min = 100, int max = 1000, int step = 100, int numTries = 3)
{
DataGenerator dg = new DataGenerator();
Console.WriteLine("Batch Size \t Size in MB \t MB / Doc \t Time (ms) \t MB / Second");
for (int numDocs = min; numDocs <= max; numDocs += step)
{
List<TimeSpan> durations = new List<TimeSpan>();
double sizeInMb = 0.0;
for (int x = 0; x < numTries; x++)
{
List<Hotel> hotels = dg.GetHotels(numDocs, "large");
DateTime startTime = DateTime.Now;
await UploadDocumentsAsync(searchClient, hotels).ConfigureAwait(false);
DateTime endTime = DateTime.Now;
durations.Add(endTime - startTime);
sizeInMb = EstimateObjectSize(hotels);
}
var avgDuration = durations.Average(timeSpan => timeSpan.TotalMilliseconds);
var avgDurationInSeconds = avgDuration / 1000;
var mbPerSecond = sizeInMb / avgDurationInSeconds;
Console.WriteLine("{0} \t\t {1} \t\t {2} \t\t {3} \t {4}", numDocs, Math.Round(sizeInMb, 3), Math.Round(sizeInMb / numDocs, 3), Math.Round(avgDuration, 3), Math.Round(mbPerSecond, 3));
// Pausing 2 seconds to let the search service catch its breath
Thread.Sleep(2000);
}
Console.WriteLine();
}
Metoden är att öka batchstorleken och övervaka den tid det tar att ta emot ett giltigt svar. Koden loopar från 100 till 1 000 i 100 dokumentsteg. För varje batchstorlek matar den ut dokumentstorleken, tiden för att få ett svar och den genomsnittliga tiden per MB. När du kör den här koden får du resultat som det här:
I exemplet ovan är den bästa batchstorleken för dataflöde 2,499 MB per sekund, 800 dokument per batch.
Implementera en återförsöksstrategi för exponentiell backoff
Om ditt index börjar begränsa begäranden på grund av överbelastningar svarar det med statusen 503 (begäran avvisas på grund av hög belastning) eller 207 (vissa dokument misslyckades i batchen). Du måste hantera dessa svar och en bra strategi är att backoff. Att säkerhetskopiera innebär att pausa en stund innan du försöker igen. Om du ökar den här tiden för varje fel säkerhetskopieras du exponentiellt.
Titta på den här koden:
// Implement exponential backoff
do
{
try
{
attempts++;
result = await searchClient.IndexDocumentsAsync(batch).ConfigureAwait(false);
var failedDocuments = result.Results.Where(r => r.Succeeded != true).ToList();
// handle partial failure
if (failedDocuments.Count > 0)
{
if (attempts == maxRetryAttempts)
{
Console.WriteLine("[MAX RETRIES HIT] - Giving up on the batch starting at {0}", id);
break;
}
else
{
Console.WriteLine("[Batch starting at doc {0} had partial failure]", id);
Console.WriteLine("[Retrying {0} failed documents] \n", failedDocuments.Count);
// creating a batch of failed documents to retry
var failedDocumentKeys = failedDocuments.Select(doc => doc.Key).ToList();
hotels = hotels.Where(h => failedDocumentKeys.Contains(h.HotelId)).ToList();
batch = IndexDocumentsBatch.Upload(hotels);
Task.Delay(delay).Wait();
delay = delay * 2;
continue;
}
}
return result;
}
catch (RequestFailedException ex)
{
Console.WriteLine("[Batch starting at doc {0} failed]", id);
Console.WriteLine("[Retrying entire batch] \n");
if (attempts == maxRetryAttempts)
{
Console.WriteLine("[MAX RETRIES HIT] - Giving up on the batch starting at {0}", id);
break;
}
Task.Delay(delay).Wait();
delay = delay * 2;
}
} while (true);
Koden håller reda på misslyckade dokument i en batch. Om ett fel inträffar väntar det på en fördröjning och fördubblar sedan fördröjningen för nästa fel.
Slutligen finns det ett maximalt antal återförsök och om det maximala antalet har nåtts finns programmet.
Använda trådning för att förbättra prestanda
Du kan slutföra din dokumentuppladdningsapp genom att kombinera ovanstående backoff-strategi med en trådningsmetod. Här är några exempelkod:
public static async Task IndexDataAsync(SearchClient searchClient, List<Hotel> hotels, int batchSize, int numThreads)
{
int numDocs = hotels.Count;
Console.WriteLine("Uploading {0} documents...\n", numDocs.ToString());
DateTime startTime = DateTime.Now;
Console.WriteLine("Started at: {0} \n", startTime);
Console.WriteLine("Creating {0} threads...\n", numThreads);
// Creating a list to hold active tasks
List<Task<IndexDocumentsResult>> uploadTasks = new List<Task<IndexDocumentsResult>>();
for (int i = 0; i < numDocs; i += batchSize)
{
List<Hotel> hotelBatch = hotels.GetRange(i, batchSize);
var task = ExponentialBackoffAsync(searchClient, hotelBatch, i);
uploadTasks.Add(task);
Console.WriteLine("Sending a batch of {0} docs starting with doc {1}...\n", batchSize, i);
// Checking if we've hit the specified number of threads
if (uploadTasks.Count >= numThreads)
{
Task<IndexDocumentsResult> firstTaskFinished = await Task.WhenAny(uploadTasks);
Console.WriteLine("Finished a thread, kicking off another...");
uploadTasks.Remove(firstTaskFinished);
}
}
// waiting for the remaining results to finish
await Task.WhenAll(uploadTasks);
DateTime endTime = DateTime.Now;
TimeSpan runningTime = endTime - startTime;
Console.WriteLine("\nEnded at: {0} \n", endTime);
Console.WriteLine("Upload time total: {0}", runningTime);
double timePerBatch = Math.Round(runningTime.TotalMilliseconds / (numDocs / batchSize), 4);
Console.WriteLine("Upload time per batch: {0} ms", timePerBatch);
double timePerDoc = Math.Round(runningTime.TotalMilliseconds / numDocs, 4);
Console.WriteLine("Upload time per document: {0} ms \n", timePerDoc);
}
Den här koden använder asynkrona anrop till en funktion ExponentialBackoffAsync
som implementerar backoff-strategin. Du anropar funktionen med hjälp av trådar, till exempel antalet kärnor som processorn har. När det maximala antalet trådar har använts väntar koden på att alla trådar ska slutföras. Sedan skapas en ny tråd tills alla dokument har laddats upp.