快速入门:在 .NET 中创建搜索索引Quickstart: Create a search index in .NET

使用 C# 创建 .NET Core 控制台应用程序,该应用程序使用 Visual Studio 和 Azure 认知搜索 .NET SDK 创建、加载和查询 Azure 认知搜索索引。Create a .NET Core console application in C# that creates, loads, and queries an Azure Cognitive Search index using Visual Studio and the Azure Cognitive Search .NET SDK.

本文逐步介绍如何创建该应用程序。This article explains how to create the application step by step. 如果要跳转到代码,还可以下载并运行完整的应用程序You could also download and run the complete application if you want to jump ahead to the code.

备注

为简单起见,本文中的演示代码使用 Azure 认知搜索 .NET SDK 的同步方法。The demo code in this article uses the synchronous methods of the Azure Cognitive Search .NET SDK for simplicity. 但是,对于生产场景,建议你在自己的应用程序中使用异步方法,使应用程序保持可缩放且响应迅速。However, for production scenarios, we recommend using the asynchronous methods in your own applications to keep them scalable and responsive. 例如,可以使用 CreateAsyncDeleteAsync,而不是 CreateDeleteFor example, you could use CreateAsync and DeleteAsync instead of Create and Delete.

先决条件Prerequisites

在开始之前,必须满足以下条件:Before you begin, you must have the following:

获取密钥和 URLGet a key and URL

对服务的调用要求每个请求都有一个 URL 终结点和一个访问密钥。Calls to the service require a URL endpoint and an access key on every request. 搜索服务是使用这二者创建的,因此,如果向订阅添加了 Azure 认知搜索,则请按以下步骤获取必需信息:A search service is created with both, so if you added Azure Cognitive Search to your subscription, follow these steps to get the necessary information:

  1. 登录到 Azure 门户,在搜索服务的“概述”页中获取 URL。Sign in to the Azure portal, and in your search service Overview page, get the URL. 示例终结点可能类似于 https://mydemo.search.azure.cnAn example endpoint might look like https://mydemo.search.azure.cn.

  2. 在“设置” > “密钥”中,获取有关该服务的完全权限的管理员密钥 。In Settings > Keys, get an admin key for full rights on the service. 有两个可交换的管理员密钥,为保证业务连续性而提供,以防需要滚动一个密钥。There are two interchangeable admin keys, provided for business continuity in case you need to roll one over. 可以在请求中使用主要或辅助密钥来添加、修改和删除对象。You can use either the primary or secondary key on requests for adding, modifying, and deleting objects.

    此外,获取查询密钥。Get the query key as well. 最好使用只读权限发出查询请求。It's a best practice to issue query requests with read-only access.

获取 HTTP 终结点和访问密钥Get an HTTP endpoint and access key

所有请求对发送到服务的每个请求都需要 API 密钥。All requests require an api-key on every request sent to your service. 具有有效的密钥可以在发送请求的应用程序与处理请求的服务之间建立信任关系,这种信任关系以每个请求为基础。Having a valid key establishes trust, on a per request basis, between the application sending the request and the service that handles it.

设置你的环境Set up your environment

首先,打开 Visual Studio,并新建能在 .NET Core 上运行的控制台应用项目。Begin by opening Visual Studio and creating a new Console App project that can run on .NET Core.

安装 NuGet 包Install NuGet packages

Azure 认知搜索 .NET SDK 由作为 NuGet 包分发的一些客户端库组成。The Azure Cognitive Search .NET SDK consists of a few client libraries that are distributed as NuGet packages.

对于此项目,使用 Microsoft.Azure.Search NuGet 包的版本 9,以及最新的 Microsoft.Extensions.Configuration.Json NuGet 包。For this project, use version 9 of the Microsoft.Azure.Search NuGet package and the latest Microsoft.Extensions.Configuration.Json NuGet package.

  1. 在“工具” > “NuGet 包管理器”中,选择“管理解决方案的 NuGet 包...” 。In Tools > NuGet Package Manager, select Manage NuGet Packages for Solution....

  2. 单击“浏览”。Click Browse.

  3. 搜索 Microsoft.Azure.Search,并选择 9.0.1 版或更高版本(最新稳定版本为 10.1.0)。Search for Microsoft.Azure.Search and select version 9.0.1 or later (Latest stable version is 10.1.0).

  4. 单击右侧的“安装”,将该程序集添加到你的项目和解决方案。Click Install on the right to add the assembly to your project and solution.

  5. Microsoft.Extensions.Configuration.Json 重复以上步骤,选择 2.2.0 版或更高版本。Repeat for Microsoft.Extensions.Configuration.Json, selecting version 2.2.0 or later.

添加 Azure 认知搜索服务信息Add Azure Cognitive Search service information

  1. 在“解决方案资源管理器”中,右键单击项目,依次选择“添加” > “新建项...” 。In Solution Explorer, right click on the project and select Add > New Item... .

  2. 在“添加新项”中,搜索“JSON”,以返回与 JSON 相关的项目类型列表。In Add New Item, search for "JSON" to return a JSON-related list of item types.

  3. 选择 JSON 文件,将该文件命名为“appsettings.json”,并单击“添加” 。Choose JSON File, name the file "appsettings.json", and click Add.

  4. 将文件添加到输出目录。Add the file to your output directory. 右键单击 appsettings.json 并选择“属性”。Right-click appsettings.json and select Properties. 在“复制到输出目录”中,选择“如果较新则复制” 。In Copy to Output Directory, select Copy if newer.

  5. 将以下 JSON 复制到新 JSON 文件中。Copy the following JSON into your new JSON file.

    {
      "SearchServiceName": "<YOUR-SEARCH-SERVICE-NAME>",
      "SearchServiceAdminApiKey": "<YOUR-ADMIN-API-KEY>",
      "SearchIndexName": "hotels-quickstart"
    }
    
  6. 将搜索服务名称 (YOUR-SEARCH-SERVICE-NAME) 和管理员 API 密钥 (YOUR-ADMIN-API-KEY) 替换为有效值。Replace the search service name (YOUR-SEARCH-SERVICE-NAME) and admin API key (YOUR-ADMIN-API-KEY) with valid values. 如果服务终结点是 https://mydemo.search.chinacloudapi.cn,则服务名称为“mydemo”。If your service endpoint is https://mydemo.search.chinacloudapi.cn, the service name would be "mydemo".

将类“.Method”文件添加到你的项目Add class ".Method" files to your project

若要在控制台中生成有意义的输出,需要执行此步骤。This step is required to produce meaningful output in the console. 将结果打印到控制台窗口时,必须以字符串形式返回 Hotel 对象的各个字段。When printing results to the console window, individual fields from the Hotel object must be returned as strings. 此步骤实现 ToString() 来执行此任务,方法是将所需的代码复制到两个新文件中。This step implements ToString() to perform this task, which you do by copying the necessary code to two new files.

  1. 将两个空的类定义添加到你的项目:Address.Methods.cs、Hotel.Methods.csAdd two empty class definitions to your project: Address.Methods.cs, Hotel.Methods.cs

  2. 在 Address.Methods.cs 中,使用以下代码(1-25 行)覆盖默认内容。In Address.Methods.cs, overwrite the default contents with the following code, lines 1-25.

  3. 在 Hotel.Methods.cs 中,复制 1-68 行In Hotel.Methods.cs, copy lines 1-68.

1 - 创建索引1 - Create index

酒店索引由简单和复杂字段组成,其中简单字段是“酒店名称”或“说明”,复杂字段是包含子字段的地址或房间集合。The hotels index consists of simple and complex fields, where a simple field is "HotelName" or "Description", and complex fields are an address with subfields, or a collection of rooms. 如果索引包含复杂类型,请在单独的类中隔离复杂字段定义。When an index includes complex types, isolate the complex field definitions in separate classes.

  1. 将两个空的类定义添加到你的项目:Address.cs、Hotel.csAdd two empty class definitions to your project: Address.cs, Hotel.cs

  2. 在 Address.cs 中,使用以下代码覆盖默认内容:In Address.cs, overwrite the default contents with the following code:

    using System;
    using Microsoft.Azure.Search;
    using Microsoft.Azure.Search.Models;
    using Newtonsoft.Json;
    
    namespace AzureSearchQuickstart
    {
        public partial class Address
        {
            [IsSearchable]
            public string StreetAddress { get; set; }
    
            [IsSearchable, IsFilterable, IsSortable, IsFacetable]
            public string City { get; set; }
    
            [IsSearchable, IsFilterable, IsSortable, IsFacetable]
            public string StateProvince { get; set; }
    
            [IsSearchable, IsFilterable, IsSortable, IsFacetable]
            public string PostalCode { get; set; }
    
            [IsSearchable, IsFilterable, IsSortable, IsFacetable]
            public string Country { get; set; }
        }
    }
    
  3. 在 Hotel.cs 中,类定义索引的整体结构,包括对地址类的引用。In Hotel.cs, the class defines the overall structure of the index, including references to the address class.

    namespace AzureSearchQuickstart
    {
        using System;
        using Microsoft.Azure.Search;
        using Microsoft.Azure.Search.Models;
        using Newtonsoft.Json;
    
        public partial class Hotel
        {
            [System.ComponentModel.DataAnnotations.Key]
            [IsFilterable]
            public string HotelId { get; set; }
    
            [IsSearchable, IsSortable]
            public string HotelName { get; set; }
    
            [IsSearchable]
            [Analyzer(AnalyzerName.AsString.EnMicrosoft)]
            public string Description { get; set; }
    
            [IsSearchable]
            [Analyzer(AnalyzerName.AsString.FrLucene)]
            [JsonProperty("Description_fr")]
            public string DescriptionFr { get; set; }
    
            [IsSearchable, IsFilterable, IsSortable, IsFacetable]
            public string Category { get; set; }
    
            [IsSearchable, IsFilterable, IsFacetable]
            public string[] Tags { get; set; }
    
            [IsFilterable, IsSortable, IsFacetable]
            public bool? ParkingIncluded { get; set; }
    
            [IsFilterable, IsSortable, IsFacetable]
            public DateTimeOffset? LastRenovationDate { get; set; }
    
            [IsFilterable, IsSortable, IsFacetable]
            public double? Rating { get; set; }
    
            public Address Address { get; set; }
        }
    }
    

    该字段的特性决定字段在应用程序中的使用方式。Attributes on the field determine how it is used in an application. 例如,IsSearchable 属性必须分配给每个应包含在全文搜索中的字段。For example, the IsSearchable attribute must be assigned to every field that should be included in a full text search.

    备注

    在 .NET SDK 中,必须显式将字段属性化为 IsSearchableIsFilterableIsSortableIsFacetableIn the .NET SDK, fields must be explicitly attributed as IsSearchable, IsFilterable, IsSortable, and IsFacetable. 此行为与 REST API 相反,后者基于数据类型隐式启用属性(例如,简单的字符串字段是自动可搜索的)。This behavior is in contrast with the REST API which implicitly enables attribution based on data type (for example, simple string fields are automatically searchable).

    类型为 string 的索引中必须恰好有一个字段为“密钥”字段,用于唯一地标识每个文档。Exactly one field in your index of type string must be the key field, uniquely identifying each document. 在此架构中,密钥为 HotelIdIn this schema, the key is HotelId.

    在此索引中,“说明”字段使用可选的 analyzer 属性,该属性在需要替代默认标准 Lucene 分析器时指定。In this index, the description fields use the optional analyzer property, specified when you want to override the default standard Lucene analyzer. description_fr 字段使用法语 Lucene 分析器 (FrLucene),因为该字段存储法语文本。The description_fr field is using the French Lucene analyzer (FrLucene) because it stores French text. description 使用可选的 Microsoft 语言分析器 (EnMicrosoft)。The description is using the optional Microsoft language analyzer (EnMicrosoft).

  4. 在 Program.cs 中,使用存储在应用程序配置文件 (appsettings.json) 中的值,创建 SearchServiceClient 类的实例,以连接到服务。In Program.cs, create an instance of the SearchServiceClient class to connect to the service, using values that are stored in the application's config file (appsettings.json).

    SearchServiceClient 具有 Indexes 属性,提供创建、列出、更新或删除 Azure 认知搜索索引所需的所有方法。SearchServiceClient has an Indexes property, providing all the methods you need to create, list, update, or delete Azure Cognitive Search indexes.

    using System;
    using System.Linq;
    using System.Threading;
    using Microsoft.Azure.Search;
    using Microsoft.Azure.Search.Models;
    using Microsoft.Extensions.Configuration;
    
    namespace AzureSearchQuickstart
    {
        class Program {
            // Demonstrates index delete, create, load, and query
            // Commented-out code is uncommented in later steps
            static void Main(string[] args)
            {
                IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
                IConfigurationRoot configuration = builder.Build();
    
                SearchServiceClient serviceClient = CreateSearchServiceClient(configuration);
    
                string indexName = configuration["SearchIndexName"];
    
                Console.WriteLine("{0}", "Deleting index...\n");
                DeleteIndexIfExists(indexName, serviceClient);
    
                Console.WriteLine("{0}", "Creating index...\n");
                CreateIndex(indexName, serviceClient);
    
                // Uncomment next 3 lines in "2 - Load documents"
                // ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
                // Console.WriteLine("{0}", "Uploading documents...\n");
                // UploadDocuments(indexClient);
    
                // Uncomment next 2 lines in "3 - Search an index"
                // Console.WriteLine("{0}", "Searching index...\n");
                // RunQueries(indexClient);
    
                Console.WriteLine("{0}", "Complete.  Press any key to end application...\n");
                Console.ReadKey();
            }
    
            // Create the search service client
            private static SearchServiceClient CreateSearchServiceClient(IConfigurationRoot configuration)
            {
                string searchServiceName = configuration["SearchServiceName"];
                string adminApiKey = configuration["SearchServiceAdminApiKey"];
    
                SearchServiceClient serviceClient = new SearchServiceClient(searchServiceName, new SearchCredentials(adminApiKey));
                serviceClient.SearchDnsSuffix = "search.azure.cn";
                return serviceClient;
            }
    
            // Delete an existing index to reuse its name
            private static void DeleteIndexIfExists(string indexName, SearchServiceClient serviceClient)
            {
                if (serviceClient.Indexes.Exists(indexName))
                {
                    serviceClient.Indexes.Delete(indexName);
                }
            }
    
            // Create an index whose fields correspond to the properties of the Hotel class.
            // The Address property of Hotel will be modeled as a complex field.
            // The properties of the Address class in turn correspond to sub-fields of the Address complex field.
            // The fields of the index are defined by calling the FieldBuilder.BuildForType() method.
            private static void CreateIndex(string indexName, SearchServiceClient serviceClient)
            {
                var definition = new Microsoft.Azure.Search.Models.Index()
                {
                    Name = indexName,
                    Fields = FieldBuilder.BuildForType<Hotel>()
                };
    
                serviceClient.Indexes.Create(definition);
            }
        }
    }    
    

    如果可能,请在应用程序中共享单个 SearchServiceClient 实例,避免打开太多连接。If possible, share a single instance of SearchServiceClient in your application to avoid opening too many connections. 类方法是线程安全的,可以启用此类共享。Class methods are thread-safe to enable such sharing.

    该类具有一系列构造函数。The class has several constructors. 需要将搜索服务名称和 SearchCredentials 对象用作参数。The one you want takes your search service name and a SearchCredentials object as parameters. SearchCredentials 包装 API 密钥。SearchCredentials wraps your api-key.

    在索引定义中,Field 对象最简单的创建方法是调用 FieldBuilder.BuildForType 方法,为类型参数传递模型类。In the index definition, the easiest way to create the Field objects is by calling the FieldBuilder.BuildForType method, passing a model class for the type parameter. 模型类具有映射到索引的字段属性。A model class has properties that map to the fields of your index. 使用此映射可将搜索索引中的文档绑定到模型类的实例。This mapping allows you to bind documents from your search index to instances of your model class.

    备注

    如果不打算使用模型类,仍可通过直接创建 Field 对象来定义索引。If you don't plan to use a model class, you can still define your index by creating Field objects directly. 可以向构造函数提供字段的名称以及数据类型(或分析器(用于字符串字段))。You can provide the name of the field to the constructor, along with the data type (or analyzer for string fields). 此外可以设置其他属性,如 IsSearchableIsFilterable,仅举几例。You can also set other properties like IsSearchable, IsFilterable, to name a few.

  5. 按 F5,可生成应用并创建索引。Press F5 to build the app and create the index.

    如果成功构建项目,则会打开控制台窗口,将删除和创建索引的状态消息写入屏幕。If the project builds successfully, a console window opens, writing status messages to the screen for deleting and creating the index.

2 - 加载文档2 - Load documents

在 Azure 认知搜索中,文档这一数据结构既是索引输入,也是查询输出。In Azure Cognitive Search, documents are data structures that are both inputs to indexing and outputs from queries. 文档输入从外部数据源获取,可能是数据库中的行、Blob 存储中的 blob 或磁盘上的 JSON 文档。As obtained from an external data source, document inputs might be rows in a database, blobs in Blob storage, or JSON documents on disk. 在此示例中,我们采用了快捷方式,并在代码本身中嵌入了四个酒店的 JSON 文档。In this example, we're taking a shortcut and embedding JSON documents for four hotels in the code itself.

上传文档时,必须使用 IndexBatch 对象。When uploading documents, you must use an IndexBatch object. IndexBatch 包含 IndexAction 对象的集合,其中每个对象均包含一个文档和一个属性,该属性用于指示 Azure 认知搜索要执行什么操作(上传、合并、删除和 mergeOrUpload)。An IndexBatch contains a collection of IndexAction objects, each of which contains a document and a property telling Azure Cognitive Search what action to perform (upload, merge, delete, and mergeOrUpload).

  1. 在 Program.cs 中,创建文档和索引操作的数组,然后将该数组传递给 IndexBatchIn Program.cs, create an array of documents and index actions, and then pass the array to IndexBatch. 以下文档符合酒店和地址类定义的 hotel-quickstart 索引。The documents below conform to the hotel-quickstart index, as defined by the hotel and address classes.

    // Upload documents as a batch
    private static void UploadDocuments(ISearchIndexClient indexClient)
    {
        var actions = new IndexAction<Hotel>[]
        {
            IndexAction.Upload(
                new Hotel()
                {
                    HotelId = "1",
                    HotelName = "Secret Point Motel",
                    Description = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
                    DescriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
                    Category = "Boutique",
                    Tags = new[] { "pool", "air conditioning", "concierge" },
                    ParkingIncluded = false,
                    LastRenovationDate = new DateTimeOffset(1970, 1, 18, 0, 0, 0, TimeSpan.Zero),
                    Rating = 3.6,
                    Address = new Address()
                    {
                        StreetAddress = "677 5th Ave",
                        City = "New York",
                        StateProvince = "NY",
                        PostalCode = "10022",
                        Country = "USA"
                    }
                }
            ),
            IndexAction.Upload(
                new Hotel()
                {
                    HotelId = "2",
                    HotelName = "Twin Dome Motel",
                    Description = "The hotel is situated in a  nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.",
                    DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                    Category = "Boutique",
                    Tags = new[] { "pool", "free wifi", "concierge" },
                    ParkingIncluded = false,
                    LastRenovationDate =  new DateTimeOffset(1979, 2, 18, 0, 0, 0, TimeSpan.Zero),
                    Rating = 3.60,
                    Address = new Address()
                    {
                        StreetAddress = "140 University Town Center Dr",
                        City = "Sarasota",
                        StateProvince = "FL",
                        PostalCode = "34243",
                        Country = "USA"
                    }
                }
            ),
            IndexAction.Upload(
                new Hotel()
                {
                    HotelId = "3",
                    HotelName = "Triple Landscape Hotel",
                    Description = "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                    DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                    Category = "Resort and Spa",
                    Tags = new[] { "air conditioning", "bar", "continental breakfast" },
                    ParkingIncluded = true,
                    LastRenovationDate = new DateTimeOffset(2015, 9, 20, 0, 0, 0, TimeSpan.Zero),
                    Rating = 4.80,
                    Address = new Address()
                    {
                        StreetAddress = "3393 Peachtree Rd",
                        City = "Atlanta",
                        StateProvince = "GA",
                        PostalCode = "30326",
                        Country = "USA"
                    }
                }
            ),
            IndexAction.Upload(
                new Hotel()
                {
                    HotelId = "4",
                    HotelName = "Sublime Cliff Hotel",
                    Description = "Sublime Cliff Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 1800 palace.",
                    DescriptionFr = "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.",
                    Category = "Boutique",
                    Tags = new[] { "concierge", "view", "24-hour front desk service" },
                    ParkingIncluded = true,
                    LastRenovationDate = new DateTimeOffset(1960, 2, 06, 0, 0, 0, TimeSpan.Zero),
                    Rating = 4.6,
                    Address = new Address()
                    {
                        StreetAddress = "7400 San Pedro Ave",
                        City = "San Antonio",
                        StateProvince = "TX",
                        PostalCode = "78216",
                        Country = "USA"
                    }
                }
            ),
        };
    
        var batch = IndexBatch.New(actions);
    
        try
        {
            indexClient.Documents.Index(batch);
        }
        catch (IndexBatchException e)
        {
            // When a service is under load, indexing might fail for some documents in the batch. 
            // Depending on your application, you can compensate by delaying and retrying. 
            // For this simple demo, we just log the failed document keys and continue.
            Console.WriteLine(
                "Failed to index some of the documents: {0}",
                String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
        }
    
        // Wait 2 seconds before starting queries 
        Console.WriteLine("Waiting for indexing...\n");
        Thread.Sleep(2000);
    }
    

    初始化 IndexBatch 对象后,即可通过对 SearchIndexClient 对象调用 Documents.Index,将其发送到索引。Once you initialize theIndexBatch object, you can send it to the index by calling Documents.Index on your SearchIndexClient object. DocumentsSearchIndexClient 的属性,后者提供在索引中添加、修改、删除或查询文档的方法。Documents is a property of SearchIndexClient that provides methods for adding, modifying, deleting, or querying documents in your index.

    Index 方法的调用周围的 try/catch 捕获索引失败,如果服务负载过大,可能发生索引失败。The try/catch surrounding the call to the Index method catches indexing failures, which might happen if your service is under heavy load. 在生产代码中,可以延迟已失败的文档索引编制并重试,也可以像示例那样记录并继续运行,还能以满足应用程序数据一致性要求的其他方式进行处理。In production code, you could delay and then retry indexing the documents that failed, or log and continue like the sample does, or handle it in some other way that meets your application's data consistency requirements.

    2 秒的延迟可对索引编制进行补偿(这是异步操作),这样可在执行查询之前对所有文档编制索引。The 2-second delay compensates for indexing, which is asynchronous, so that all documents can be indexed before the queries are executed. 以延迟方式编写代码通常仅在演示、测试和示例应用程序中是必要的。Coding in a delay is typically only necessary in demos, tests, and sample applications.

  2. 在 Program.cs 的 main 中,取消注释“2 - 加载文档”行。In Program.cs, in main, uncomment the lines for "2 - Load documents".

    // Uncomment next 3 lines in "2 - Load documents"
    ISearchIndexClient indexClient = serviceClient.Indexes.GetClient(indexName);
    Console.WriteLine("{0}", "Uploading documents...\n");
    UploadDocuments(indexClient);
    
  3. 按 F5 可重新生成应用。Press F5 to rebuild the app.

    如果成功构建项目,则会打开控制台窗口,在其中写入状态消息,这次是有关上传文档的消息。If the project builds successfully, a console window opens, writing status messages, this time with a message about uploading documents. 在 Azure 门户的搜索服务“概述”页面中,hotels-quickstart 索引现在应该具有 4 个文档。In the Azure portal, in the search service Overview page, the hotels-quickstart index should now have 4 documents.

有关文档处理的详细信息,请参阅“.NET SDK 如何处理文档”For more information about document processing, see "How the .NET SDK handles documents".

3 - 搜索索引3 - Search an index

对第一个文档编制索引后,可立即获取查询结果,但索引的实际测试应等到对所有文档编制索引后进行。You can get query results as soon as the first document is indexed, but actual testing of your index should wait until all documents are indexed.

此部分添加了两个功能:查询逻辑和结果。This section adds two pieces of functionality: query logic, and results. 对于查询,请使用 Search 方法。For queries, use the Search method. 此方法采用搜索文本以及其他参数This method takes search text as well as other parameters.

DocumentsSearchResult 类表示结果。The DocumentsSearchResult class represents the results.

  1. 在 Program.cs 中,创建 WriteDocuments 方法,该方法用于将搜索结果输出到控制台。In Program.cs, create a WriteDocuments method that prints search results to the console.

    private static void WriteDocuments(DocumentSearchResult<Hotel> searchResults)
    {
        foreach (SearchResult<Hotel> result in searchResults.Results)
        {
            Console.WriteLine(result.Document);
        }
    
        Console.WriteLine();
    }
    
  2. 创建 RunQueries 方法,该方法用于执行查询并返回结果。Create a RunQueries method to execute queries and return results. 结果是 Hotel 对象。Results are Hotel objects. 可使用 select 参数来显示各个字段。You can use the select parameter to surface individual fields. 如果 select 参数中不包含某个字段,则其对应的 Hotel 属性将为 null。If a field is not included in the select parameter, its corresponding Hotel property will be null.

    private static void RunQueries(ISearchIndexClient indexClient)
    {
        SearchParameters parameters;
        DocumentSearchResult<Hotel> results;
    
        // Query 1 
        Console.WriteLine("Query 1: Search for term 'Atlanta' with no result trimming");
        parameters = new SearchParameters();
        results = indexClient.Documents.Search<Hotel>("Atlanta", parameters);
        WriteDocuments(results);
    
        // Query 2
        Console.WriteLine("Query 2: Search on the term 'Atlanta', with trimming");
        Console.WriteLine("Returning only these fields: HotelName, Tags, Address:\n");
        parameters =
            new SearchParameters()
            {
                Select = new[] { "HotelName", "Tags", "Address" },
            };
        results = indexClient.Documents.Search<Hotel>("Atlanta", parameters);
        WriteDocuments(results);
    
        // Query 3
        Console.WriteLine("Query 3: Search for the terms 'restaurant' and 'wifi'");
        Console.WriteLine("Return only these fields: HotelName, Description, and Tags:\n");
        parameters =
            new SearchParameters()
            {
                Select = new[] { "HotelName", "Description", "Tags" }
            };
        results = indexClient.Documents.Search<Hotel>("restaurant, wifi", parameters);
        WriteDocuments(results);
    
        // Query 4 -filtered query
        Console.WriteLine("Query 4: Filter on ratings greater than 4");
        Console.WriteLine("Returning only these fields: HotelName, Rating:\n");
        parameters =
            new SearchParameters()
            {
                Filter = "Rating gt 4",
                Select = new[] { "HotelName", "Rating" }
            };
        results = indexClient.Documents.Search<Hotel>("*", parameters);
        WriteDocuments(results);
    
        // Query 5 - top 2 results
        Console.WriteLine("Query 5: Search on term 'boutique'");
        Console.WriteLine("Sort by rating in descending order, taking the top two results");
        Console.WriteLine("Returning only these fields: HotelId, HotelName, Category, Rating:\n");
        parameters =
            new SearchParameters()
            {
                OrderBy = new[] { "Rating desc" },
                Select = new[] { "HotelId", "HotelName", "Category", "Rating" },
                Top = 2
            };
        results = indexClient.Documents.Search<Hotel>("boutique", parameters);
        WriteDocuments(results);
    }
    

    有两种方法可用于在查询中匹配术语:全文搜索和筛选器。There are two ways of matching terms in a query: full-text search, and filters. 全文搜索查询会在索引的 IsSearchable 字段中搜索一个或多个术语。A full-text search query searches for one or more terms in IsSearchable fields in your index. 筛选器是布尔表达式,该表达式通过索引中的 IsFilterable 字段求值。A filter is a boolean expression that is evaluated over IsFilterable fields in an index. 可以配合使用全文搜索和筛选器,也可以单独使用。You can use full-text search and filters together or separately.

    使用 Documents.Search 方法可同时执行搜索和查询。Both searches and filters are performed using the Documents.Search method. 搜索查询可在searchText 参数中传递,而筛选表达式则可在 SearchParameters 类的 Filter 属性中传递。A search query can be passed in the searchText parameter, while a filter expression can be passed in the Filter property of the SearchParameters class. 若要筛选但不搜索,只需传递 "*" 作为 searchText 参数。To filter without searching, just pass "*" for the searchText parameter. 若要搜索但不筛选,只需不设置 Filter 属性,或不在 SearchParameters 实例中传递。To search without filtering, just leave the Filter property unset, or do not pass in a SearchParameters instance at all.

  3. 在 Program.cs 的 main 中,取消注释“3 - 搜索”行。In Program.cs, in main, uncomment the lines for "3 - Search".

    // Uncomment next 2 lines in "3 - Search an index"
    Console.WriteLine("{0}", "Searching documents...\n");
    RunQueries(indexClient);
    
  4. 该解决方案现已完成。The solution is now finished. 按 F5 可重新生成应用并完整运行该程序。Press F5 to rebuild the app and run the program in its entirety.

    输出包含与以前相同的消息,并添加了查询信息和结果。Output includes the same messages as before, with addition of query information and results.

清理资源Clean up resources

在自己的订阅中操作时,最好在项目结束时确定是否仍需要已创建的资源。When you're working in your own subscription, it's a good idea at the end of a project to identify whether you still need the resources you created. 持续运行资源可能会产生费用。Resources left running can cost you money. 可以逐个删除资源,也可以删除资源组以删除整个资源集。You can delete resources individually or delete the resource group to delete the entire set of resources.

可以使用左侧导航窗格中的“所有资源”或“资源组”链接 ,在门户中查找和管理资源。You can find and manage resources in the portal, using the All resources or Resource groups link in the left-navigation pane.

如果使用的是免费服务,请记住只能设置三个索引、索引器和数据源。If you are using a free service, remember that you are limited to three indexes, indexers, and data sources. 可以在门户中删除单个项目,以不超出此限制。You can delete individual items in the portal to stay under the limit.

后续步骤Next steps

在此 C# 快速入门中,你已完成一系列任务,即创建索引、使用文档加载索引并运行查询。In this C# quickstart, you worked through a series of tasks to create an index, load it with documents, and run queries. 在不同的阶段,我们采用快捷方式来简化代码,从而实现可读性和可理解性。At different stages, we took shortcuts to simplify the code for readability and comprehension. 如果你已理解这些基本概念,我们建议阅读下一篇文章,探索替代方法和概念,进一步加深你的理解。If you are comfortable with the basic concepts, we recommend the next article for an exploration of alternative approaches and concepts that will deepen your knowledge.

示例代码和索引是此快速入门的扩展版本。The sample code and index are expanded versions of this one. 下一个示例将添加“房间”集合,使用不同的类和操作,并进一步介绍处理的工作原理。The next sample adds a Rooms collection, uses different classes and actions, and takes a closer look at how processing works.