详细介绍基于MySQL的搜索引擎MySQL-Fullltext

本文涵盖了一个简单的C实现的搜索引擎的搭建始末。

我通常使用SQL Server和C #,但我教C/C++的朋友要远离微软。在过去,MySQL不是我想要的数据库,因为标准安装版不支持事务,但它变得越来越成熟。我使用64位InnoDB引擎的MySQL 5.6,使用Unicode(utf8)编码,这是我新数据库的默认设置。

Freetext是InnoDB的新特征,它在MySQL5.6版中被首次推出。

与C相比我通常更喜欢C++,即使在小项目中:不用知道所有的函数名,而且有一些内置的常用操作和漂亮的IntelliSense支持。在C++中,还有有STL及集合和字符串助手。

C++的Mysql接口比较弱,而C的接口很成熟,所以我决定使用C接口。

C的dll文件是和WCF一起发布的,以便完成AJAX请求,在Visual Studio Ultimate 2012中我使用C#的"WCF Service Application"模板,我搜索了使用C++搭建WebService的方法,但只找到一些使用C++处理WebServices调用的例子。

用户界面是一个使用Jquery和Jquery-UI自动提示的HTML界面,页面被增加到"WCF服务应用",项目被命名为VisionWeb

网页看起来是这样的:

我在.NET框架4.0,64位系统上配置这个项目,如果你使用32位的Mysql服务器,你必需随之做些更改。记得设置UNICODE选项为默认值。

配置MySQL

你有可能会从VisionSmall中打开这个VisionDAL项目, 假定你必须修改连接MySQL的C程序接口. 在这儿,我介绍了如何在新项目中安装MySQL接口: 检查那些设置是否符合你的要求,尤其是mysql.lib文件和VisionDAL.dll的路径.

在Visual Studio中,添加一个VisionDAL工程, 通过这个流程"Other Languages/Visual C++/Empty Project". 在这之中, 你只需要改变"应用类型" 为DLL. 把VisionDAL.cpp改名为VisionDAL.c, 这就清楚的告诉Visual Studio把编译器从C++改为C. 给这个工程添加一个头文件命名为VisionDAL.h.

在窗口中, 右击VisionDAL工程并选择属性. 然后在"配置属性"/Linker/Input, 选择 "Additional Dependencies" 并且添加libmysql.lib 到这个路径, 不要忘记了分隔符 ";".

在 "配置属性"/Linker/General这个菜单下, 选择"添加库目录" ,对我来说就是添加 C:\Program Files\MySQL\MySQL Server 5.6\lib>这个目录. 现在我们已经连接到C接口, 但是在libmysql.lib中调用执行的DLL必须是系统的可执行路径: 从控制面板, 选择系统, 点击 "高级系统设置", "点出环境变量" 在 "系统变量"下面,选择路径, 并添加这个 libmysql.lib 的路径 (DLL和这个lib文件在相同的文件夹里): C:\Program Files\MySQL\MySQL Server 5.6\lib.

我们也需要把这个VisionDal.dll放到我们的path路径里, IIS 并不能从这个网站的bin目录中取到DLL文件. 添加 <项目路径>/x64/debug 到路径变量path里. 重启后生效. 当网站得到一个request请求时将会加载VisionDAL.dll; 如果你现在重建项目, 你会得到一个VisionDAL.dll的写入错误: 为了解决它, 重启该网站或是用unlocker之类的解锁.

如果需要指定VisonDAL的包含属性. 在 "配置属性"/"C/C++" 菜单下添加MYSQL的头文件路径, 例如像这样: C:\Program Files\MySQL\MySQL Server 5.6\include.

下面我们在“C/C++”/"预编译头"菜单栏中,从“预编译头”切换到“不使用预编译头”,设置Preproccessor定义防止使用strcpy和fopen时产生的错误消息:在"C/C++"/预编译器/"预编译器定义 "中设定SE_STANDARD_FILE_FUNCTIONS和_CRT_SECURE_NO_WARNINGS。

当你现在连接,mysqllib引用的问题并没有解决,因为它们是64位处理器。通过在VisionDal中打开工程属性,选择“配置管理”,然后设置为x64平台。

现在我们来创建名为 Vision 的样本数据库

打开SQL Development 中的 MySql 工作台,打开你的实例。将会出现一个新窗口 "SQL File 1" 。 双击VisionDAL项目中的 Sql.txt 文件。复制所有内容到剪贴板,粘贴到工作台中的"SQL File 1"窗口。 点击螺栓图标(左边第三个图标),创建样本数据库。
接下来我们需要用来数据库登录的通用信息。

我们有一个关于此的配置文件: <installation director>VisionSmall\x64\Debug\VisionConfiguration.txt, 看起来像这样:
 

代码如下:

Host: localhost
User: root
Password: frob4frob
Database: vision
Port: 3306

修改这些数值以匹配你的SQL-Configuration。

Vision 数据库

数据库中只有一张表

CREATE TABLE 'document' (
 'DocumentID' int(11) NOT NULL AUTO_INCREMENT,
 'Title' varchar(255) DEFAULT NULL,
 'Text' text,
 PRIMARY KEY ('DocumentID'),
 FULLTEXT KEY 'ft' ('Title','Text'),
 FULLTEXT KEY 'ftTitle' ('Title')
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

搜索的时候我们使用名为'ft'的全文索引,查找自动完成单词的时候我们使用名为'ftTitle'的全文索引。

如果你拥有一个很多字段的全文索引,你可以在Microsoft SQL Server中选择,查询的时候,哪个字段被包含进搜索。在MySQL中,通常全文索引的所有字段都被搜索,所以我们必须指定额外的全文索引'ftTitle'。

通过C接口进行MySQL查询
首先呢,为了执行查询我们需要连接到数据库并取得一个MYSQL的指针:

  MYSQL *Connect(){
  MYSQL *conn; // Connection

  // 连接到MySQL
  conn = mysql_init(NULL);
  if(mysql_real_connect(
    conn, Configuration.Host, Configuration.User, Configuration.Password,
    Configuration.Database, Configuration.Port, NULL, 0) == NULL) {
      fprintf(stderr, "sorry, no database connection ...\n");
      return NULL;
  }
  return conn;
}

在启动的时候我们把VisionConfiguration.txt文件里的变量赋值到全局变量, 这个文件应该和我们的程序在同一目录. 这是一个例行操作.获取当前运行程序目录是通过Win32 API的GetModuleFileName函数,如下:

TCHAR *GetExecutablePath(){
  TCHAR *pBuf = (TCHAR *)malloc(512);
  int bytes = GetModuleFileName(NULL, pBuf, 255);
  if(bytes == 0)
    return NULL;
  else
    return pBuf;
}

这里只有一个程序我们想要说明:GetDocuments. 在头文件定义:

#define FORMAT_TEXT 0
#define FORMAT_JSON 1

__declspec(dllexport) TCHAR*   __cdecl GetDocuments(TCHAR *search, int format, int forAutocomplete);
在资源文件中定义:
 
__declspec(dllexport) TCHAR* GetDocuments(TCHAR *search, int format, int forAutocomplete)

__declspec(dllexport)的声明和定义实现是通过添加到VisionDAL.lib文件并通过VisionDAL.dll文件输出.__cdecl定义如何调用这个过程, 这里我们使用C风格的调用约定.当UNICODE定义被设置时,TCHAR和WCHAR是一样的,否则TCHAR就是一个简单的char, 假定我们这里的UNICODE 已经设置好了.

  • 注意这里有一些不同的 Unicode格式:
  • 在C语言里我们使用两个字节来表示一个char值
  • 在MYSQL和.NET 框架的UTF-8格式, 它意味着一个字节对应一个字符并且仅在超过一个字节被使用时
  • 在终端程序中通过用一个字符对应一个字符并且当值大于127时使用Codepage 850.

参数格式是 FORMAT_TEXT 和 FORMAT_JSON,来保证输出在text和 JSON之间.

如果forAutocomplete是true,那么只有标题被搜索并返回.

VisionDALClientConsole

VisionDALClientConsole是一个很小的Windows Console应用程序。测试我们的GetDocuments程序将会涉及到VisionDAL工程集合,它将文件从VisionDAL输出到 VisionSmall\x64\Debug 。

VisionDALClientConsole 发出搜索字串请求,包括通配符“*”,它将会搜索title列和text列,并通过调用GetDocuments将字符输出。

一个简单的例子:

main 入口:

int _tmain(int argc,TCHAR* argv[])
{
  char c;
  TCHAR *result;
  TCHAR *search = (TCHAR *)malloc(1000*2);
  char *searchA = (char *)malloc(1000);
  int retval = 1;
  char buffer[32000];

  buffer[0]=0;
  printf("Search for: ");
  /* wscanf doesn't get umlauts */
  if(scanf("%[^\n]", searchA) <= 0){
    printf("Could not read input - retrieving all Documents \n");
    *search=0;
  }else{
    MultiByteToWideChar(850,0,searchA, -1,search, 999);
  }
  result=GetDocuments(search, FORMAT_TEXT, 0);
  if(result == NULL){
    retval = 0;
  }else{
    WideCharToMultiByte(850,0,result, -1,buffer, 32000,NULL,NULL);
    printf("%s", buffer);
  }
  fflush(stdin);
  printf("Press RETURN Key to Exit\n");
  getchar();
  return retval;
}

在Microsoft C V.12中按照惯例可以处理Unicode-16字串。在函数字串开始加上w或是用wcs替换str,如:wscanf,wprintf以及wcslen替换的是strlen。用wscanf不能正确的处理宽窄字符转化。我用MultiByteToWideChar,codepage用850来转化宽字符和用WideCharToMultiByte转化为一般字符。

查询MySQL数据库

上面我演示了如何连接数据库以及获得一个叫做conn的连接点。

接下来我们建立SQL查询:

mysql_query(conn, "SET NAMES 'utf8'");
if(forAutocomplete){
  if(search == NULL || wcslen(search) ==0){
    WideCharToMultiByte(CP_UTF8,0,
     L"SELECT Title from Document LIMIT 20",-1,sql,1000,NULL,NULL);
  }else{
    wsprintf(lbuffer, L"SELECT Title, match(Title) against('%ls' IN
     BOOLEAN MODE) as Score from Document where match(Title) against('%ls'
     IN BOOLEAN MODE) > 0.001 order by Score Desc LIMIT 20",
      search, search);
    WideCharToMultiByte(CP_UTF8,0,lbuffer,-1,sql,1000,NULL,NULL);
  }
}else if(search == NULL || wcslen(search) ==0){
  WideCharToMultiByte(CP_UTF8,0,L"SELECT DocumentID, Title, Text from Document",-1,sql,1000,NULL,NULL);
}else{
  wsprintf(lbuffer, L"SELECT DocumentID, Title, Text, match(Title, Text)
       against('%ls' IN BOOLEAN MODE) as Score from Document where match(Title, Text)
       against('%ls' IN BOOLEAN MODE) > 0.001 order by Score Desc",
    search, search);
  WideCharToMultiByte(CP_UTF8,0,lbuffer,-1,sql,1000,NULL,NULL);
}

查询match(Title, Text) against('%ls' IN BOOLEAN MODE)在列Title和Text中查询要搜索的字符串,并返回一个反馈查询匹配情况的值。只有分数大于0.001的文档将显示,输出结果按评分排序。

IN BOOLEAN MODE时多个单词的搜索分别进行。

在搜索字符串中,你可以使用“*”作为通配符,它匹配0到n个字符。例如“as*”会匹配ASP。搜索不区分大小写。在SQL server中有些例外,“as**”不匹配任何内容,“*SP”也不匹配,你可以在字符串的开头匹配通配符。

获得数据

if(mysql_query(conn, sql)) {
  fprintf(stderr, "%s\n", mysql_error(conn));
  fprintf(stderr, "%s\n", sql);
  return NULL;
}
  // Process results
result = mysql_store_result(conn);
  ...
  while((row = mysql_fetch_row(result)) != NULL) {
  if(format == FORMAT_TEXT){
    MultiByteToWideChar(CP_UTF8,0,row[0], -1,buffer, 255);
    wsprintf(resultBufferp,L"%s\t", buffer);
    resultBufferp+=wcslen(buffer)+1;
    MultiByteToWideChar(CP_UTF8,0,row[1], -1,buffer, 255);
    wsprintf(resultBufferp,L"%s\t", buffer);
    resultBufferp+=wcslen(buffer)+1;
    MultiByteToWideChar(CP_UTF8,0,row[2], -1,buffer, 32000);
    wsprintf(resultBufferp,L"%s\n", buffer);
    resultBufferp+=wcslen(buffer)+1;
  }else if(format == FORMAT_JSON){
    if(!forAutocomplete){
      MultiByteToWideChar(CP_UTF8,0,row[0], -1,buffer, 255);
      wsprintf(resultBufferp,L"{\"DocumentID\": %s, ", buffer);
      resultBufferp+=wcslen(buffer)+wcslen(L"{\"DocumentID\": , ");
      MultiByteToWideChar(CP_UTF8,0,row[1], -1,buffer, 255);
      wsprintf(resultBufferp,L"\"Title\": \"%s\", ", buffer);
      resultBufferp+=wcslen(buffer)+wcslen(L"\"Title\": \"\", ");
      MultiByteToWideChar(CP_UTF8,0,row[2], -1,buffer, 32000);
      wsprintf(resultBufferp,L"\"Text\": \"%s\"},", buffer);
      resultBufferp+=wcslen(buffer)+wcslen(L"\"Text\": \"\"},");
    }else{
      MultiByteToWideChar(CP_UTF8,0,row[0], -1,buffer, 255);
      wsprintf(resultBufferp,L"\"%s\",", buffer);
      resultBufferp+=wcslen(buffer)+wcslen(L"\"\",");
    }
  }
}

mysql_query 将查询发送到服务器。mysql_store_result将结果准备为一个集合,你可用mysql_fetch_row(result)进行迭代。无论列具有什么数据类型,每行都是一个字符串数组。我更喜欢ADO.NET中的具有类型的列。在.NET中,我们可能使用StringBuilder来聚集结果字符串,这里我们通过malloc和增长resultBufferp指针来定位char[]。我们使用MultiByteToWideChar来转换到WCHAR。
 
 JSON 格式

我决定不采用XML格式,而使用轻量级的 JSON-格式,以此来从Web页面通过AJAX与Webservice通讯。

JSON-输出看起来像这样

[{"DocumentID": 1, "Title": "ASP MVC 4", "Text":
   "Was für Profis"},{"DocumentID": 2, "Title": "JQuery",
   "Text": "Hat Ajax Support"},{"DocumentID": 3, "Title": "
WebServices", "Text": "Visual C++ kanns nicht"},{"DocumentID": 4,
 "Title": "Boost", "Text": "Muss Extra installiert werden"}]
 在参数自动完成为真的时候,JSON-看起来像这样:
?
1

["ASP MVC 4","JQuery","WebServices","Boost"]

"[]" 符号表明了一个数组的开始与结束, "{}" 标明了一个对象的开始与结束。在一个对象中,":"前面的部分是属性名称,在它后面的部分是属性值。与之类似的,在你用JavaScript编码的时候也差不多一样。通过JavaScript-命令JSON.parse,你得到一个完整的对象,这个对象的属性可以通过通常的"." 符号访问。

为 GetDocuments 方法搭建 Webservice

我使用"Visual C#/WCF/WCF Service Application"模板创建了 VisionWeb项目,需要添加必要的System.ServiceModel引用。

下一步我们使用 NuGet 来添加必要的 JavaScript 库。选择 "Tools/Library Packet Manager/Package Manager Console" 并执行如下命令:

Install-Package jQuery
Install-Package jQuery.UI.Combined

下一步我们在 “  App-Code/IVisionService.cs” 文件中定义 service contract  :

namespace VisionServices
{
  [ServiceContract(SessionMode = SessionMode.Allowed)]
  public interface IVisionService
  {
    [OperationContract]
    [WebInvoke(
      Method = "POST",
      BodyStyle = WebMessageBodyStyle.WrappedRequest,
      RequestFormat = WebMessageFormat.Json,
      ResponseFormat = WebMessageFormat.Json)]
    string GetDocuments(string search, int format, int forautocomplete);
  }
}

WebInvoke 属性是保证 service 能够被Ajax调用。我选择POST作为在HTTP请求中传递参数的方式。这个可选择的 GET 方式, 会加密并且暴露在URL中的参数。
我们指定以JSON格式发送请求和响应。当传递一个或多个参数时必须使用

BodyStyle = WebMessageBodyStyle.WrappedRequest。
你可以使用 WebMessageBodyStyle.Bareif ,这样你会得到零或者一个参数。

Webservice的实现

我们将实现定义在 "App-Code/IVisionService.cs"中:

namespace VisionServices
{
  public class PInvoke
  {
    [DllImport("VisionDAL.dll", CharSet = CharSet.Unicode)]
    public static extern string GetDocuments(string search, int format, int forAutocomplete);
  }
  public class VisionService : IVisionService
  {
    public string GetDocuments(string search, int format, int forautocomplete)
    {
      string result = PInvoke.GetDocuments(search, format, forautocomplete).ToString();
      return result;
    }
  }
}

VisionService.svc的实现

<%@ ServiceHost Language="C#" Debug="true" Service="VisionServices.VisionService" CodeBehind="App_Code\VisionService.cs" %>

这里定义了调用"http://<your webserver>:<your port>VisionService.svc"时的服务端点 ,调用GetDocuments函数的URL地址是 "http://<your webserver>:<your port>VisionService.svc/GetDocuments"。

Web.config 文件

<?xml version="1.0"?>
<configuration>
 <appSettings/>
 <system.web>
  <httpRuntime/>
  <compilation debug="true"/>
 </system.web>
 <system.serviceModel>
  <services>
   <service name="VisionServices.VisionService">
    <endpoint address="" binding="webHttpBinding"
       contract="VisionServices.IVisionService" behaviorConfiguration="webHttpEndpoint"/>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
   </service>
  </services>
  <behaviors>
   <endpointBehaviors>
    <behavior name="webHttpEndpoint">
     <webHttp helpEnabled="true"/>
    </behavior>
   </endpointBehaviors>
   <serviceBehaviors>
    <behavior>
     <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
     <serviceDebug includeExceptionDetailInFaults="true"/>
    </behavior>
   </serviceBehaviors>
  </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/>
 </system.serviceModel>
 <system.webServer>
  <modules runAllManagedModulesForAllRequests="true"/>
  <directoryBrowse enabled="true"/>
 </system.webServer>
</configuration>

这是允许Ajax请求的配置。 你可以使用很多选项来配置WCF。你可以到Safari上查看更多类似于[2]的文档。

<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>配置了一个提供元数据交换的端点,通过元数据你可以自动生成代码来获得WebService代理,比如使用svcutil。选择"Programs/Microsoft Visual Studio 2012/Visual Studio Tools/Developer Command Prompt for VS2012". 输入svcutil http://localhost:8001/VisionService.svc.一个名为VisionService.cs 的文件就生成了, 在其他情况下也会生成一个包含了Webservice配置信息的文件。

托管网站

启动“设置/控制面板/管理工具/ Internet信息服务(IIS)管理器”。当没有安装iis的时候,导航到“应用程序池”,找到正在运行.Net Framework 4.0版本应用程序池的名称,或者添加一个新的应用程序池。导航到“网站”节点,右击它,然后选择“添加网站...”,使用Vision作为网站的名称,为这个应用选择一个正在运行的 .Net Framework 4.0版本应用池。使用 <vision installdir>/VisionWeb作为物理路径,设置端口为8001.选择属性上VisionWeb项目,选择“网络”,选中“使用自定义的Web服务器”,输入服务器URL http://localhost:8001。你可以使用其他的选项来托管网站,例如在IIS Express中,但是如果你不想改变Default.html文件,你必须将端口设置为8001。

HTML/JQuery 页面

在VisionWeb中有个名为Default.html的单一HTML页面,它包含了HTML与JavaScript的内容,它被标为起始页。

同样这里是这个页面的样子:

<html>
<head>
  <title>Search</title>
  <script src="Scripts/jquery-2.0.2.js"></script>
  <script src="Scripts/jquery-ui-1.10.3.js"></script>
  <link href="Content/themes/base/jquery.ui.autocomplete.css" rel="stylesheet" />
  <style type=text/css>
    .ui-menu-item {
      background: White;
    }
    .ui-helper-hidden-accessible { display:none; }
  </style>
</head>

HTML代码说明了这是一个HTML 5的文档类型。接着我们包含进了必须的JavaScript文件。在jQuery-UI中我们只用到了自动完成插件,为此我们还包含了它的CSS文件。

对于自动完成对象,包含了类[__em all="[object HTMLCollection]"__] .ui-menu-item,我们将背景设置为白色,不然的话它的透明背景会使表格的内容穿透出来。

[__em all="[object HTMLCollection]"__].ui-helper-hidden-accessible { display:none; }将一个烦人的帮助信息从自动完成插件移走。

<form>
    <label for="search" >Search:</label>
    <input type="text" id="search", name="search" />
    <input type="button" id="update" name="update" value="Update" />
     <div id="result"></div>
  </form>

表单中的元素被赋以了id,因此你可以类似$('#result')用jQuery获得它们。你还可以用jQuery代替缩写的$,例如[__em all="[object HTMLCollection]"__] jQuery('#result')。JavaScript的函数调用document.getElementById('result')具有同样的效果,但是jQuery支持所有类型的CSS选择符。

我使用无侵入的JavaScript,也就是说html代码没有混在JavaScript代码中。事件处理器是在function$(document).ready(function ()方法中绑定的,这个方法会在页面加载后执行。

$(document).ready(function () {
  $('#update').bind('click', GetDocuments);
  $('#search').bind("keydown", GetInput);
  $("#search").autocomplete({
    source: function (request, callback) {
      GetAutocomplete();
      callback(Documents);
    },
    open: function (event) {
      var $ul = $(this).autocomplete("widget");
    }
  });
});

当你点击"update"按钮的时候会执行GetDocuments方法。它会进行一次全文检索然后将结果显示到一个HTML表格中:

function GetDocuments(e) {
var searchstring = $('#search').val();
if (searchstring.length > 0) {
  if (searchstring[searchstring.length - 1] != "*") {
    searchstring += "*";
  }
}
$.ajax({
    type: 'POST',
    url: 'http://localhost:8001/VisionService.svc/GetDocuments',
    dataType: 'json',
    crossDomain: true,
    data: JSON.stringify({ search: searchstring, format: 1, forautocomplete: 0 }),
    processData: true,
    contentType: "application/json ; charset=utf-8",
    success: function (json, textStatus) {
      var result = JSON.parse(json);
      var display;
      display = "";
      display += "<table id='mytable' border=2 <thead><th style='text-align:left'
         >ID</th><th style='text-align:left' >Title</th><th
         style='text-align:left' >Text</th></thead><tbody>";
      $.each(result, function (index, value) {
        display += "<tr>";
        display += "<td>" + value.DocumentID + "</td>";
        display += "<td>" + value.Title + "</td>";
        display += "<td>" + value.Text + "</td>";
        display += "<tr>";
      });
      display += "</tbody></table>";
      $('#result').empty()
      $('#result').html(display);
    },
    error: function (xhr, textStatus, errorThrown) {
      alert('An error occurred! ' + (errorThrown ? errorThrown : xhr.status) +
       " xhr: " + xhr + " textStatus: " + textStatus);
    }
  });
}

我们把查询表单中"search"字段的值付给变量searchstring,然后,当searchstring中不包含"*"通配符的时候,我们在其后面添加通配符"*",jQuery提供了对Ajax的支持,比如$.ajax()方法。

你可以从这儿查看关于这个方法的说明:jQuery.ajax()。

url:制定了我们在WCF应用中配置好的路径。就如我们在WCF应用中设置的一样,我们使用JSON数据格式。在success方法(这个方法会在ajax请求成功后被异步调用)中,我们获取了json变量,也就是GetDocuments方法输出的值。通过简单的调用JSON.parse(json)方法,我们获得了一个完全成熟的JavaScript对象,我们使用这个对象生成Html表格。result>变量是一个JavaScript对象数组。jquery的$.each方法遍历整个数组,当方法执行的时候,使用当前数组元素的索引和处于当前索引位置的元素作为参数。我们通过调用$('#result').html(display)来显示Html代码,从而生成我们的结果DIV。底层数据:我们使用JSON.stringify方法将用来传输的数据转化为JavaScript对象并将其作为参数。当发生错误的时候,在error:后面的代码将会执行。

自动完成是如何工作的

在我们的 JavaScript 代码开头,我们描述了一个全局变量,用来将用于自动完成的单词保存在一个数组中:var Documents = [];。 函数GetAutocomplete填充了Documents数组。 Autocomplete函数:

function GetAutocomplete(e) {
  var searchstring = $('#search').val();
  if (searchstring.length > 0) {
    if (searchstring[searchstring.length - 1] != "*") {
      searchstring += "*";
    }
  }
  $.ajax({
    type: 'POST',
    url: 'http://localhost:8001/VisionService.svc/GetDocuments',
    dataType: 'json',
    data: JSON.stringify({ search: searchstring, format: 1, forautocomplete: 1}),
    processData: true,
    async: false,
    contentType: "application/json ; charset=utf-8",
    success: function (json, textStatus) {
      Documents = JSON.parse(json);
    },
    error: function (xhr, textStatus, errorThrown) {
      alert('An error occurred! ' + (errorThrown ? errorThrown : xhr.status) +
       " xhr: " + xhr + " textStatus: " + textStatus);
    }
  });
}

这看起来非常像GetDocuments函数。success 函数只是更新了Documents变量,通过JSON.parse来将Webservice的输出进行转换。注意async: false,这使得调用是异步的。这个自动完成插件会调用GetAutocomplete函数,并立即显示Documents。

在 $(document).ready(function () 中初始化自动完成插件:

$("#search").autocomplete({
  source: function (request, callback) {
    GetAutocomplete();
    callback(Documents);
  },
  open: function (event) {
    var $ul = $(this).autocomplete("widget");
  }
});

你可以在这里找到关于自动完成的信息:自动完成。

在搜索框中处理 [RETURN] 键:

$('#search').bind("keydown", GetInput);

function GetInput(e) {
  if (e.keyCode == 13) {
    e.preventDefault();
    GetDocuments(e);
    $('#search').autocomplete("close");
  }
}

e.preventDefault();停止了对当前事件的处理。

调试

你可以在web浏览器中输入 URL http://localhost:8001/VisionService.svc。如果服务激活失败,将会有一条信息提示,例如 VisionDAL.dll 无法加载。你可以用像Fiddler 之类的工具检测其间的http通信。

(0)

相关推荐

  • 浅谈MySQL中的子查询优化技巧

    mysql的子查询的优化一直不是很友好,一直有受业界批评比较多,也是我在sql优化中遇到过最多的问题之一,你可以点击这里 ,这里来获得一些信息,mysql在处理子查询的时候,会将子查询改写,通常情况下,我们希望由内到外,也就是先完成子查询的结果,然后在用子查询来驱动外查询的表,完成查询,但是恰恰相反,子查询不会先被执行:今天希望通过介绍一些实际的案例来加深对mysql子查询的理解: 案例:用户反馈数据库响应较慢,许多业务动更新被卡住:登录到数据库中观察,发现长时间执行的sql: | 10437

  • 在MySQL中使用GTIDs复制协议和中断协议的教程

    MySQL5.6有很多新的特性,其中很多人都感兴趣的一条就是全局事务序号功能(GTIDs).而大家都对这一特性很感兴趣的原因也很好理解,即:本来重新连接从服务器和一个新的主服务器一直是件很麻烦的事,然而在启用GTIDs功能之后就变得简单易行.可是,GTIDs的使用不单单是用单独的标识符替换旧的二进制日志文件/位置,它也采用了新的复制协议.假如你还不太明白这些,那你可以在这篇文章里学点什么. 复制协议:新的 VS 旧的 旧的协议往往简单直接即:首先从服务器上在一个特定的偏移量那里连接到一个给定的二

  • 简单讲解MySQL中的多源复制

    近日ORACLE发布几个新的功能在最新的Mysql5.7.2的版本上,由此有了此篇文章.大多数的改善是在数据库性能和复制相关的功能上,这个新版本会带给我们不可思议的效果. 在这篇文章里,我将要用一些简单的步奏来尝试了解这新的多源复制工作原理以及我们怎样进行自己的测试.需要说明的是,这还是一个开发版本,不是给生产环境准备的.因此这篇文章是打算给那些想了解此新功能的人,看看它是如何在应用中工作的,都是在临时环境中进行相关操作. 什么是多源复制? 首先,我们需要清楚 multi-master 与mul

  • 详细介绍基于MySQL的搜索引擎MySQL-Fullltext

    本文涵盖了一个简单的C实现的搜索引擎的搭建始末. 我通常使用SQL Server和C #,但我教C/C++的朋友要远离微软.在过去,MySQL不是我想要的数据库,因为标准安装版不支持事务,但它变得越来越成熟.我使用64位InnoDB引擎的MySQL 5.6,使用Unicode(utf8)编码,这是我新数据库的默认设置. Freetext是InnoDB的新特征,它在MySQL5.6版中被首次推出. 与C相比我通常更喜欢C++,即使在小项目中:不用知道所有的函数名,而且有一些内置的常用操作和漂亮的I

  • mysql事务详细介绍

    目录 简介 事务四个特性 事务隔离级别 验证 MVCC 当前读 快照读 当前读.快照读.MVCC关系 mvcc 解决的问题 MVCC实现原理 可见性规则 简介 事务是由一组sql语句组成的逻辑处理单元 事务四个特性 原子性(Atomicity): 要么都成功要么都失败 undo log实现 一致性(Consistent): 如转账前后两个数额总合保持不变 隔离性(lsolation):数据库提供一定的隔离机制,保证事务在不受外部并发操作影响的"独立"环境下运行 锁,mvcc多版本并发控

  • mysql数据库之索引详细介绍

    目录 思维导图 简单理解 索引模型的演变 二叉查找树 自平衡二叉树 B树 B+树 聚集索引与二级索引 总结 如果你想深入了解为什么mysql可以快速的进行检索数据,那么你一定要来了解一下mysql的索引原理 思维导图 简单理解 你可以把索引理解为一本书的目录,我们可以通过索引快速的找到我们需要的数据,大概就像下面这个图,索引就像是右边的二叉树,每个节点指向具体的数据的物理地址,先通过二叉树找到数据的位置,然后再去物理磁盘中获取数据. 但是不同的二叉树的特性不同,我们还要选择合适的树来作为索引,所

  • MySQL主从复制之GTID模式详细介绍 

    目录 一.GTID概述 二.GTID相较与传统复制的优势 三.GTID自身存在哪些限制 四.GTID工作原理简单介绍 五.如何开启GTID复制 六.查看GTID相关参数 七.GTID与传统模式建立复制时候语句的不同点 八.GTID同步状态简单解析 一.GTID概述 MySQL5.6 在原有主从复制的基础上增加了一个新的复制方式,即基于GTID的复制方式,它由UUID和事务ID两个部分组成,具有如下特点. GTID事务是全局唯一性的,并且一个事务对应一个GTID值. 一个GTID值在同一个MySQ

  • MySQL高级查询示例详细介绍

    目录 1.左关联 2.右关联 3.子查询 4.联合查询 5.分组查询 1.左关联 MySQL中的左关联(Left Join)是一种基于共同列的连接操作, 它将左侧表中的所有行与右侧表中匹配的行结合在一起, 如果右侧表中没有匹配的行,则结果集中右侧表中的所有列将显示为NULL. 左侧表是指在关键字LEFT JOIN中出现在关键字左侧的表. 下面是一个使用MySQL的LEFT JOIN进行连接操作的简单示例: 假设我们有两个表,一个是学校表(school),包含学校的ID和名称: 另一个是年级表(g

  • mysql zip archive 版本(5.7.19)安装教程详细介绍

    1.  从官网下载zip archive版本http://dev.mysql.com/downloads/mysql/ MySQL v5.7.19 官方正式版(32/64位 安装版与zip解压版) 2. 解压缩至相应目录,并配置环境变量(将*\bin添加进path中): 3. 理论上现在这样就可以直接安装服务了,但是因为是默认配置,我们使用的时候会出现很多问题.比如里面的汉字全是乱码之类的,所以建议先配置一下默认文件.在解压的mysql目录下,新建个my.ini,//在根目录新建my.ini文件

  • MySql索引详细介绍及正确使用方法

    MySql索引详细介绍及正确使用方法 1.前言: 索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点. 索引是存储引擎用于快速查找记录的一种数据结构,通过合理的使用数据库索引可以大大提高系统的访问性能,接下来主要介绍在MySql数据库中索引类型,以及如何创建出更加合理且高效的索引技巧. 注:这里主要针对的是InnoDB存储引擎的B+Tree索引数据结构 2.索引的优点 1.大大减轻了服务器需要扫描的数据量,从而提高了数据的检索速度 2.帮助服务器避免排序和临时表 3.可以将

  • 开源MySQL高效数据仓库解决方案:Infobright详细介绍

    Infobright是一款基于独特的专利知识网格技术的列式数据库.Infobright是开源的MySQL数据仓库解决方案,引入了列存储方案,高强度的数据压缩,优化的统计计算(类似sum/avg/group by之类),infobright 是基于mysql的,但不装mysql亦可,因为它本身就自带了一个.mysql可以粗分为逻辑层和物理存储引擎,infobright主要实现的就是一个存储引擎,但因为它自身存储逻辑跟关系型数据库根本不同,所以,它不能像InnoDB那样直接作为插件挂接到mysql,

  • Mysql表的七种类型详细介绍

    学习Mysql数据库,Mysql表类型都有哪些是一定需要知道的,下面就为您介绍七种Mysql表类型,希望能对您学习Mysql表类型有所帮助. MySQL作为当前最为流行的免费数据库服务引擎,已经风靡了很长一段时间,不过也许也有人对于MySQL的内部环境不很了解,尤其那些针对并发性处理的机制.今天,我们先了解一下Mysql表类型,以及它们的一些简单性质. 截至目前,MySQL一共向用户提供了包括DBD.HEAP.ISAM.MERGE.MyIAS.InnoDB以及Gemeni这7种Mysql表类型.

  • mysql模糊查询like与REGEXP的使用详细介绍

    前言 在mysql中实现模糊查询的有like和regexp.本文通过实例代码给大家详细介绍这两者的使用方法,下面来跟着小编一起学习学习吧. like模式 like意思是长得像,有两个模式:_和% _表示单个字符,通常用来查询定长的数据,如查出所有姓王的三个字的人名,假设姓名列名为name,注意"王"后面有两个_ select name from 表名 where name like '王__'; %表示0个或多个任意字符,如查出所有姓王的人名 select name from 表名 w

随机推荐