NuGet.Services.SearchService.Core is the search microservice for nuget.org. It is a read-only ASP.NET Core web application that sits between NuGet clients and an Azure AI Search resource, acting as a protocol adapter: clients speak the NuGet V2/V3 search protocol; the service translates those requests into Azure Search REST calls and maps the returned documents back into the JSON shapes callers expect.The service is stateless and horizontally scalable. nuget.org deploys at least two instances across four Azure regions, each paired with its own Azure Search resource for business-continuity and disaster-recovery purposes.
V3 Search Endpoint
/query — NuGet V3 Search resource used by Visual Studio Package Manager UI and the .NET CLI (dotnet tool search).
V3 Autocomplete Endpoint
/autocomplete — NuGet V3 Autocomplete resource used by Package Manager Console tab-completion.
Internal V2 Search Endpoint
/search/query — Used exclusively by NuGetGallery for OData hijack queries, V2 API search, and gallery UI search.
Health and Diagnostics
/ returns a simple success boolean; /search/diag returns detailed diagnostic JSON for monitoring.
NuGet Clients / Gallery UI │ ▼NuGet.Services.SearchService.Core ←── this project │ ├── /query, /autocomplete ──→ Azure AI Search (search index) └── /search/query ──→ Azure AI Search (search index OR hijack index) + Azure Blob Storage (auxiliary files)
The service never writes to Azure Search. Population of the search and hijack indexes is handled by separate jobs (Db2AzureSearch, Catalog2AzureSearch, Auxiliary2AzureSearch). This project only reads.NuGetGallery calls /search/query when it “hijacks” OData queries instead of going to SQL — covering V2 package search, FindPackagesById, single-version metadata lookups, and the gallery UI search page.
Single controller, all endpoints. Every public HTTP endpoint lives on SearchController. Before any search action executes, EnsureInitializedAsync() blocks until auxiliary file data (download counts, verified package flags) has been loaded at least once from Blob Storage.
ignoreFilter toggles between two Azure Search indexes. When ignoreFilter=false (the default), the search index is queried — containing only the latest, listed packages. When ignoreFilter=true, the hijack index is queried — containing all versions including unlisted ones. This single parameter switch makes the same endpoint serve both package discovery and package restore scenarios.
Autocomplete dual-mode dispatch. The /autocomplete endpoint infers its mode from which query parameter is present: if q is set (or neither parameter is set), it returns matching package IDs; if only id is set, it returns all versions for that package ID. This branching is handled directly in the controller action with a single ternary expression.
/search/query is an unstable internal contract. The response uses PascalCase field names and includes extra fields (Owners, Listed, FlattenedDependencies, Frameworks, Tfms, ComputedFrameworks, ComputedTfms, Hash, PackageFileSize, etc.) that are absent from the V3 /query response. External clients must not depend on this endpoint — use /query discovered via the V3 service index instead.
Test data filtering has edge cases. Optimized document-lookup queries (e.g., packageid:BaseTestPackage) bypass the testData=false filter entirely. Owner-based filtering is also impossible when querying the hijack index because the owners field is not populated in that index.
debug=true is available on all search and autocomplete endpoints. Appending debug=true to any request includes the raw Azure Search document and internal diagnostic fields in the response — useful for live-site investigations without needing direct Azure portal access.
Configuration is read only from APPSETTING_-prefixed environment variables in Azure App Services.HostBuilderHelper strips all default environment variable configuration sources and replaces them with a single source scoped to the APPSETTING_ prefix, preventing accidental leakage of unrelated process environment variables into the application configuration.