使用 Azure 搜索和 Xamarin.Forms 搜索数据

Azure 搜索是一项云服务,可为上传的数据提供索引和查询功能。 这消除了与在应用程序中实现搜索功能相关的基础结构要求和搜索算法复杂性。 本文演示了如何使用 Microsoft Azure 搜索库将 Azure 搜索集成到 Xamarin.Forms 应用程序中。

概述

数据以索引和文档的形式存储在 Azure 搜索中。 索引是 Azure 搜索服务可以搜索的数据的存储,在概念上类似于数据库表。 文档是索引中的单个可搜索数据单元,在概念上类似于数据库行。 上传文档并向 Azure 搜索提交搜索查询时,系统会向搜索服务中的特定索引发出请求。

向 Azure 搜索发出的每个请求都必须包括服务的名称和 API 密钥。 API 密钥有两种类型:

  • 管理员密钥授予所有操作的完整权限。 其中包括管理服务、创建和删除索引以及数据源。
  • 查询密钥授予对索引和文档的只读访问权限,应由发出搜索请求的应用程序使用。

对 Azure 搜索的最常见请求是执行查询。 可以提交两种类型的查询:

可以单独使用搜索查询或筛选查询,也可以一起使用。 一起使用时,先将筛选查询应用于整个索引,然后对筛选查询的结果执行搜索查询。

Azure 搜索还支持基于搜索输入检索建议。 有关详细信息,请参阅建议查询

注意

如果还没有 Azure 订阅,可以在开始前创建一个免费帐户

安装

将 Azure 搜索集成到 Xamarin.Forms 应用程序中的过程如下所示:

  1. 创建 Azure 搜索服务。 有关详细信息,请参阅使用 Azure 门户创建 Azure 搜索服务
  2. 从 Xamarin.Forms 解决方案可移植类库 (PCL) 中删除作为目标框架的 Silverlight。 这可以通过将 PCL 配置文件更改为任何支持跨平台开发但不支持 Silverlight 的配置文件(例如配置文件 151 或配置文件 92)来实现。
  3. Microsoft Azure 搜索库 NuGet 包添加到 Xamarin.Forms 解决方案中的 PCL 项目。

执行这些步骤后,可以使用 Microsoft 搜索库 API 来管理搜索索引和数据源、上传和管理文档以及执行查询。

创建 Azure 搜索索引

必须定义映射到要搜索的数据结构的索引架构。 这可以在 Azure 门户中完成,也可以使用 SearchServiceClient 类采用编程方式完成。 此类管理与 Azure 搜索的连接,也可用于创建索引。 以下代码示例演示了如何创建此类的实例:

var searchClient =
  new SearchServiceClient(Constants.SearchServiceName, new SearchCredentials(Constants.AdminApiKey));

SearchServiceClient 构造函数重载采用搜索服务名称和 SearchCredentials 对象作为参数,SearchCredentials 对象包装 Azure 搜索服务的管理员密钥。 创建索引需要管理员密钥

注意

应在应用程序中使用单个 SearchServiceClient 实例,以避免打开太多与 Azure 搜索的连接。

索引由 Index 对象定义,如以下代码示例所示:

static void CreateSearchIndex()
{
  var index = new Index()
  {
    Name = Constants.Index,
    Fields = new[]
    {
      new Field("id", DataType.String) { IsKey = true, IsRetrievable = true },
      new Field("name", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSortable = true, IsSearchable = true },
      new Field("location", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSortable = true, IsSearchable = true },
      new Field("details", DataType.String) { IsRetrievable = true, IsFilterable = true, IsSearchable = true },
      new Field("imageUrl", DataType.String) { IsRetrievable = true }
    },
    Suggesters = new[]
    {
      new Suggester("nameSuggester", SuggesterSearchMode.AnalyzingInfixMatching, new[] { "name" })
    }
  };

  searchClient.Indexes.Create(index);
}

Index.Name 属性应设置为索引的名称,并且 Index.Fields 属性应设置为 Field 对象的数组。 每个 Field 实例指定一个名称、一种类型和任何属性,用于指定字段的使用方式。 这些属性包括:

  • IsKey – 指示字段是否为索引的密钥。 索引中只有一个字段(类型为 DataType.String),必须指定为密钥字段。
  • IsFacetable – 指示是否可以在此字段中执行分面导航。 默认值为 false
  • IsFilterable – 指示是否可以在筛选查询中使用字段。 默认值为 false
  • IsRetrievable – 指示是否可以在搜索结果中检索字段。 默认值为 true
  • IsSearchable – 指示字段是否包含在全文搜索中。 默认值为 false
  • IsSortable – 指示是否可以在 OrderBy 表达式中使用字段。 默认值为 false

注意

部署索引后更改索引涉及重新生成和重新加载数据。

Index 对象可以选择指定 Suggesters 属性,该属性定义索引中用于支持自动补全或搜索建议查询的字段。 Suggesters 属性应设置为 Suggester 对象的数组,这些对象定义用于生成搜索建议结果的字段。

创建 Index 对象后,通过对 SearchServiceClient 实例调用 Indexes.Create 来创建索引。

注意

从必须保持响应的应用程序创建索引时,请使用 Indexes.CreateAsync 方法。

有关详细信息,请参阅使用 .NET SDK 创建 Azure 搜索索引

删除 Azure 搜索索引

可以通过对 SearchServiceClient 实例调用 Indexes.Delete 来删除索引:

searchClient.Indexes.Delete(Constants.Index);

将数据上传到 Azure 搜索索引

定义索引后,可以使用以下两个模型之一将数据上传到该索引:

  • 拉取模型 – 数据定期从 Azure Cosmos DB、Azure SQL 数据库、Azure Blob 存储或 Azure 虚拟机中托管的 SQL Server 引入。
  • 推送模型 – 数据以编程方式发送到索引。 这是本文中采用的模型。

必须创建一个 SearchIndexClient 实例,才能将数据导入索引。 这可以通过调用 SearchServiceClient.Indexes.GetClient 方法来实现,如以下代码示例所示:

static void UploadDataToSearchIndex()
{
  var indexClient = searchClient.Indexes.GetClient(Constants.Index);

  var monkeyList = MonkeyData.Monkeys.Select(m => new
  {
    id = Guid.NewGuid().ToString(),
    name = m.Name,
    location = m.Location,
    details = m.Details,
    imageUrl = m.ImageUrl
  });

  var batch = IndexBatch.New(monkeyList.Select(IndexAction.Upload));
  try
  {
    indexClient.Documents.Index(batch);
  }
  catch (IndexBatchException ex)
  {
    // Sometimes when the Search service is under load, indexing will fail for some
    // documents in the batch. Compensating actions like delaying and retrying should be taken.
    // Here, the failed document keys are logged.
    Console.WriteLine("Failed to index some documents: {0}",
      string.Join(", ", ex.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
  }
}

要导入索引的数据打包为一个 IndexBatch 对象,该对象封装了 IndexAction 对象集合。 每个 IndexAction 实例都包含一个文档和一个属性,该属性指示 Azure 搜索对文档执行的操作。 在上面的代码示例中,指定了 IndexAction.Upload 操作,这会导致文档如果是新的则被插入到索引中,如果已存在则被替换。 然后,通过对 SearchIndexClient 对象调用 Documents.Index 方法将 IndexBatch 对象发送到索引。 有关其他索引操作的信息,请参阅确定要使用的索引操作

注意

单个索引请求中只能包含 1000 个文档。

请注意,在上面的代码示例中,monkeyList 集合是从 Monkey 对象集合中创建为匿名对象的。 这将为 id 字段创建数据,并解析帕斯卡式大小写 Monkey 属性名称到驼峰式大小写搜索索引字段名称的映射。 或者,也可以通过将 [SerializePropertyNamesAsCamelCase] 属性添加到 Monkey 类来实现此映射。

有关详细信息,请参阅使用 .NET SDK 将数据上传到 Azure 搜索

查询 Azure 搜索索引

必须创建一个 SearchIndexClient 实例来查询索引。 当应用程序执行查询时,建议遵循最低特权原则并直接创建 SearchIndexClient,将查询密钥作为参数传递。 这样可以确保用户对索引和文档具有只读访问权限。 以下代码示例演示了此方法:

SearchIndexClient indexClient =
  new SearchIndexClient(Constants.SearchServiceName, Constants.Index, new SearchCredentials(Constants.QueryApiKey));

SearchIndexClient 构造函数重载采用搜索服务名称、索引名称和 SearchCredentials 对象作为参数,SearchCredentials 对象包装 Azure 搜索服务的查询密钥

搜索查询

可以通过对 SearchIndexClient 实例调用 Documents.SearchAsync 方法来查询索引,如以下代码示例所示:

async Task AzureSearch(string text)
{
  Monkeys.Clear();

  var searchResults = await indexClient.Documents.SearchAsync<Monkey>(text);
  foreach (SearchResult<Monkey> result in searchResults.Results)
  {
    Monkeys.Add(new Monkey
    {
      Name = result.Document.Name,
      Location = result.Document.Location,
      Details = result.Document.Details,
      ImageUrl = result.Document.ImageUrl
    });
  }
}

SearchAsync 方法采用搜索文本参数,以及可用于进一步优化查询的可选 SearchParameters 对象。 搜索查询被指定为搜索文本参数,而筛选查询可以通过设置 SearchParameters 参数的 Filter 属性来指定。 下面的代码示例演示了这两种查询类型:

var parameters = new SearchParameters
{
  Filter = "location ne 'China' and location ne 'Vietnam'"
};
var searchResults = await indexClient.Documents.SearchAsync<Monkey>(text, parameters);

此筛选查询应用于整个索引,并从结果中删除 location 字段不等于中国且不等于越南的文档。 筛选后,对筛选查询的结果执行搜索查询。

注意

若要在不搜索的情况下进行筛选,请传递 * 作为搜索文本参数。

SearchAsync 方法返回包含查询结果的 DocumentSearchResult 对象。 将枚举此对象,将每个 Document 对象创建为 Monkey 对象并添加到 MonkeysObservableCollection 中进行显示。 以下屏幕截图显示了从 Azure 搜索返回的搜索查询结果:

搜索结果

有关搜索和筛选的详细信息,请参阅使用 .NET SDK 查询 Azure 搜索索引

建议查询

通过对 Documents.SuggestAsync 实例调用 SearchIndexClient 方法,Azure 搜索允许根据搜索查询请求建议。 以下代码示例对此进行了演示:

async Task AzureSuggestions(string text)
{
  Suggestions.Clear();

  var parameters = new SuggestParameters()
  {
    UseFuzzyMatching = true,
    HighlightPreTag = "[",
    HighlightPostTag = "]",
    MinimumCoverage = 100,
    Top = 10
  };

  var suggestionResults =
    await indexClient.Documents.SuggestAsync<Monkey>(text, "nameSuggester", parameters);

  foreach (var result in suggestionResults.Results)
  {
    Suggestions.Add(new Monkey
    {
      Name = result.Text,
      Location = result.Document.Location,
      Details = result.Document.Details,
      ImageUrl = result.Document.ImageUrl
    });
  }
}

SuggestAsync 采用搜索文本参数、要使用的建议器的名称(在索引中定义)和可用于进一步优化查询的可选 SuggestParameters 对象。 SuggestParameters 实例设置以下属性:

  • UseFuzzyMatching – 如果设置为 true,则即使搜索文本中包含替代或缺少的字符,Azure 搜索也会查找建议。
  • HighlightPreTag – 追加到建议命中前面的标记。
  • HighlightPostTag – 追加到建议命中的标记。
  • MinimumCoverage – 表示建议查询报告成功时必须涵盖的索引百分比。 默认值为 80。
  • Top – 要检索的建议数。 它必须是介于 1 和 100 之间的整数,默认值为 5。

总体效果是,索引中的前 10 个结果将返回并突出显示命中,并且结果将包括包含拼写相似的搜索词的文档。

SuggestAsync 方法返回包含查询结果的 DocumentSuggestResult 对象。 将枚举此对象,将每个 Document 对象创建为 Monkey 对象并添加到 MonkeysObservableCollection 中进行显示。 以下屏幕截图显示了从 Azure 搜索返回的建议结果:

建议结果

请注意,在示例应用程序中,仅在用户完成输入搜索词时调用 SuggestAsync 方法。 不过,还可通过在每次按键时执行此方法来支持自动补全搜索查询。

总结

本文演示了如何使用 Microsoft Azure 搜索库将 Azure 搜索集成到 Xamarin.Forms 应用程序中。 Azure 搜索是一项云服务,可为上传的数据提供索引和查询功能。 这消除了与在应用程序中实现搜索功能相关的基础结构要求和搜索算法复杂性。