欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-02-28 16:44:28
 本文涵盖了一个简单的c实现的搜索引擎的搭建始末。 我通常使用sql server和c #,但我教c/c++的朋友要远离微软。在过去,mysql不是我想要的数据...

 本文涵盖了一个简单的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

网页看起来是这样的: 

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

 我在.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将字符输出。

一个简单的例子: 

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

 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的内容,它被标为起始页。

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

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

<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通信。