Skip to main content

Overview

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.

Role in the NuGetGallery Ecosystem

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.

Key Files and Classes

FileClassPurpose
Program.csProgramEntry point; delegates host construction to HostBuilderHelper from the shared web library.
Startup.csStartupRegisters all DI services, configures Autofac, sets up CORS (GET/HEAD/OPTIONS, any origin), HSTS, and Application Insights.
Controllers/SearchController.csSearchControllerSingle API controller exposing all five endpoints: /, /search/diag, /search/query, /query, /autocomplete.
Support/AuxiliaryFileReloaderBackgroundService.csAuxiliaryFileReloaderBackgroundServiceBackgroundService that continuously reloads auxiliary files (downloads, verified packages) from Blob Storage via IAuxiliaryFileReloader.
Support/FeatureFlagBackgroundService.csFeatureFlagBackgroundServiceBackgroundService that continuously polls and caches feature flags via IFeatureFlagCacheService.
Support/SecretRefresherBackgroundService.csSecretRefresherBackgroundServiceBackgroundService that periodically refreshes Azure Key Vault secrets into the running configuration via ISecretRefresher.

Dependencies

Internal Project References

ProjectRole
Microsoft.PackageManagement.Search.WebShared ASP.NET Core infrastructure: HostBuilderHelper, StartupHelper, ApiExceptionFilterAttribute, CORS/HSTS helpers, Autofac host factory.
NuGet.Services.AzureSearchCore search logic, Azure Search client wrappers, ISearchService, ISearchStatusService, IAuxiliaryDataCache, IAuxiliaryFileReloader, ISecretRefresher, all request/response models.

NuGet Package References (via Microsoft.PackageManagement.Search.Web)

PackagePurpose
Microsoft.ApplicationInsights.AspNetCoreTelemetry; adaptive sampling disabled; custom KnownOperationNameEnricher tags known operation names.
Microsoft.AspNetCore.HttpLifted transitive dependency for Azure DevOps Component Governance compliance.
System.Drawing.CommonLifted transitive dependency for Component Governance compliance.
Autofac / Autofac.Extensions.DependencyInjectionDI container; Autofac modules registered via builder.RegisterAssemblyModules(...).

Notable Patterns and Implementation Details

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.

Background Services

Three BackgroundService implementations keep runtime state fresh without requiring restarts:
ServiceWhat It RefreshesObservable Via
AuxiliaryFileReloaderBackgroundServiceDownload counts, verified-packages list, popularity-transfers from Azure Blob Storage/search/diagAuxiliaryFiles.Loaded
FeatureFlagBackgroundServiceFeature flag cache from Azure Table StorageInternal flag checks at request time
SecretRefresherBackgroundServiceKey Vault secrets rotated into live IConfiguration/search/diagServer.LastServiceRefreshTime

Supported sortBy Values for /search/query

ValueBehavior
relevanceDefault; Azure Search relevance scoring
lastEditedDescending by last-edit timestamp
publishedDescending by published timestamp
created-asc / created-descAscending or descending by creation timestamp
title-asc / title-descCase-insensitive lexicographic by title (falls back to package ID if no title)
totalDownloads-asc / totalDownloads-descAscending or descending by total download count