Generated banner for Azure Search Documents TS
Generated logo for Azure Search Documents TS

Azure Search Documents TS

MicrosoftOfficialCreated by Microsoft

  • Skill
  • Tool integration
  • Data and AI
  • MIT
  • No issues found

From the author

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.
— the description by Microsoft

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The azure-search-documents-ts skill provides a comprehensive guide to using the @azure/search-documents SDK for JavaScript and TypeScript. It covers the full lifecycle of search application development, including index creation, document management, and advanced retrieval techniques. Developers can implement full-text search, vector search, hybrid search, and semantic ranking to build robust, AI-powered search experiences. The skill also details how to handle authentication using Azure Identity, manage index schemas, and perform batch operations for efficient data ingestion.

What it does

  • Create and manage search indexes with custom field definitions.
  • Implement vector search using pre-computed embeddings or integrated vectorization.
  • Execute hybrid search queries combining keyword and vector retrieval.
  • Enable semantic ranking for improved relevance in natural language queries.
  • Perform batch operations for uploading, merging, or deleting documents.
  • Configure autocomplete and suggestion features for search interfaces.

When to use it

  • When building search-enabled applications on the Azure platform.
  • When implementing RAG (Retrieval-Augmented Generation) pipelines with knowledge bases.
  • When you need to support complex search requirements like semantic reranking or vector-based similarity.
  • When managing search index schemas programmatically in a TypeScript environment.

Advantages

  • Native support for modern search paradigms like vector and hybrid search.
  • Strongly-typed TypeScript interfaces for index and search configurations.
  • Seamless integration with Azure Identity for secure authentication.
  • Built-in support for semantic ranking to enhance search result quality.

Usage examples

“How do I create a search index with a vector field using the Azure SDK?”
The agent provides a code snippet demonstrating the use of SearchIndexClient to define an index with vectorSearch profiles and vectorSearchDimensions.
“Show me how to perform a hybrid search with text and vector queries.”
The agent provides an example using the search method with both a search string and vectorSearchOptions to combine keyword and vector results.
“How can I enable semantic ranking for my search results?”
The agent explains how to configure semanticSearch in the index and use queryType: 'semantic' in the search options.
“How do I upload multiple documents to my index in one go?”
The agent demonstrates the use of the indexDocuments method with an array of actions to perform batch uploads, merges, or deletions.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/microsoft/azure-search-documents-ts/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip azure-search-documents-ts.zip -d .agents/skills

Documentation by Microsoft

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`);
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}`);
}
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,
});
// 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

references/vector-search.md

@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


Installation

npm install @azure/search-documents @azure/identity

Client Setup

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

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;

Search using a pre-computed embedding vector:

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:

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:

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"],
});

Search across multiple vector fields simultaneously:

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:

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:

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:

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:

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
        },
      },
    ],
  },
});

Force brute-force search instead of approximate (HNSW):

const results = await searchClient.search("*", {
  vectorSearchOptions: {
    queries: [
      {
        kind: "text",
        text: "specific product query",
        kNearestNeighborsCount: 10,
        fields: ["descriptionVector"],
        exhaustive: true, // Slower but more accurate
      },
    ],
  },
});

Create an index with vector search capabilities:

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

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

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Microsoft
Publisher
Microsoft (Official)
Source
microsoft/skills · .github/plugins/azure-sdk-typescript/skills/azure-search-documents-ts · commit 0696600
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download azure-search-documents-ts.zip