GitHub Copilot
ChatGPT
Claude
Codex CLI
Cursor
opencode
Skill
Text
azure-search-documents-ts
Build search applications using Azure AI Search SDK for JavaScript (@azure/search-documents). Use when creating/managing indexes, implementing vector/hybrid search, semantic ranking, or building agentic retrieval with knowledge bases.
Virus-scanned
Reviewed automatically before listing.
Download
microsoft-skills-.github_plugins_azure-sdk-typescript_skills_azure-search-documents-ts-e58528d.zip · 8 KB
Install
skills CLI
npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/azure-search-documents-ts
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git
git clone https://github.com/microsoft/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Azure AI Search SDK for TypeScript
Build search applications with vector, hybrid, and semantic search capabilities.
Installation
npm install @azure/search-documents @azure/identity
Environment Variables
AZURE_SEARCH_ENDPOINT=https://<service-name>.search.windows.net
AZURE_SEARCH_INDEX_NAME=my-index
AZURE_SEARCH_ADMIN_KEY=<admin-key> # Optional if using Entra ID
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
Authentication
import { SearchClient, SearchIndexClient } from "@azure/search-documents";
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
const endpoint = process.env.AZURE_SEARCH_ENDPOINT!;
const indexName = process.env.AZURE_SEARCH_INDEX_NAME!;
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();
// For searching
const searchClient = new SearchClient(endpoint, indexName, credential);
// For index management
const indexClient = new SearchIndexClient(endpoint, credential);
Core Workflow
Create Index with Vector Field
import { SearchIndex, SearchField, VectorSearch } from "@azure/search-documents";
const index: SearchIndex = {
name: "products",
fields: [
{ name: "id", type: "Edm.String", key: true },
{ name: "title", type: "Edm.String", searchable: true },
{ name: "description", type: "Edm.String", searchable: true },
{ name: "category", type: "Edm.String", filterable: true, facetable: true },
{
name: "embedding",
type: "Collection(Edm.Single)",
searchable: true,
vectorSearchDimensions: 1536,
vectorSearchProfileName: "vector-profile",
},
],
vectorSearch: {
algorithms: [
{ name: "hnsw-algorithm", kind: "hnsw" },
],
profiles: [
{ name: "vector-profile", algorithmConfigurationName: "hnsw-algorithm" },
],
},
};
await indexClient.createOrUpdateIndex(index);
Index Documents
const documents = [
{ id: "1", title: "Widget", description: "A useful widget", category: "Tools", embedding: [...] },
{ id: "2", title: "Gadget", description: "A cool gadget", category: "Electronics", embedding: [...] },
];
const result = await searchClient.uploadDocuments(documents);
console.log(`Indexed ${result.results.length} documents`);
Full-Text Search
const results = await searchClient.search("widget", {
select: ["id", "title", "description"],
filter: "category eq 'Tools'",
orderBy: ["title asc"],
top: 10,
});
for await (const result of results.results) {
console.log(`${result.document.title}: ${result.score}`);
}
Vector Search
const queryVector = await getEmbedding("useful tool"); // Your embedding function
const results = await searchClient.search("*", {
vectorSearchOptions: {
queries: [
{
kind: "vector",
vector: queryVector,
fields: ["embedding"],
kNearestNeighborsCount: 10,
},
],
},
select: ["id", "title", "description"],
});
for await (const result of results.results) {
console.log(`${result.document.title}: ${result.score}`);
}
Hybrid Search (Text + Vector)
const queryVector = await getEmbedding("useful tool");
const results = await searchClient.search("tool", {
vectorSearchOptions: {
queries: [
{
kind: "vector",
vector: queryVector,
fields: ["embedding"],
kNearestNeighborsCount: 50,
},
],
},
select: ["id", "title", "description"],
top: 10,
});
Semantic Search
// Index must have semantic configuration
const index: SearchIndex = {
name: "products",
fields: [...],
semanticSearch: {
configurations: [
{
name: "semantic-config",
prioritizedFields: {
titleField: { name: "title" },
contentFields: [{ name: "description" }],
},
},
],
},
};
// Search with semantic ranking
const results = await searchClient.search("best tool for the job", {
queryType: "semantic",
semanticSearchOptions: {
configurationName: "semantic-config",
captions: { captionType: "extractive" },
answers: { answerType: "extractive", count: 3 },
},
select: ["id", "title", "description"],
});
for await (const result of results.results) {
console.log(`${result.document.title}`);
console.log(` Caption: ${result.captions?.[0]?.text}`);
console.log(` Reranker Score: ${result.rerankerScore}`);
}
Filtering and Facets
// Filter syntax
const results = await searchClient.search("*", {
filter: "category eq 'Electronics' and price lt 100",
facets: ["category,count:10", "brand"],
});
// Access facets
for (const [facetName, facetResults] of Object.entries(results.facets || {})) {
console.log(`${facetName}:`);
for (const facet of facetResults) {
console.log(` ${facet.value}: ${facet.count}`);
}
}
Autocomplete and Suggestions
// Create suggester in index
const index: SearchIndex = {
name: "products",
fields: [...],
suggesters: [
{ name: "sg", sourceFields: ["title", "description"] },
],
};
// Autocomplete
const autocomplete = await searchClient.autocomplete("wid", "sg", {
mode: "twoTerms",
top: 5,
});
// Suggestions
const suggestions = await searchClient.suggest("wid", "sg", {
select: ["title"],
top: 5,
});
Batch Operations
// Batch upload, merge, delete
const batch = [
{ upload: { id: "1", title: "New Item" } },
{ merge: { id: "2", title: "Updated Title" } },
{ delete: { id: "3" } },
];
const result = await searchClient.indexDocuments({ actions: batch });
Key Types
import {
SearchClient,
SearchIndexClient,
SearchIndexerClient,
SearchIndex,
SearchField,
SearchOptions,
VectorSearch,
SemanticSearch,
SearchIterator,
} from "@azure/search-documents";
Best Practices
- Use hybrid search - Combine vector + text for best results
- Enable semantic ranking - Improves relevance for natural language queries
- Batch document uploads - Use
uploadDocumentswith arrays, not single docs - Use filters for security - Implement document-level security with filters
- Index incrementally - Use
mergeOrUploadDocumentsfor updates - Monitor query performance - Use
includeTotalCount: truesparingly in production
Files (skills)
-
references
-
semantic-ranking.md 11.1 KB
# @azure/search-documents - Semantic Ranking Patterns Reference documentation for semantic search and ranking in the Azure AI Search TypeScript SDK. **Source**: [Azure SDK for JS - search-documents](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/search/search-documents) --- ## Installation ```bash npm install @azure/search-documents @azure/identity ``` --- ## SemanticSearchOptions Interface ```typescript interface SemanticSearchOptions { /** Name of the semantic configuration to use */ configurationName?: string; /** Error handling mode: "partial" or "fail" */ errorMode?: SemanticErrorMode; /** Max wait time for semantic processing (ms) */ maxWaitInMilliseconds?: number; /** Extract answers from documents */ answers?: QueryAnswer; /** Extract captions with highlighting */ captions?: QueryCaption; /** AI-generated query rewrites */ queryRewrites?: QueryRewrites; /** Separate query for semantic reranking */ semanticQuery?: string; /** Fields for semantic search */ semanticFields?: string[]; /** Enable debug info */ debugMode?: QueryDebugMode; } ``` --- ## Basic Semantic Search Enable semantic ranking with `queryType: "semantic"`: ```typescript import { SearchClient } from "@azure/search-documents"; import { DefaultAzureCredential } from "@azure/identity"; interface Article { id: string; title: string; content: string; category: string; } const client = new SearchClient<Article>( process.env["AZURE_SEARCH_ENDPOINT"]!, "articles", new DefaultAzureCredential() ); const results = await client.search("what causes climate change", { queryType: "semantic", semanticSearchOptions: { configurationName: "my-semantic-config", }, select: ["id", "title", "content"], top: 10, }); for await (const result of results.results) { console.log(`${result.document.title}`); console.log(`Reranker Score: ${result.rerankerScore}`); } ``` --- ## Extractive Captions Extract representative passages with keyword highlighting: ```typescript const results = await client.search("renewable energy benefits", { queryType: "semantic", semanticSearchOptions: { configurationName: "my-semantic-config", captions: { captionType: "extractive", highlight: true, }, }, }); for await (const result of results.results) { console.log(`Title: ${result.document.title}`); // Access captions result.captions?.forEach((caption) => { console.log(`Caption: ${caption.text}`); console.log(`Highlighted: ${caption.highlights}`); // With <em> tags }); } ``` ### Caption Options ```typescript interface QueryCaption { /** Caption type: "extractive" | "none" */ captionType: QueryCaptionType; /** Include highlighting with <em> tags */ highlight?: boolean; } ``` --- ## Extractive Answers Extract direct answers for Q&A-style queries: ```typescript const results = await client.search("what is the speed of light", { queryType: "semantic", semanticSearchOptions: { configurationName: "science-config", answers: { answerType: "extractive", count: 3, // Number of answers to extract threshold: 0.7, // Minimum confidence threshold }, }, }); // Access answers at the result set level if (results.answers && results.answers.length > 0) { console.log("Top Answers:"); results.answers.forEach((answer, i) => { console.log(`${i + 1}. ${answer.text}`); console.log(` Highlights: ${answer.highlights}`); console.log(` Score: ${answer.score}`); console.log(` Document: ${answer.key}`); }); } ``` ### Answer Options ```typescript interface QueryAnswer { /** Answer type: "extractive" | "none" */ answerType: QueryAnswerType; /** Number of answers to return (1-10) */ count?: number; /** Minimum confidence threshold (0-1) */ threshold?: number; } ``` --- ## Query Rewrites Enable AI-generated query rewrites for better recall: ```typescript const results = await client.search("ML algorithms", { queryType: "semantic", semanticSearchOptions: { configurationName: "tech-config", queryRewrites: { rewritesType: "generative", count: 3, // Number of rewrites to generate }, }, }); // Access generated rewrites if (results.queryRewrites && results.queryRewrites.length > 0) { console.log("Query Rewrites:"); results.queryRewrites.forEach((rewrite) => { console.log(`- ${rewrite}`); }); } ``` --- ## Semantic + Hybrid Search Combine semantic ranking with vector search for best results: ```typescript const results = await client.search("climate change solutions", { // Enable semantic reranking queryType: "semantic", semanticSearchOptions: { configurationName: "articles-semantic", captions: { captionType: "extractive", highlight: true }, answers: { answerType: "extractive", count: 3 }, }, // Add vector search vectorSearchOptions: { queries: [ { kind: "text", text: "climate change solutions", kNearestNeighborsCount: 50, fields: ["contentVector"], }, ], }, // Keyword search fields searchFields: ["title", "content"], top: 10, select: ["id", "title", "content", "category"], }); // Results are: // 1. Retrieved via hybrid (keyword + vector) // 2. Reranked by semantic model // 3. Enriched with captions and answers ``` --- ## Semantic Query Override Use different queries for retrieval vs. semantic reranking: ```typescript const results = await client.search("ML", { // "ML" used for keyword matching queryType: "semantic", semanticSearchOptions: { configurationName: "tech-config", // Full question used for semantic reranking semanticQuery: "What are the best machine learning algorithms for classification?", captions: { captionType: "extractive" }, }, }); ``` --- ## Debug Mode Get detailed information about semantic processing: ```typescript const results = await client.search("quantum computing applications", { queryType: "semantic", semanticSearchOptions: { configurationName: "science-config", debugMode: "semantic", // "disabled" | "speller" | "semantic" | "all" }, }); for await (const result of results.results) { // Access debug info per document console.log(`Document: ${result.document.title}`); console.log(`Debug Info:`, result.documentDebugInfo); } ``` --- ## Error Handling Control behavior when semantic processing fails: ```typescript const results = await client.search("complex query here", { queryType: "semantic", semanticSearchOptions: { configurationName: "my-config", // "partial" - Return results without semantic enrichment on failure // "fail" - Return error if semantic processing fails errorMode: "partial", // Timeout for semantic processing maxWaitInMilliseconds: 5000, }, }); ``` --- ## Index Semantic Configuration Define semantic configuration in your index: ```typescript import { SearchIndexClient, SearchIndex } from "@azure/search-documents"; const indexClient = new SearchIndexClient(endpoint, credential); const index: SearchIndex = { name: "articles", fields: [ { name: "id", type: "Edm.String", key: true }, { name: "title", type: "Edm.String", searchable: true }, { name: "content", type: "Edm.String", searchable: true }, { name: "summary", type: "Edm.String", searchable: true }, { name: "category", type: "Edm.String", filterable: true }, ], semanticSearch: { configurations: [ { name: "articles-semantic", prioritizedFields: { // Title field (highest priority for reranking) titleField: { fieldName: "title", }, // Content fields (used for caption/answer extraction) contentFields: [ { fieldName: "content" }, { fieldName: "summary" }, ], // Keyword fields (boost exact matches) keywordsFields: [ { fieldName: "category" }, ], }, }, ], defaultConfiguration: "articles-semantic", }, }; await indexClient.createOrUpdateIndex(index); ``` --- ## Complete Example ```typescript import { SearchClient } from "@azure/search-documents"; import { DefaultAzureCredential } from "@azure/identity"; interface Document { id: string; title: string; content: string; contentVector: number[]; category: string; } async function semanticHybridSearch(query: string) { const client = new SearchClient<Document>( process.env["AZURE_SEARCH_ENDPOINT"]!, "knowledge-base", new DefaultAzureCredential() ); const results = await client.search(query, { queryType: "semantic", semanticSearchOptions: { configurationName: "default-semantic", captions: { captionType: "extractive", highlight: true }, answers: { answerType: "extractive", count: 3, threshold: 0.7 }, }, vectorSearchOptions: { queries: [ { kind: "text", text: query, kNearestNeighborsCount: 50, fields: ["contentVector"], }, ], }, searchFields: ["title", "content"], top: 10, select: ["id", "title", "content", "category"], }); // Process answers first (highest quality extracts) const response: { answers: Array<{ text: string; score: number; documentId: string }>; results: Array<{ document: Document; score: number; rerankerScore: number; captions: string[]; }>; } = { answers: [], results: [], }; // Extract answers if (results.answers) { response.answers = results.answers.map((a) => ({ text: a.highlights || a.text || "", score: a.score ?? 0, documentId: a.key || "", })); } // Extract results with captions for await (const result of results.results) { response.results.push({ document: result.document, score: result.score ?? 0, rerankerScore: result.rerankerScore ?? 0, captions: result.captions?.map((c) => c.highlights || c.text || "") ?? [], }); } return response; } // Usage const response = await semanticHybridSearch("how does photosynthesis work"); console.log("=== Direct Answers ==="); response.answers.forEach((a, i) => { console.log(`${i + 1}. ${a.text} (score: ${a.score})`); }); console.log("\n=== Search Results ==="); response.results.forEach((r, i) => { console.log(`${i + 1}. ${r.document.title}`); console.log(` Reranker Score: ${r.rerankerScore}`); console.log(` Caption: ${r.captions[0] || "N/A"}`); }); ``` --- ## Best Practices 1. **Use extractive answers** - For Q&A scenarios, answers provide direct responses 2. **Enable captions** - They provide relevant snippets without returning full content 3. **Combine with hybrid search** - Semantic + vector + keyword yields best results 4. **Set appropriate thresholds** - Filter low-confidence answers 5. **Configure semantic fields** - Prioritize title > content > keywords 6. **Use partial error mode** - Gracefully degrade when semantic processing fails 7. **Monitor latency** - Semantic processing adds ~100-300ms latency --- ## See Also - [Vector Search Patterns](./vector-search.md) - Combine with vector search - [Official Documentation](https://learn.microsoft.com/azure/search/semantic-search-overview) -
vector-search.md 9.8 KB
# @azure/search-documents - Vector Search Patterns Reference documentation for vector search in the Azure AI Search TypeScript SDK. **Source**: [Azure SDK for JS - search-documents](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/search/search-documents) --- ## Installation ```bash npm install @azure/search-documents @azure/identity ``` --- ## Client Setup ```typescript import { SearchClient, SearchIndexClient } from "@azure/search-documents"; import { DefaultAzureCredential } from "@azure/identity"; const credential = new DefaultAzureCredential(); const endpoint = process.env["AZURE_SEARCH_ENDPOINT"]!; // For searching const searchClient = new SearchClient<MyDocument>( endpoint, "my-index", credential ); // For index management const indexClient = new SearchIndexClient(endpoint, credential); ``` --- ## VectorSearchOptions Interface ```typescript interface VectorSearchOptions { /** Vector queries to execute */ queries?: VectorQuery[]; /** Filter mode for vector queries */ filterMode?: VectorFilterMode; // "preFilter" | "postFilter" } type VectorQuery = | VectorizedQuery // Pre-computed vector | VectorizableTextQuery // Text to be vectorized | VectorizableImageUrlQuery | VectorizableImageBinaryQuery; ``` --- ## Basic Vector Search Search using a pre-computed embedding vector: ```typescript import { SearchClient } from "@azure/search-documents"; interface Product { id: string; name: string; description: string; descriptionVector: number[]; } const searchClient = new SearchClient<Product>(endpoint, "products", credential); // Your embedding (e.g., from OpenAI, Azure OpenAI, etc.) const queryVector = await getEmbedding("comfortable running shoes"); const results = await searchClient.search("*", { vectorSearchOptions: { queries: [ { kind: "vector", vector: queryVector, kNearestNeighborsCount: 10, fields: ["descriptionVector"], }, ], }, select: ["id", "name", "description"], }); for await (const result of results.results) { console.log(`${result.document.name} (score: ${result.score})`); } ``` --- ## Integrated Vectorization (Text) Let Azure AI Search vectorize the query text automatically: ```typescript const results = await searchClient.search("*", { vectorSearchOptions: { queries: [ { kind: "text", text: "comfortable running shoes", kNearestNeighborsCount: 10, fields: ["descriptionVector"], }, ], }, }); ``` > **Note**: Requires a vectorizer configured on the index. --- ## Hybrid Search (Text + Vector) Combine keyword search with vector search for better results: ```typescript const results = await searchClient.search("running shoes", { // Keyword search component searchFields: ["name", "description"], // Vector search component vectorSearchOptions: { queries: [ { kind: "text", text: "comfortable running shoes", kNearestNeighborsCount: 50, fields: ["descriptionVector"], }, ], }, // Return top 10 after RRF fusion top: 10, select: ["id", "name", "description"], }); ``` --- ## Multi-Vector Search Search across multiple vector fields simultaneously: ```typescript const results = await searchClient.search("*", { vectorSearchOptions: { queries: [ // Search title embeddings { kind: "text", text: "machine learning", kNearestNeighborsCount: 50, fields: ["titleVector"], weight: 2.0, // Boost title matches }, // Search content embeddings { kind: "text", text: "machine learning", kNearestNeighborsCount: 50, fields: ["contentVector"], weight: 1.0, }, ], }, top: 10, }); ``` --- ## Vector Filtering ### Pre-Filter (Default) Apply filters before vector search - smaller search space, faster: ```typescript const results = await searchClient.search("*", { filter: "category eq 'Electronics' and price lt 500", vectorSearchOptions: { filterMode: "preFilter", // Default queries: [ { kind: "text", text: "wireless headphones", kNearestNeighborsCount: 10, fields: ["descriptionVector"], }, ], }, }); ``` ### Post-Filter Apply filters after vector search - ensures k results are found first: ```typescript const results = await searchClient.search("*", { filter: "category eq 'Electronics'", vectorSearchOptions: { filterMode: "postFilter", queries: [ { kind: "text", text: "wireless headphones", kNearestNeighborsCount: 50, // Get more, then filter fields: ["descriptionVector"], }, ], }, top: 10, }); ``` ### Per-Query Filter Override Apply different filters to different vector queries: ```typescript const results = await searchClient.search("*", { filter: "inStock eq true", // Global filter vectorSearchOptions: { queries: [ { kind: "text", text: "premium headphones", kNearestNeighborsCount: 20, fields: ["descriptionVector"], filterOverride: "category eq 'Audio' and price gt 200", // Override for this query }, ], }, }); ``` --- ## Vector Thresholds Set minimum similarity thresholds: ```typescript const results = await searchClient.search("*", { vectorSearchOptions: { queries: [ { kind: "text", text: "wireless headphones", kNearestNeighborsCount: 50, fields: ["descriptionVector"], threshold: { kind: "vectorSimilarity", value: 0.8, // Only results with similarity >= 0.8 }, }, ], }, }); ``` --- ## Exhaustive Search Force brute-force search instead of approximate (HNSW): ```typescript const results = await searchClient.search("*", { vectorSearchOptions: { queries: [ { kind: "text", text: "specific product query", kNearestNeighborsCount: 10, fields: ["descriptionVector"], exhaustive: true, // Slower but more accurate }, ], }, }); ``` --- ## Index Configuration for Vector Search Create an index with vector search capabilities: ```typescript import { SearchIndexClient, SearchIndex } from "@azure/search-documents"; const indexClient = new SearchIndexClient(endpoint, credential); const index: SearchIndex = { name: "products", fields: [ { name: "id", type: "Edm.String", key: true }, { name: "name", type: "Edm.String", searchable: true }, { name: "description", type: "Edm.String", searchable: true }, { name: "category", type: "Edm.String", filterable: true, facetable: true }, { name: "price", type: "Edm.Double", filterable: true, sortable: true }, { name: "descriptionVector", type: "Collection(Edm.Single)", searchable: true, vectorSearchDimensions: 1536, vectorSearchProfileName: "vector-profile", }, ], vectorSearch: { algorithms: [ { name: "hnsw-algorithm", kind: "hnsw", parameters: { m: 4, efConstruction: 400, efSearch: 500, metric: "cosine", }, }, ], profiles: [ { name: "vector-profile", algorithmConfigurationName: "hnsw-algorithm", vectorizerName: "openai-vectorizer", // Optional: for integrated vectorization }, ], vectorizers: [ { name: "openai-vectorizer", kind: "azureOpenAI", azureOpenAIParameters: { resourceUri: process.env["AZURE_OPENAI_ENDPOINT"]!, deploymentId: "text-embedding-ada-002", modelName: "text-embedding-ada-002", }, }, ], }, }; await indexClient.createOrUpdateIndex(index); ``` --- ## Complete Hybrid Search Example ```typescript import { SearchClient } from "@azure/search-documents"; import { DefaultAzureCredential } from "@azure/identity"; interface Product { id: string; name: string; description: string; category: string; price: number; } async function hybridSearch(query: string, category?: string) { const client = new SearchClient<Product>( process.env["AZURE_SEARCH_ENDPOINT"]!, "products", new DefaultAzureCredential() ); const filter = category ? `category eq '${category}'` : undefined; const results = await client.search(query, { filter, searchFields: ["name", "description"], vectorSearchOptions: { queries: [ { kind: "text", text: query, kNearestNeighborsCount: 50, fields: ["descriptionVector"], }, ], }, top: 10, select: ["id", "name", "description", "category", "price"], }); const items: Array<{ document: Product; score: number }> = []; for await (const result of results.results) { items.push({ document: result.document, score: result.score ?? 0, }); } return items; } // Usage const results = await hybridSearch("wireless noise canceling", "Electronics"); results.forEach((r) => { console.log(`${r.document.name}: $${r.document.price} (score: ${r.score})`); }); ``` --- ## Best Practices 1. **Use hybrid search** - Combining keyword + vector typically outperforms either alone 2. **Tune kNearestNeighborsCount** - Higher values increase recall but slow down search 3. **Use pre-filtering** - When filter selectivity is high (filters out most documents) 4. **Use post-filtering** - When you need exactly k vector matches, then filter 5. **Set appropriate thresholds** - Filter out low-quality matches 6. **Weight vector queries** - Boost more relevant vector fields 7. **Monitor index metrics** - Track search latency and recall --- ## See Also - [Semantic Ranking](./semantic-ranking.md) - Combine with semantic reranking - [Official Samples](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/search/search-documents/samples)
-
-
SKILL.md 7.1 KB
--- name: azure-search-documents-ts description: Build search applications using Azure AI Search SDK for JavaScript (@azure/search-documents). Use when creating/managing indexes, implementing vector/hybrid search, semantic ranking, or building agentic retrieval with knowledge bases. license: MIT metadata: author: Microsoft version: "1.0.0" package: '@azure/search-documents' --- # Azure AI Search SDK for TypeScript Build search applications with vector, hybrid, and semantic search capabilities. ## Installation ```bash npm install @azure/search-documents @azure/identity ``` ## Environment Variables ```bash AZURE_SEARCH_ENDPOINT=https://<service-name>.search.windows.net AZURE_SEARCH_INDEX_NAME=my-index AZURE_SEARCH_ADMIN_KEY=<admin-key> # Optional if using Entra ID AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production ``` ## Authentication ```typescript import { SearchClient, SearchIndexClient } from "@azure/search-documents"; import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity"; const endpoint = process.env.AZURE_SEARCH_ENDPOINT!; const indexName = process.env.AZURE_SEARCH_INDEX_NAME!; // Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential> const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]}); // Or use a specific credential directly in production: // See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes // const credential = new ManagedIdentityCredential(); // For searching const searchClient = new SearchClient(endpoint, indexName, credential); // For index management const indexClient = new SearchIndexClient(endpoint, credential); ``` ## Core Workflow ### Create Index with Vector Field ```typescript import { SearchIndex, SearchField, VectorSearch } from "@azure/search-documents"; const index: SearchIndex = { name: "products", fields: [ { name: "id", type: "Edm.String", key: true }, { name: "title", type: "Edm.String", searchable: true }, { name: "description", type: "Edm.String", searchable: true }, { name: "category", type: "Edm.String", filterable: true, facetable: true }, { name: "embedding", type: "Collection(Edm.Single)", searchable: true, vectorSearchDimensions: 1536, vectorSearchProfileName: "vector-profile", }, ], vectorSearch: { algorithms: [ { name: "hnsw-algorithm", kind: "hnsw" }, ], profiles: [ { name: "vector-profile", algorithmConfigurationName: "hnsw-algorithm" }, ], }, }; await indexClient.createOrUpdateIndex(index); ``` ### Index Documents ```typescript const documents = [ { id: "1", title: "Widget", description: "A useful widget", category: "Tools", embedding: [...] }, { id: "2", title: "Gadget", description: "A cool gadget", category: "Electronics", embedding: [...] }, ]; const result = await searchClient.uploadDocuments(documents); console.log(`Indexed ${result.results.length} documents`); ``` ### Full-Text Search ```typescript const results = await searchClient.search("widget", { select: ["id", "title", "description"], filter: "category eq 'Tools'", orderBy: ["title asc"], top: 10, }); for await (const result of results.results) { console.log(`${result.document.title}: ${result.score}`); } ``` ### Vector Search ```typescript const queryVector = await getEmbedding("useful tool"); // Your embedding function const results = await searchClient.search("*", { vectorSearchOptions: { queries: [ { kind: "vector", vector: queryVector, fields: ["embedding"], kNearestNeighborsCount: 10, }, ], }, select: ["id", "title", "description"], }); for await (const result of results.results) { console.log(`${result.document.title}: ${result.score}`); } ``` ### Hybrid Search (Text + Vector) ```typescript const queryVector = await getEmbedding("useful tool"); const results = await searchClient.search("tool", { vectorSearchOptions: { queries: [ { kind: "vector", vector: queryVector, fields: ["embedding"], kNearestNeighborsCount: 50, }, ], }, select: ["id", "title", "description"], top: 10, }); ``` ### Semantic Search ```typescript // Index must have semantic configuration const index: SearchIndex = { name: "products", fields: [...], semanticSearch: { configurations: [ { name: "semantic-config", prioritizedFields: { titleField: { name: "title" }, contentFields: [{ name: "description" }], }, }, ], }, }; // Search with semantic ranking const results = await searchClient.search("best tool for the job", { queryType: "semantic", semanticSearchOptions: { configurationName: "semantic-config", captions: { captionType: "extractive" }, answers: { answerType: "extractive", count: 3 }, }, select: ["id", "title", "description"], }); for await (const result of results.results) { console.log(`${result.document.title}`); console.log(` Caption: ${result.captions?.[0]?.text}`); console.log(` Reranker Score: ${result.rerankerScore}`); } ``` ## Filtering and Facets ```typescript // Filter syntax const results = await searchClient.search("*", { filter: "category eq 'Electronics' and price lt 100", facets: ["category,count:10", "brand"], }); // Access facets for (const [facetName, facetResults] of Object.entries(results.facets || {})) { console.log(`${facetName}:`); for (const facet of facetResults) { console.log(` ${facet.value}: ${facet.count}`); } } ``` ## Autocomplete and Suggestions ```typescript // Create suggester in index const index: SearchIndex = { name: "products", fields: [...], suggesters: [ { name: "sg", sourceFields: ["title", "description"] }, ], }; // Autocomplete const autocomplete = await searchClient.autocomplete("wid", "sg", { mode: "twoTerms", top: 5, }); // Suggestions const suggestions = await searchClient.suggest("wid", "sg", { select: ["title"], top: 5, }); ``` ## Batch Operations ```typescript // Batch upload, merge, delete const batch = [ { upload: { id: "1", title: "New Item" } }, { merge: { id: "2", title: "Updated Title" } }, { delete: { id: "3" } }, ]; const result = await searchClient.indexDocuments({ actions: batch }); ``` ## Key Types ```typescript import { SearchClient, SearchIndexClient, SearchIndexerClient, SearchIndex, SearchField, SearchOptions, VectorSearch, SemanticSearch, SearchIterator, } from "@azure/search-documents"; ``` ## Best Practices 1. **Use hybrid search** - Combine vector + text for best results 2. **Enable semantic ranking** - Improves relevance for natural language queries 3. **Batch document uploads** - Use `uploadDocuments` with arrays, not single docs 4. **Use filters for security** - Implement document-level security with filters 5. **Index incrementally** - Use `mergeOrUploadDocuments` for updates 6. **Monitor query performance** - Use `includeTotalCount: true` sparingly in production
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.