詳細(xì)介紹基于MySQL的搜索引擎MySQL-Fullltext
本文涵蓋了一個簡單的C實現(xiàn)的搜索引擎的搭建始末。
我通常使用SQL Server和C #,但我教C/C++的朋友要遠(yuǎn)離微軟。在過去,MySQL不是我想要的數(shù)據(jù)庫,因為標(biāo)準(zhǔn)安裝版不支持事務(wù),但它變得越來越成熟。我使用64位InnoDB引擎的MySQL 5.6,使用Unicode(utf8)編碼,這是我新數(shù)據(jù)庫的默認(rèn)設(shè)置。
Freetext是InnoDB的新特征,它在MySQL5.6版中被首次推出。
與C相比我通常更喜歡C++,即使在小項目中:不用知道所有的函數(shù)名,而且有一些內(nèi)置的常用操作和漂亮的IntelliSense支持。在C++中,還有有STL及集合和字符串助手。
C++的Mysql接口比較弱,而C的接口很成熟,所以我決定使用C接口。
C的dll文件是和WCF一起發(fā)布的,以便完成AJAX請求,在Visual Studio Ultimate 2012中我使用C#的"WCF Service Application"模板,我搜索了使用C++搭建WebService的方法,但只找到一些使用C++處理WebServices調(diào)用的例子。
用戶界面是一個使用Jquery和Jquery-UI自動提示的HTML界面,頁面被增加到"WCF服務(wù)應(yīng)用",項目被命名為VisionWeb
網(wǎng)頁看起來是這樣的:
我在.NET框架4.0,64位系統(tǒng)上配置這個項目,如果你使用32位的Mysql服務(wù)器,你必需隨之做些更改。記得設(shè)置UNICODE選項為默認(rèn)值。
配置MySQL
你有可能會從VisionSmall中打開這個VisionDAL項目, 假定你必須修改連接MySQL的C程序接口. 在這兒,我介紹了如何在新項目中安裝MySQL接口: 檢查那些設(shè)置是否符合你的要求,尤其是mysql.lib文件和VisionDAL.dll的路徑.
在Visual Studio中,添加一個VisionDAL工程, 通過這個流程"Other Languages/Visual C++/Empty Project". 在這之中, 你只需要改變"應(yīng)用類型" 為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>這個目錄. 現(xiàn)在我們已經(jīng)連接到C接口, 但是在libmysql.lib中調(diào)用執(zhí)行的DLL必須是系統(tǒng)的可執(zhí)行路徑: 從控制面板, 選擇系統(tǒng), 點擊 "高級系統(tǒng)設(shè)置", "點出環(huán)境變量" 在 "系統(tǒng)變量"下面,選擇路徑, 并添加這個 libmysql.lib 的路徑 (DLL和這個lib文件在相同的文件夾里): C:\Program Files\MySQL\MySQL Server 5.6\lib.
我們也需要把這個VisionDal.dll放到我們的path路徑里, IIS 并不能從這個網(wǎng)站的bin目錄中取到DLL文件. 添加 <項目路徑>/x64/debug 到路徑變量path里. 重啟后生效. 當(dāng)網(wǎng)站得到一個request請求時將會加載VisionDAL.dll; 如果你現(xiàn)在重建項目, 你會得到一個VisionDAL.dll的寫入錯誤: 為了解決它, 重啟該網(wǎng)站或是用unlocker之類的解鎖.
如果需要指定VisonDAL的包含屬性. 在 "配置屬性"/"C/C++" 菜單下添加MYSQL的頭文件路徑, 例如像這樣: C:\Program Files\MySQL\MySQL Server 5.6\include.
下面我們在“C/C++”/"預(yù)編譯頭"菜單欄中,從“預(yù)編譯頭”切換到“不使用預(yù)編譯頭”,設(shè)置Preproccessor定義防止使用strcpy和fopen時產(chǎn)生的錯誤消息:在"C/C++"/預(yù)編譯器/"預(yù)編譯器定義 "中設(shè)定SE_STANDARD_FILE_FUNCTIONS和_CRT_SECURE_NO_WARNINGS。
當(dāng)你現(xiàn)在連接,mysqllib引用的問題并沒有解決,因為它們是64位處理器。通過在VisionDal中打開工程屬性,選擇“配置管理”,然后設(shè)置為x64平臺。
現(xiàn)在我們來創(chuàng)建名為 Vision 的樣本數(shù)據(jù)庫
打開SQL Development 中的 MySql 工作臺,打開你的實例。將會出現(xiàn)一個新窗口 "SQL File 1" 。 雙擊VisionDAL項目中的 Sql.txt 文件。復(fù)制所有內(nèi)容到剪貼板,粘貼到工作臺中的"SQL File 1"窗口。 點擊螺栓圖標(biāo)(左邊第三個圖標(biāo)),創(chuàng)建樣本數(shù)據(jù)庫。
接下來我們需要用來數(shù)據(jù)庫登錄的通用信息。
我們有一個關(guān)于此的配置文件: <installation director>VisionSmall\x64\Debug\VisionConfiguration.txt, 看起來像這樣:
Host: localhost
User: root
Password: frob4frob
Database: vision
Port: 3306
修改這些數(shù)值以匹配你的SQL-Configuration。
Vision 數(shù)據(jù)庫
數(shù)據(jù)庫中只有一張表
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中選擇,查詢的時候,哪個字段被包含進(jìn)搜索。在MySQL中,通常全文索引的所有字段都被搜索,所以我們必須指定額外的全文索引'ftTitle'。
通過C接口進(jìn)行MySQL查詢
首先呢,為了執(zhí)行查詢我們需要連接到數(shù)據(jù)庫并取得一個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文件里的變量賦值到全局變量, 這個文件應(yīng)該和我們的程序在同一目錄. 這是一個例行操作.獲取當(dāng)前運行程序目錄是通過Win32 API的GetModuleFileName函數(shù),如下:
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)的聲明和定義實現(xiàn)是通過添加到VisionDAL.lib文件并通過VisionDAL.dll文件輸出.__cdecl定義如何調(diào)用這個過程, 這里我們使用C風(fēng)格的調(diào)用約定.當(dāng)UNICODE定義被設(shè)置時,TCHAR和WCHAR是一樣的,否則TCHAR就是一個簡單的char, 假定我們這里的UNICODE 已經(jīng)設(shè)置好了.
- 注意這里有一些不同的 Unicode格式:
- 在C語言里我們使用兩個字節(jié)來表示一個char值
- 在MYSQL和.NET 框架的UTF-8格式, 它意味著一個字節(jié)對應(yīng)一個字符并且僅在超過一個字節(jié)被使用時
- 在終端程序中通過用一個字符對應(yīng)一個字符并且當(dāng)值大于127時使用Codepage 850.
參數(shù)格式是 FORMAT_TEXT 和 FORMAT_JSON,來保證輸出在text和 JSON之間.
如果forAutocomplete是true,那么只有標(biāo)題被搜索并返回.
VisionDALClientConsole
VisionDALClientConsole是一個很小的Windows Console應(yīng)用程序。測試我們的GetDocuments程序?qū)婕暗絍isionDAL工程集合,它將文件從VisionDAL輸出到 VisionSmall\x64\Debug 。
VisionDALClientConsole 發(fā)出搜索字串請求,包括通配符“*”,它將會搜索title列和text列,并通過調(diào)用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字串。在函數(shù)字串開始加上w或是用wcs替換str,如:wscanf,wprintf以及wcslen替換的是strlen。用wscanf不能正確的處理寬窄字符轉(zhuǎn)化。我用MultiByteToWideChar,codepage用850來轉(zhuǎn)化寬字符和用WideCharToMultiByte轉(zhuǎn)化為一般字符。
查詢MySQL數(shù)據(jù)庫
上面我演示了如何連接數(shù)據(jù)庫以及獲得一個叫做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中查詢要搜索的字符串,并返回一個反饋查詢匹配情況的值。只有分?jǐn)?shù)大于0.001的文檔將顯示,輸出結(jié)果按評分排序。
IN BOOLEAN MODE時多個單詞的搜索分別進(jìn)行。
在搜索字符串中,你可以使用“*”作為通配符,它匹配0到n個字符。例如“as*”會匹配ASP。搜索不區(qū)分大小寫。在SQL server中有些例外,“as**”不匹配任何內(nèi)容,“*SP”也不匹配,你可以在字符串的開頭匹配通配符。
獲得數(shù)據(jù)
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 將查詢發(fā)送到服務(wù)器。mysql_store_result將結(jié)果準(zhǔn)備為一個集合,你可用mysql_fetch_row(result)進(jìn)行迭代。無論列具有什么數(shù)據(jù)類型,每行都是一個字符串?dāng)?shù)組。我更喜歡ADO.NET中的具有類型的列。在.NET中,我們可能使用StringBuilder來聚集結(jié)果字符串,這里我們通過malloc和增長resultBufferp指針來定位char[]。我們使用MultiByteToWideChar來轉(zhuǎn)換到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"}] 在參數(shù)自動完成為真的時候,JSON-看起來像這樣: ? 1 ["ASP MVC 4","JQuery","WebServices","Boost"]
"[]" 符號表明了一個數(shù)組的開始與結(jié)束, "{}" 標(biāo)明了一個對象的開始與結(jié)束。在一個對象中,":"前面的部分是屬性名稱,在它后面的部分是屬性值。與之類似的,在你用JavaScript編碼的時候也差不多一樣。通過JavaScript-命令JSON.parse,你得到一個完整的對象,這個對象的屬性可以通過通常的"." 符號訪問。
為 GetDocuments 方法搭建 Webservice
我使用"Visual C#/WCF/WCF Service Application"模板創(chuàng)建了 VisionWeb項目,需要添加必要的System.ServiceModel引用。
下一步我們使用 NuGet 來添加必要的 JavaScript 庫。選擇 "Tools/Library Packet Manager/Package Manager Console" 并執(zhí)行如下命令:
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調(diào)用。我選擇POST作為在HTTP請求中傳遞參數(shù)的方式。這個可選擇的 GET 方式, 會加密并且暴露在URL中的參數(shù)。
我們指定以JSON格式發(fā)送請求和響應(yīng)。當(dāng)傳遞一個或多個參數(shù)時必須使用
BodyStyle = WebMessageBodyStyle.WrappedRequest。
你可以使用 WebMessageBodyStyle.Bareif ,這樣你會得到零或者一個參數(shù)。
Webservice的實現(xiàn)
我們將實現(xiàn)定義在 "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的實現(xiàn)
<%@ ServiceHost Language="C#" Debug="true" Service="VisionServices.VisionService" CodeBehind="App_Code\VisionService.cs" %>
這里定義了調(diào)用"http://<your webserver>:<your port>VisionService.svc"時的服務(wù)端點 ,調(diào)用GetDocuments函數(shù)的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"/>配置了一個提供元數(shù)據(jù)交換的端點,通過元數(shù)據(jù)你可以自動生成代碼來獲得WebService代理,比如使用svcutil。選擇"Programs/Microsoft Visual Studio 2012/Visual Studio Tools/Developer Command Prompt for VS2012". 輸入svcutil http://localhost:8001/VisionService.svc.一個名為VisionService.cs 的文件就生成了, 在其他情況下也會生成一個包含了Webservice配置信息的文件。
托管網(wǎng)站
啟動“設(shè)置/控制面板/管理工具/ Internet信息服務(wù)(IIS)管理器”。當(dāng)沒有安裝iis的時候,導(dǎo)航到“應(yīng)用程序池”,找到正在運行.Net Framework 4.0版本應(yīng)用程序池的名稱,或者添加一個新的應(yīng)用程序池。導(dǎo)航到“網(wǎng)站”節(jié)點,右擊它,然后選擇“添加網(wǎng)站...”,使用Vision作為網(wǎng)站的名稱,為這個應(yīng)用選擇一個正在運行的 .Net Framework 4.0版本應(yīng)用池。使用 <vision installdir>/VisionWeb作為物理路徑,設(shè)置端口為8001.選擇屬性上VisionWeb項目,選擇“網(wǎng)絡(luò)”,選中“使用自定義的Web服務(wù)器”,輸入服務(wù)器URL http://localhost:8001。你可以使用其他的選項來托管網(wǎng)站,例如在IIS Express中,但是如果你不想改變Default.html文件,你必須將端口設(shè)置為8001。
HTML/JQuery 頁面
在VisionWeb中有個名為Default.html的單一HTML頁面,它包含了HTML與JavaScript的內(nèi)容,它被標(biāo)為起始頁。
同樣這里是這個頁面的樣子:
<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的文檔類型。接著我們包含進(jìn)了必須的JavaScript文件。在jQuery-UI中我們只用到了自動完成插件,為此我們還包含了它的CSS文件。
對于自動完成對象,包含了類[__em all="[object HTMLCollection]"__] .ui-menu-item,我們將背景設(shè)置為白色,不然的話它的透明背景會使表格的內(nèi)容穿透出來。
[__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的函數(shù)調(diào)用document.getElementById('result')具有同樣的效果,但是jQuery支持所有類型的CSS選擇符。
我使用無侵入的JavaScript,也就是說html代碼沒有混在JavaScript代碼中。事件處理器是在function$(document).ready(function ()方法中綁定的,這個方法會在頁面加載后執(zhí)行。
$(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"); } }); });
當(dāng)你點擊"update"按鈕的時候會執(zhí)行GetDocuments方法。它會進(jìn)行一次全文檢索然后將結(jié)果顯示到一個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,然后,當(dāng)searchstring中不包含"*"通配符的時候,我們在其后面添加通配符"*",jQuery提供了對Ajax的支持,比如$.ajax()方法。
你可以從這兒查看關(guān)于這個方法的說明:jQuery.ajax()。
url:制定了我們在WCF應(yīng)用中配置好的路徑。就如我們在WCF應(yīng)用中設(shè)置的一樣,我們使用JSON數(shù)據(jù)格式。在success方法(這個方法會在ajax請求成功后被異步調(diào)用)中,我們獲取了json變量,也就是GetDocuments方法輸出的值。通過簡單的調(diào)用JSON.parse(json)方法,我們獲得了一個完全成熟的JavaScript對象,我們使用這個對象生成Html表格。result>變量是一個JavaScript對象數(shù)組。jquery的$.each方法遍歷整個數(shù)組,當(dāng)方法執(zhí)行的時候,使用當(dāng)前數(shù)組元素的索引和處于當(dāng)前索引位置的元素作為參數(shù)。我們通過調(diào)用$('#result').html(display)來顯示Html代碼,從而生成我們的結(jié)果DIV。底層數(shù)據(jù):我們使用JSON.stringify方法將用來傳輸?shù)臄?shù)據(jù)轉(zhuǎn)化為JavaScript對象并將其作為參數(shù)。當(dāng)發(fā)生錯誤的時候,在error:后面的代碼將會執(zhí)行。
自動完成是如何工作的
在我們的 JavaScript 代碼開頭,我們描述了一個全局變量,用來將用于自動完成的單詞保存在一個數(shù)組中:var Documents = [];。 函數(shù)GetAutocomplete填充了Documents數(shù)組。 Autocomplete函數(shù):
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函數(shù)。success 函數(shù)只是更新了Documents變量,通過JSON.parse來將Webservice的輸出進(jìn)行轉(zhuǎn)換。注意async: false,這使得調(diào)用是異步的。這個自動完成插件會調(diào)用GetAutocomplete函數(shù),并立即顯示Documents。
在 $(document).ready(function () 中初始化自動完成插件:
$("#search").autocomplete({ source: function (request, callback) { GetAutocomplete(); callback(Documents); }, open: function (event) { var $ul = $(this).autocomplete("widget"); } });
你可以在這里找到關(guān)于自動完成的信息:自動完成。
在搜索框中處理 [RETURN] 鍵:
$('#search').bind("keydown", GetInput); function GetInput(e) { if (e.keyCode == 13) { e.preventDefault(); GetDocuments(e); $('#search').autocomplete("close"); } }
e.preventDefault();停止了對當(dāng)前事件的處理。
調(diào)試
你可以在web瀏覽器中輸入 URL http://localhost:8001/VisionService.svc。如果服務(wù)激活失敗,將會有一條信息提示,例如 VisionDAL.dll 無法加載。你可以用像Fiddler 之類的工具檢測其間的http通信。
相關(guān)文章
MySQL提升大量數(shù)據(jù)查詢效率的優(yōu)化神器
這篇文章主要介紹了MySQL提升大量數(shù)據(jù)查詢效率的優(yōu)化神器,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-07-07修改MySQL的數(shù)據(jù)庫引擎為INNODB的方法
本文主要介紹了修改MySQL的數(shù)據(jù)庫引擎為INNODB的方法,希望能對您有所幫助。2015-09-09MYSQL中有關(guān)SUM字段按條件統(tǒng)計使用IF函數(shù)(case)問題
MYSQL中SUM字段按條件統(tǒng)計使用IF函數(shù),具體實現(xiàn)代碼如下,感興趣的朋友不要錯過2014-01-01mysql下為數(shù)據(jù)庫設(shè)置交叉權(quán)限的方法
由于 SupeSite 需要調(diào)用 Discuz! 和 UCHome 的數(shù)據(jù),所以如果它們不安裝在同一個數(shù)據(jù)庫,SupeSite 的數(shù)據(jù)庫用戶必須要對 Discuz! 和 UCHome 的數(shù)據(jù)庫有讀取、修改、刪除等權(quán)限。2011-07-07