65.9K
CodeProject 正在变化。 阅读更多。
Home

构建云启用搜索设备

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2019年8月7日

CPOL

9分钟阅读

viewsIcon

16046

Azure Functions 是一个功能极其丰富的操作平台。dtSearch 工具能让我们在任何需要智能搜索功能的地方构建和管理搜索索引。

当你想到你电脑、Mac 或手机上的所有文档时,你知道有大量的文件散落各处,你或者需要 meticulously 地组织它们,或者需要能够快速搜索,以便轻松快捷地找到所需文件。我知道我的 OneDrive 和 iCloud Drive 里散落着照片、PDF 和 Microsoft Office 文档,里面包含各种各样的材料,如果我不记得我存放它们的层级结构,我就什么也找不到了。

如果有一种更简单的方法,既能将我的文档存储在云端进行备份,又能获得和本地存储文档时一样强大的搜索功能,那会怎么样?这就是 dtSearch 和 Azure Functions 可以结合起来,为在云端管理文档及其内容提供出色体验的地方。

为什么选择 Azure Functions 和存储

Azure 存储是一项出色的服务,它能跨区域地为您的文档和数据保留多个备份。您可以选择将数据安全地存储在美国东海岸的数据中心,但同时它也存在于像北欧和巴西等地的另外两个数据中心。

Azure Functions 允许我们编写少量代码,这些代码可以在适当的时候被管理和运行,以与那些存储位置交互并为我们执行服务。这让我们作为应用程序的作者和维护者,不必去操心管理系统和应用程序的运行时。我们只期望我们的函数在被触发运行时能够运行。不多也不少。

对于 dtSearch 和我们的数据来说,这是一个完美的组合。我们可以构建我们的存储位置,使其能够妥善地存放我们想要用搜索引擎处理的文档存档,并且当有新文档添加到集合中时,我们也可以适时地触发搜索索引的重新生成。Azure Function 架构还允许我们通过一个 HTTP 端点使查询该托管搜索索引变得轻而易举,这个端点可以从世界任何地方访问。

Azure Functions 入门

Azure Functions 是一种简单、低开销、基于消耗量的方式来与我们的数据进行交互。其“无服务器”架构意味着我们可以专注于编写高效的函数来处理请求和数据。对于这个搜索设备,我首先在 Visual Studio 2019 中创建了一个 Azure Functions v2.0 应用程序。这些项目使用 .NET Core 编写,并且也可以引用 .NET Standard 库。在 Visual Studio 的初始配置界面中,我选择了从 Blob 触发器配置开始。

图 1 使用 Blob 触发器配置新的 Azure Functions 项目

我保留了右侧其他设置的默认值,并创建了项目。通过选择“Blob 触发器”,我们可以在新文件被加载到 Azure Blob 存储时激活一个方法。明白这是怎么回事了吧?让我们来编写一个简单的方法,从 Blob 存储中获取新文档,并准备将它们添加到 dtSearch 引擎中。

[FunctionName("AddDoc")]
public static void AddDocument(
	[BlobTrigger("docstoindex/{name}", Connection = "AzureWebJobsStorage")]Stream myBlob, 
	string name, ILogger log)
{

	var fileNameParts = name.Split('.');
	var newFileName = string.Concat(fileNameParts[0], "_", DateTime.UtcNow.ToString("yyyyMMddHHmmss"), ".", fileNameParts[1]);
	var targetStream = new FileStream(Path.Combine(DocsFolder, newFileName), FileMode.Create);
	using (var sw = new BinaryWriter(targetStream))
	{
		var sr = new BinaryReader(myBlob);
		while (myBlob.Position != myBlob.Length)
		{
			sw.Write(sr.ReadByte());
		}
		sw.Flush();
	}
	targetStream.Close();
	targetStream.Dispose();

	log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");

}

我们来分解一下。这个方法上的 FunctionName 特性定义了 Azure 将用来引用此方法的名称。参数列表中的 BlobTrigger 提示表明,当一个 blob 到达名为 `AzureWebJobsStorage` 的连接中定义的 "docstoindex" 文件夹位置时,将启动此方法,并且该 blob 的内容会在名为 `myBlob` 的 Stream 中可用。文件名在名为 ‘name’ 的输入参数中提供,并且还提供了一个用于 Azure Functions 的记录器。

此方法将识别文件名,并在文件扩展名前添加一个日期戳。然后,它会将文件复制到由 DocsFolder 属性标识的 Azure 文件存储位置。DocsFolder 属性通过以下语法进行识别和管理:

private static string GetFolder(string folderName)
{

	var baseFolder = Environment.ExpandEnvironmentVariables(@"%HOME%\data\dtSearch");
	var fullPath = Path.Combine(baseFolder, folderName);
	if (!Directory.Exists(fullPath)) Directory.CreateDirectory(fullPath);

	return fullPath;

}

private static string DocsFolder { get {

	if (string.IsNullOrEmpty(_DocsFolder)) _DocsFolder = GetFolder("Docs");

	return _DocsFolder;
} }

当您部署 Azure Function 应用程序时,会挂载一个 Azure 存储帐户,您可以在 %HOME%\data 文件夹中处理其内容。在这种情况下,我们为 dtSearch 创建了一个文件夹,并在其下配置了一个 Docs 文件夹,用于存放我们将要索引的文档。

构建索引,并在 Azure 上运行原生代码

文档上传后,我们如何构建 dtSearch 索引?dtSearch 引擎是用原生代码编写的,并提供了一个 .NET Standard 库。我们可以将 Azure 配置为在 Windows 服务上运行我们的函数,并且我们可以通过在 csproj 文件中进行一些手动编辑来引用适用于该平台的 dtSearch 库。对于我们的项目,我们已经复制了 dtSearch 引擎 SDK 中用于 .NET Standard 的文件夹,并将其内容放在了我们项目旁边的 lib 文件夹中。

<ItemGroup>
<Reference Include="dtSearchNetStdApi">
<HintPath>..\lib\engine\netstd\dtSearchNetStdApi.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
	<Content Include="..\lib\engine\win\x86\dtSearchEngine.dll">
		<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
	</Content>
</ItemGroup>

在撰写本文时,Windows 服务器上的 Azure Functions 运行时只能加载并使用 32 位的原生 DLL。Azure 团队根本不保证对原生库的支持,但在我们的案例中(剧透一下),dtSearchEngine.dll 文件可以正常加载和运行。这两个 ItemGroup 元素允许我们的 .NET Core 代码引用 dtSearch 的 .NET Standard 包装库,而该库又引用了在第二个 ItemGroup 中有条件包含的原生 DLL。如果我们也需要部署到 Mac 或 Linux,那么还会有额外的元素,带有对那些操作系统的原生库的条件引用。

在正确引用了搜索引擎后,我们可以在 Azure Functions 项目中添加一个函数,该函数将分析我们之前代码示例中引用的 DocsFolder 的内容,并构建一个我们可以用来提供搜索结果的 IndexFolder

[FunctionName("BuildIndex")]
public static async Task BuildIndex(
[QueueTrigger("buildindex", Connection = "AzureWebJobsStorage" )]string queueItem,
ILogger log)
{

	var indexPath = GetFolder("Index");
	log.LogInformation("Building index at " + indexPath);
	log.LogInformation("Building index with documents from " + DocsFolder);

	using (var job = new IndexJob())
	{
		job.IndexPath = indexPath;
		job.ActionCreate = true;
		job.ActionAdd = true;
		job.FoldersToIndex.Add(DocsFolder);
		job.Execute();
	}

	log.LogInformation("Completed Indexing");
}

这一次,我们将根据名为“buildindex”的队列中出现条目来触发这个“BuildIndex”函数。一旦有条目到达,我们忽略队列中的消息内容,并启动一个 dtSearch IndexJob,将内容写入索引文件夹。dtSearch 引擎支持在构建索引的同时进行搜索,所以我们不必担心锁或管理索引文件夹的状态。将文档上传与索引构建分开意味着我们可以上传许多文档,并在最后一个文档上传后一次性构建索引。

现在我们有了一个索引,如何搜索它呢?Azure Functions 也可以作为 HTTP 端点被触发。你可以通过浏览器访问该函数并触发其执行,或者你可以使用 HttpClient 从该函数的位置获取数据。我们将按如下方式构建搜索函数:

[FunctionName("Search")]
public static IActionResult Search([HttpTrigger] HttpRequest request, ILogger log)
{

	if (request.Query["t"].Count == 0) return new NotFoundResult();

	var queryTerm = request.Query["t"].ToString();

	var results = new SearchResults();
	using (var job = new SearchJob())
	{

		job.Request = queryTerm;
		job.MaxFilesToRetrieve = 10;
		job.IndexesToSearch.Add(IndexFolder);
		job.SearchFlags = SearchFlags.dtsSearchDelayDocInfo;
		job.Execute(results);

	}

	log.LogInformation($"Searching for '{queryTerm}' and total hits found: " + results.TotalHitCount);
	if (results.TotalHitCount == 0) return new NotFoundResult();

	return new ContentResult { Content = results.SerializeAsXml(), ContentType = @"text/xml", StatusCode = 200 };

}

这个 Search 方法由 HttpTrigger 提示触发,专门寻找一个名为 "t" 的查询字符串参数。我们可以使用像 "https://dtsearchsample.azurewebsites.net/api/SearchIndex?t=OCR" 这样的请求格式来在我们的索引中搜索术语 OCR。如果我们使用 Azure Function 密钥来保护函数,我们还可以在该 URL 的末尾添加 "&code=<security code>"。

执行了一个标准的 dtSearch SearchJob,最多查找10个文件返回,并在我们上一个函数中定义的 IndexFolder 中进行搜索。该作业被执行,结果以 XML 格式返回。

图 2 来自 dtSearch 引擎的 XML 格式搜索结果

在 Azure 上进行配置和部署

将我们的函数部署到 Azure 是一个多步骤的过程,可以通过 Azure 门户轻松完成。首先,搜索并创建一个“函数应用”(Function App)。我们已经用以下详细信息填写了所需的配置:

图 3 创建 Azure 函数应用

对于这个项目,我们需要确保创建一个托管在 Windows 操作系统上的函数应用。如果我们想部署到 Linux,我们就需要包含 Linux 原生的 dtSearchEngine.so 库。你还会注意到为托管 Azure Functions 使用的文件创建了新的存储计划。我们的搜索索引和被索引的文件将存放在这里。

点击底部的“创建”,几分钟后,Azure 函数应用程序就会被创建好。创建完成后,我们需要在“配置”设置中仔细检查应用程序的位数。点击进入“常规设置”视图,并确认平台设置为 32 位。

图 4 Azure 函数配置

图 5 平台位数设置

接下来,在 Azure 门户中函数应用的主视图上点击“获取发布配置文件”链接。然后我们可以通过点击“生成-发布…”主菜单项,从 Visual Studio 开始发布应用程序。

图 6 从 Visual Studio 初始发布 Azure 函数的对话框

忽略屏幕中间的文本,点击左下角的“导入配置文件”按钮,加载你在上一步下载的配置文件。这将为 Visual Studio 提供将你的项目上传到 Azure 所需的一切信息。点击“完成”按钮,片刻之后,你的应用程序就会愉快地在云端运行了。

函数运行后,我们可以通过编程方式上传到我们刚刚配置的 Azure Blob 存储帐户,或者通过在 Azure 门户中浏览到该位置并使用那里的上传功能来添加要索引的文档。

图 7 直接上传文档到 Azure Blob

一旦我们的文档上传完毕,我们就可以通过向我们函数正在监视的“buildindex”队列中添加消息来触发索引的构建。你可以再次通过编程、使用工具,甚至通过 Azure 门户直接导航到该队列并点击“添加消息”按钮来完成此操作。

图 8 队列的“添加消息”按钮

添加该队列消息将导致索引被构建并对我们的消费者可用。然后我们就可以搜索索引,并构建客户端来消费来自 dtSearch 引擎的 XML 搜索结果。

摘要

Azure Functions 是一个功能极其丰富的操作平台。dtSearch 工具能让我们在任何需要智能搜索功能的地方构建和管理搜索索引。这两种技术的集成为我们提供了一种快速愉悦的搜索体验,任何连接到互联网的客户端都可以访问。

更多关于 dtSearch
dtSearch.com
口袋里的搜索引擎 – 介绍 Android 上的 dtSearch
云端疾速源代码搜索
使用 Azure 文件、RemoteApp 和 dtSearch,从任何计算机或设备跨越 PB 级数据的各种数据类型进行安全即时搜索
使用 dtSearch 引擎进行 Windows Azure SQL 数据库开发
使用 dtSearch 进行分面搜索 - 不是普通的搜索过滤器
使用 dtSearch® ASP.NET Core WebDemo 示例应用程序极速提升您的搜索体验
在您的 Windows 10 通用 (UWP) 应用程序中嵌入搜索引擎
使用 dtSearch Engine DataSource API 索引 SharePoint 网站集
使用 dtSearch® ASP.NET Core WebDemo 示例应用程序
在 AWS 上使用 dtSearch(EC2 & EBS)
使用 dtSearch 和 AWS Aurora 进行全文搜索

© . All rights reserved.