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.

Ciza · 0 points · 21 views 1 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download microsoft-skills-.github_plugins_azure-sdk-typescript_skills_azure-search-documents-ts-e58528d.zip · 8 KB
Part of microsoft/skills — 195 skills

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

  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
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.

No comments yet.

Reviews (0)

No reviews yet.

Related