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

云端疾速源代码搜索

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2015 年 4 月 7 日

CPOL

10分钟阅读

viewsIcon

34490

这篇博客文章展示了如何利用 dtSearch 对安全存储在 Microsoft Azure 云中的数据执行快速搜索。

引言

使用 dtSearch 和本文中的技术将使您的数据搜索快如闪电,从而能够在亚秒级响应时间内搜索数 TB 的数据。

但首先,关于这篇博客文章有两点初步说明。(1) 这篇博客文章描述了源代码数据,但相同的方法也适用于存储在 Microsoft Azure 云中的其他数据:HTML、XML、MS Office 文档,甚至是电子邮件数据。(2) 虽然这篇博客文章中的数据驻留在 Microsoft Azure 云中,但索引位于本地 PC 上。后续文章将探讨云中的数据和索引。

这是我们整个项目的工作计划

整体工作计划

在本文的第一部分,我们将进入 Azure 门户并预置存储帐户。当然,前提是您已经注册了 Azure 帐户。如果您还没有,注册免费试用版相对容易,这样您就可以在投入资金之前查看它是否满足您的需求。

预置存储帐户后,访问密钥将自动生成。这些访问密钥将复制到我们的 Visual Studio 项目中,因为它们是授予对存储帐户特权访问的秘密密钥,我们将把要索引和稍后搜索的源代码复制到存储帐户中。

本文的第二部分将向您展示可以在哪里获取带有入门代码的 Visual Studio 解决方案。该解决方案将大大减少我们实际需要做的工作量,以实现这个有用的源代码搜索应用程序。如果您安装了 dtSearch Engine 的完整版,入门项目实际上会安装在您的程序文件文件夹中。

我们将使用带有最新更新的 Visual Studio 2013。我们还将安装最新的 Azure 存储 SDK 二进制文件。

真正的任务始于第三部分。我们在这里要做的是构建将源代码上传到存储帐户的功能。您可以下载各种实用程序来执行将源代码上传到存储帐户的任务,但如果我们可以将其构建到我们的主搜索应用程序中,那将更加方便。完成此改造和升级后,我们就可以运行应用程序上传源代码,对其进行索引,然后进入我们工作计划的第四部分。

第四部分将快速而轻松,因为我们已经基本完成了困难的工作。第四部分是关于测试和打包我们的应用程序。生成的索引文件可以复制到其他客户端计算机。这意味着我们可以将应用程序连同生成的索引文件复制到任何计算机上,以执行闪电般的源代码搜索。

第 1 部分 - 在 Azure 门户进行预置

预置存储帐户实际上非常简单。在撰写本文时,传统的 Azure 门户是首选之地。但到 2015 年 5 月的第一周之后,Microsoft 将发布新门户。

2015 年 5 月 5 日之前的门户 http://manage.windowsazure.com/
2015 年 5 月 5 日之后的新门户 http://portal.azure.com/

登录 Azure 门户后,只需导航到“存储”菜单项并单击“新建”即可。

在 Azure 门户预置存储帐户

“快速创建”菜单项将变为可见。单击它以继续。

此时,您已准备好提供 URL、位置和复制模式。您想到的 URL 需要在全球范围内是唯一的。如您所见,“mysourcecode”没有被占用。我选择“美国东部”作为我的位置,但您可以从世界各地的数据中心中选择。更近的数据中心意味着更低的延迟。您可以在此处阅读有关复制选项的信息:http://blogs.msdn.com/b/windowsazurestorage/archive/2013/12/11/introducing-read-access-geo-replicated-storage-ra-grs-for-windows-azure-storage.aspx

完成后,单击右下角的“创建存储帐户”。预置存储帐户应该不到五分钟。我做的时候不到一分钟。

创建存储帐户

当门户指示您的存储帐户处于“联机”状态时,您就可以继续了。单击指向右侧的小箭头以深入了解此新预置的存储帐户的详细信息。

已预置的存储帐户

您现在可以复制访问密钥到剪贴板。单击“管理访问密钥”。

复制访问密钥

点击红色框的图标将“主访问密钥”复制到剪贴板,并将其与“存储帐户名称”一起安全存储。您的“存储帐户名称”和“主访问密钥”都将与您在此处看到的有所不同。

复制存储账户名称和主访问密钥
存储账户名称 mysourcecode
主访问密钥 CnQ6dUXdOQ81qSCFJhscuB3PCNM92o4bIuDoKG7mO
7tJ1imxa5sMkzKtnghsG11EwKgxRaTW5g6fFKRcXZ8z6g==

第 2 部分 - 定位入门项目

dtSearch Engine 附带的入门项目可以在此程序文件文件夹中找到

  • C:\Program Files (x86)\dtSearch Developer\examples\cs4\AzureBlobDemo\AzureBlobDemo.sln

入门项目为我们开始工作提供了一个极好的起点。请确保您正在使用已安装所有最新更新的 Visual Studio 2013。

项目应该无缝打开,但我们想确保已安装最新的 Azure 存储二进制文件。我们将在 Visual Studio 的解决方案资源管理器中右键单击并选择“管理 NuGet 包”。

添加 NuGet 包

在右上角的搜索框中,输入“Azure Storage”。正如您所料,这将弹出 Windows Azure 存储客户端库,我们将使用它来读写我们即将预置的 Windows Azure 存储帐户。

使用最新 Azure 存储 SDK 更新项目

在 Visual Studio 解决方案资源管理器中,您可以展开引用节点以验证我们是否安装了存储客户端库。

验证 Azure 存储客户端库

第 3 部分 - 将存储帐户连接信息添加到 app.config

现在是时候将存储帐户信息复制到您的 app.config 文件中了。app.config 文件提供了一个方便的位置,可供您的应用程序全局访问。它将在运行时访问。要求用户每次使用应用程序时都不断提供连接信息是不合适的。

修改 App.Config

<?xml version="1.0"?>
<configuration>
  <startup>
    <supportedRuntime
        version = "v4.0"
        sku = ".NETFramework,Version=v4.0"/>
  </startup>
  <appSettings>
    <add
        key = "StorageAccountName"
        value = "mysourcecode"/>
    <add
        key = "AccessKey"
        value = "CnQ6dUXdOQ81qSCFJhscuB3PCNM92o4bIuDoKG7mO7tJ1imxa5sMkzKtnghsG11EwKgxRaTW5g6fFKRcXZ8z6g=="/>
  </appSettings>
</configuration>

加密选项

如果您想加密这些信息,这里有几个选项

添加支持将源代码上传到您的 Azure 存储帐户

我们的下一个任务是增强入门项目,以启用源代码上传。将此功能直接添加到应用程序中将大大提高可用性。在本节中,我们将添加一个命令按钮,然后编写一些代码。

这是我们更改前的应用程序外观。这是 MainForm.cs。

在 MainForm.cs 中添加按钮之前

现在我们将添加第三个按钮,如下所示。按钮的名称是 cmdAddCode,标题(Text 属性)是 将源代码添加到 Azure 存储。您需要将索引和搜索按钮稍微向下移动,以为这个新的第三个按钮腾出空间。

在设计器中,单击将源代码添加到 Azure 存储按钮以检索代码。

在 MainForm.cs 中添加按钮之后

现在我们将添加一些代码,提供上传源代码的能力。

添加代码隐藏

重复前面步骤中的步骤以“添加引用”。我们将添加的引用是 System.Configuration。在单击“确定”之前,请确保红色框内的复选框已选中。

添加对 System.configuration 的引用

确保 MainForm.cs 的顶部有以下新的语句。

必要的 using 语句

修改 MainForm.cs

private void cmdAddCode_Click(object sender, EventArgs e)
{
    string windowTitle = this.Text;
    try
    {
        string selectedFolder = null;
        FolderBrowserDialog fDialog = new FolderBrowserDialog();
 
        //  if the user has clicked the OK button after choosing a file,To display a MessageBox with the path of the file:
        if (fDialog.ShowDialog() == DialogResult.OK)
        {
            selectedFolder = fDialog.SelectedPath.ToString();
        }
 
        string storageAccountName = ConfigurationManager.AppSettings["StorageAccountName"];
        string accessKey = ConfigurationManager.AppSettings["AccessKey"];
        string connString = string.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
            storageAccountName, accessKey);
 
        // Parse the connection string and create a client
        var storageAccount = CloudStorageAccount.Parse(connString);
        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
 
        List<FileInfo> filesToUpload = new List<FileInfo>();
        RecursiveFileUpload(selectedFolder, filesToUpload, "*.*");
        var fileUploadParallelism = new ParallelOptions() {MaxDegreeOfParallelism = 4};
 
        string blobContainerName = "code";
        blobClient = storageAccount.CreateCloudBlobClient();
        CloudBlobContainer container = blobClient.GetContainerReference(blobContainerName);
        container.CreateIfNotExists();
 
        Parallel.ForEach(filesToUpload, fileUploadParallelism, currentFileInfo =>
        {
            // Fix up the file path so it works with a blob path
            string cloudFileNamePath = currentFileInfo.FullName.Replace(@"\", @"_");
            cloudFileNamePath = cloudFileNamePath.Length == 0 ? "" : cloudFileNamePath;
            if (cloudFileNamePath.Length > 0)
            {
                if (cloudFileNamePath.Substring(0, 1).Equals("/"))
                {
                    cloudFileNamePath = cloudFileNamePath.Substring(1);
                }
            }
            try
            {
                var blobFileToUpload = container.GetBlockBlobReference(cloudFileNamePath);
                ShowTitle("Uploading..." + currentFileInfo.Name);
                if (!blobFileToUpload.Exists())
                {
                    blobFileToUpload.OpenWrite(null, null, null);
                    blobFileToUpload.UploadFromFile(currentFileInfo.FullName, FileMode.Open, null, null, null);
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show("Issue with  blob upload = " + exception.Message.ToString());
            }
 
        }
        );
 
    }
    catch (Exception ex)
    {
        throw;
    }
    finally
    {
        this.Text = windowTitle;
    }
}
delegate void StringParameterDelegate(string value);
public void ShowTitle(string value)
{
    if (InvokeRequired)
    {
        // We're not in the UI thread, so we need to call BeginInvoke
        BeginInvoke(new StringParameterDelegate(ShowTitle), new object[] { value });
        return;
    }
    // Must be on the UI thread if we've got this far
    this.Text = value;
}
private List<FileInfo> RecursiveFileUpload(string sourceDir, List<FileInfo> filesToCopy, string search_type)
{
    DirectoryInfo sDirInfo = null;
    FileInfo sFileInfo = null;
    if (!(sourceDir.EndsWith(Path.DirectorySeparatorChar.ToString())))
    {
        sourceDir += Path.DirectorySeparatorChar;
    }
    try
    {
        foreach (string sDir in Directory.GetDirectories(sourceDir))
        {
            sDirInfo = new DirectoryInfo(sDir);
            RecursiveFileUpload(sDir, filesToCopy, search_type);
            sDirInfo = null;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Issue with  RecursiveFileUpload " + ex.Message.ToString());
    }
 
    try
    {
        string[] theFiles = Directory.GetFiles(sourceDir);
        foreach (string sFile in theFiles)
        {
            if (sFile.Length >= 1024)
                continue;
 
            sFileInfo = new FileInfo(sFile);
            try
            {
                filesToCopy.Add(sFileInfo);
            }
            catch (System.IO.IOException ex)
            {
                MessageBox.Show("Skipping " + sDirInfo.FullName + " because of " + ex.Message.ToString());
            }
            sFileInfo = null;
        }
 
    }
    catch (System.UnauthorizedAccessException ex)
    {
        MessageBox.Show("Skipping " + sourceDir + " because of " + ex.Message.ToString());
    }
    catch (System.Exception ex)
    {
        MessageBox.Show("Skipping " + sourceDir + " because of " + ex.Message.ToString());
    }
    return filesToCopy;
}

BLOBDATASOURCE.CS 文件中的 `Rewind()` 方法中的一些代码需要更新。

// Fixes for BlobDataSource.cs
//
public override bool Rewind()
{
    // Check connection interaction success flag.  If an earlier attempt to
    // connect to the storage failed, then method will not be successful.
    if (_isStorageFailed)
        return false; // failure code - no documents to read           
 
    // Setup the connection to Windows Azure Storage
    try
    {
        // Parse the connection string and create a client
        var storageAccount = CloudStorageAccount.Parse(_connectionString);
        _blobClient = storageAccount.CreateCloudBlobClient();
 
        // Create (or re-create) the blob table
        _blobTable = new Dictionary<string, List<string>>();
 
        // Add all files into the blob table using the container name as the key
        foreach (CloudBlobContainer container in _blobClient.ListContainers())
        {
            // Get the BlobTable key: the container name
            string containerName = container.Name;
 
            // Get the BlobTable value: a list of blob URIs
            List<string> blobURIs = new List<string>();
 
            //List blobs and directories in this container
            var blobs = container.ListBlobs();
 
            // FIX: Used to be foreach (CloudBlob blob in container.ListBlobs())
            foreach (var blobItem in blobs)
            {
                blobURIs.Add(blobItem.Uri.ToString());
                //System.Diagnostics.Debug.WriteLine(blobItem.Uri.ToString());
            }
 
            // Add the entry to the BlobTable                          
            _blobTable.Add(containerName, blobURIs);
        }
 
        // Initialize iterators; fail if not successful
        if (!ResetIterators())
        {
            _isStorageFailed = true;
            return false;
        }
 
        // Set success
        _isStorageFailed = false;
        return true;
    }
    catch (Exception ex)
    {
        // Add diagnostic code here if desired
 
        // Set failure
        _isStorageFailed = true;
        return false;
    }
}

我们对 *AskConnectForm.cs* 进行了一些修改。

这将始终检索连接字符串,以便用户不必不断输入。理想情况下,我们可以编写一些代码来完全绕过 AskConnectForm 窗体,但我试图避免过多的修改,以使这篇帖子保持直截了当。

public AskConnectForm()
{
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent();
    // Add the code below
    string storageAccountName = ConfigurationManager.AppSettings["StorageAccountName"];
    string accessKey = ConfigurationManager.AppSettings["AccessKey"];
    string connString = string.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
        storageAccountName, accessKey);
 
    this.ConnectString.Text = connString;
}

第四部分 - 测试

我们现在准备开始测试刚刚更新的应用程序。有一件事可能很有趣,那就是验证我们是否已正确使用源代码更新了存储帐户。我运行了一次应用程序并将源代码上传到 Azure 存储,如下图所示。

您可以从以下 URL 免费下载 Azure 存储资源管理器

http://azurestorageexplorer.codeplex.com/

安装并配置 Azure 存储资源管理器后,您可以浏览容器,查找您之前可能已上传的任何源代码。它还允许您删除内容,如果您想这样做的话。

存储资源管理器等工具

虽然我们正在添加源代码,但您可以添加几乎任何文件,无论是 Word 文档还是 PowerPoint。dtSearch 将自动索引许多不同类型的文件。

顺便说一句,前面的代码是异步执行上传的,开发人员可以根据网络和系统资源控制并发级别。

请参阅代码片段

var fileUploadParallelism = new ParallelOptions() {MaxDegreeOfParallelism = 4};

单击高亮按钮,将源代码添加到您的 Azure 存储帐户。

添加代码

您可以重复此过程,选择包含要上传的源代码的文件夹。您选择的文件夹(及其子文件夹)中的所有文件也将用于使用源代码填充 Windows Azure 存储。

选择包含源代码的文件夹

创建索引时,需要一个位置来存储索引文件。

输入一个有效位置,然后点击“索引”按钮。

输入关于索引的信息并创建索引

我们已经在上面输入了必要的代码,用适当的连接字符串填充此对话框。您只需在此对话框上点击“确定”。

输入连接字符串并点击“确定”

您将在此对话框中点击两个按钮。第一个按钮是“索引 Azure 存储帐户”。第二个按钮是“搜索”。

执行索引操作,然后点击搜索

输入搜索词并点击搜索

我们的工作完成了。您现在可以针对您的 Azure 存储帐户快速搜索关键字并获得闪电般的结果。

如果您决定向 Azure 存储帐户添加更多源代码,则需要重新生成索引。

查看搜索结果

结论

您现在可以搜索数 TB 的源代码,并立即获得搜索结果。这里的一个核心优势是您不必将所有源代码都存储在您自己的笔记本电脑或台式机上。所有源代码都可以安全地存储在您的 Azure 存储帐户中,只有拥有访问密钥的人才能访问。

其他资源

更多关于 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 进行全文搜索

云端极速源代码搜索 - CodeProject - 代码之家
© . All rights reserved.