0、写在前面
本文主要讲述在Windows2000 Server环境下,Exchange Server和Sharepoint Portal Server全检索功能的应用,同时涉及一些SQL Server下的概念。
本文只会对一些基本的概念进行说明,主要以个人理解和事例为主,希望了解详细的资料,可以访问参考文献的连接。对于文章中的错误与不足,请及时指正。
1、基本概念
我相信很多人对于全文检索的功能都非常的敬畏,心想Exchange居然能够查找一个作为附件的PDF文档中的内容,Exchange实在太强了。其实Exchange没干啥事(还是干了点事情的,不是什么都没做,下面会讲到),全文检索是Windows下一个重要的服务“MS Search Service”实现的功能,Exchange只需要和MS Search交互就自然拥有了全文检索的功能。所以在MS的Enterprise Server中不仅仅Exchange,Shareporit Portal Server和SQL Server等都支持全文检索。
Windows下还有一个服务Index Service,也完成类似的工作,有它存在,就连Windows下的文件系统以及IIS都可以进行全文检索,非常方便的功能。
不知道可不可以这么说Index Service是MS Search Service的一个特例,或者倒过来,我没有看到两者在体系结构以及完成功能上有什么不同,这也是我比较困惑的地方之一。
说到全文检索必须要明确一些基本概念,我们才能够知道那个服务干了点什么,这样实现特定功能的时候才明白应该在那一个层面上进行开发。
全文检索分为两部分的工作:全文索引(Full-Text Index)和全文查询(Full-Text Query)。我们知道为了提高查询的效率,有一种机制叫做“索引(Index)”,索引中存储了关键字和对应的记录在逻辑存储空间中的位置。数据库管理系统(DBMS)中有索引表,相信大家都可以理解,全文检索中也是同样的原理。
全文索引(Full-Text Index):创建索引的过程,建立关键字与记录的对应关系。创建完成的索引信息或者以增量的方式修改属性信息。
索引分类(Full-Text Catalog):可以认为是关键字存储的组织形式。
全文查询(Full-Text Query):利用索引分类,根据关键字查找对应的记录。
在MS的产品体系结果中,MS Search Service和Index Service这类服务(以后主要描述Search Service)的主要工作就是创建和维护索引表和索引分类,可以称之为搜索引擎;诸如Exchange和SQL Server是为记录提供存储空间,可以统称为存储引擎,存储引擎必须支持MS Search的一些特定接口,就可以利用。全文索引的过程就是MS Search请求存储引擎,获得需要索引的数据,分析关键字,创建或者维护索引信息;全文查询就是存储引擎请求MS Search,MS Search根据关键字返回对应的纪录的位置,存贮引擎组织这些记录返回给调用者。
这样我们可以了解到,如果希望支持全文检索,那么首先要有全文检索服务的提供者,支持全文检索的存储引擎。在Windows Server的平台下,针对Exchange的应用,以上条件均满足,那么我们可以开始了!
2、全文检索下的开发
了解了Windows平台下全文检索基本的体系结构,我们可以在很多方面进行相关的开发工作。
2-1、全文查询
全文检索就是利用查询语句,在存储引擎中查找满足条件的记录。这个应该是大家最熟悉也是应用最多的方面,也是本文讲述的重点,后面有专门的章节详细描述这部分的功能。
需要指出的一点是,全文查询的过程依赖于MS Search以及他所维护的索引数据,但是是存储引擎支持的功能,这部分的应用是与存储引擎进行交互,MS Search这个搜索引擎对我们来说是透明的。针对不同的存储引擎,查询语句(基本上就是SELECT了)是不同的,不过没有本质的区别。
2-2、MS Search下IFilter的开发
全文检索最令人感到神奇的地方就是,他怎么能够从Word或者PDF这类二进制或者特定编码的文件中检索出文本字符串呢?另一个问题就是MS Search是不是支持所有的文件格式呢?
MS Search并不能支持所有的文件格式,即使以MS之强悍,也无法对抗天下英豪,但是MS Search提供一个机制,使支持任何一种格式的文档成为可能,这个机制就是Index Filter,简称IFilter。我个人以为IFilter最终要的工作有两个,一个是读取指定格式的文件,解析内部的文本内容(而非格式或者图形、其他二进制包容对象)和文档属性(例如:作者,分类等);第二个就是分词(Parsing Word or Phrase),全文检索的命中率高不高,关键看关键字生成的是否合理,我总觉得目前以后的这些IFilter对于中文支持的不够理想,检索中文经常莫名其妙。
很不幸如果你的系统需要支持的文档没有IFilter,或者你自己定义的文档个是,那么就需要开发自定义的IFilter了。到时候需要具体问题具体分析,这里就不多说了。
2-3、开发支持全文检索的存储引擎
2-1关注的是用户需求,可谓最贴近应用领域的开发;2-2关注的是存储的内容;这部分关心的就是存储本身了。简单的说当你的工作是开发一个存储管理系统,并且希望利用已经存在的检索引擎(Search Service)那么你就需要和检索引擎本身交道了。
在介绍概念的时候提到过,这类交互分为两大类,一类是创建或者维护索引时,另一个类是查询时。简单的看一下检索引擎本身,分为三部分:索引支持(Indexing Support)/索引引擎(Index Engine)和查询支持(Querying Support)/查找引擎(Search Engine)。在创建索引时,存储引擎主要和Indexing Support部分打交道,索引创建和维护的工作由Index Engine完成;在全文查询时,主要和Querying Support打交道,查询工作由Search Engine完成。
所谓支持就是实现一系列的接口并且调用一系列的接口,具体我也没干过,再说也是废话,就这些了。
3、全文查询指南
下面将比较详细地讲解以下在Exchange Server 2000平台下如何进行全文查询。
3-1、配置
缺省情况下Exchange下是不启动全文检索的功能,需要进行一些简单的配置。配置的过程就是为指定的存储空间(Public Store或者Private Store)创建索引。
在Exchange提供的管理工具System Manager下,这些配置工作非常容易完成,只有三个步骤。
a)选择特定的Public Store,执行“Create Full-Text Index”的功能,这是需要选择一个存放Index Catalog的目录。
b)刚刚创建完的全文索引是没有任何内容的,需要执行“Start Full Population”。这是一个比较耗时的操作,不会立刻产生效果,也正是因为在平时创建好了索引信息,在使用的时候才能够比较快速的定位记录。
c)然后还需要修改Store的属性中有关“Full-Text Index”的标签页中,修改其中的“Update Interval”属性项,选择自动更新索引信息的时间间隔,如果选择的是“Always run”,那么就会实时更新,当然系统开销也就比较大。当然也可以通过“Start Incremental Population”,以手动的方式,更新索引信息。
d)此外还需要在“Full-Text Index”标签页中,选择“This index is currently available for searching by clients”属性项,这样在查询过程中就会使用这部分创建的索引信息。
这一部分的功能我没有找到编程接口,所以目前只能够通过手动的方式配置。希望了解详细的内容,可以查阅MS相关的白皮书。
3-2、查询语句的语法说明
2-1中提到查询语句是没一个存储引擎所提供的,通常都是针对SELECT语句的扩展,下面将以Exchange的Store SQL为例讲解一下全文查询。
Exchange下全文查询的语句并不复杂,在Exchange Store SQL中提供了如下谓词(Predicate)加以支持,在WHERE子句(CLAUSE)中包含如下谓词,即可以继续全文检索:
CONTAINS:进行关键字的完整单词的匹配,格式为,
CONTAINS( [" propertyNAME " | * , ] ' searchspec ' )
如果“propertyName”部分*(Astrisk),则在所有属性中检索关键字,不包含"propertyName",则在消息或者文档的正文部分检索关键字,下同。“searchspec”部分包含关键字,通配符*,例如'GOOD*';也可以包含多个关键字组合查找,支持逻辑操作符AND和OR。每个关键字需要用"(Quote)包含,否则会产生语法错误。
FREETEXT:与CONTAINS比较类似,但是可以对关键字中每一个单词或者一组单词的多种变化形式进行模糊(Loosely)匹配,格式为,
FREETEXT( [" propertyNAME ", ] ' searchspec ' )
这里需要强调的是,对于关键字变换形式的匹配,而不是关键字子串(Substring)的匹配,也就是说可以通过'rose'找到'roses',但是不能够通过'public'找到'republican'。
FORMSOF:这个谓词需要包含在CONTAINS或者FREETEXT谓词中使用,通过该谓词修饰,可以对关键字的每一个变换形式进行匹配,关键字的变换形式是由搜索引擎决定的。格式为,
FORMSOF( type,"string" [,"string"] )
在Exchange中type参数的值恒为INFLECTIONAL。
RANK BY:该谓词通常用来修饰CONTAINS或者FREETEXT谓词,用来表示关键字出现的频度。格式为,
RANK BY CLAUSE (Mechanism, Weight)
其中CLAUSE参数为WEIGHT或者COERCION,WEIGHT表示权值,CORECION居然没有任何说明。Mechanism参数,表示行为,例如WEIGHT或者MULTIPHY。Weight为0-1之间的一个数值,表示权重。
当WHERE子句中包含多部分CONTAINS和FREETEXT时,有了RANK BY,可以认为是匹配度,非常有用。添加RANK BY谓词之后,可以读取"urn:schemas.microsoft.com:fulltextqueryinfo"属性的值,来比较记录之间的匹配程度。我不知道该属性最大的值为多少,但是从获得的实际数据的情况来看,最大为128。
来看一个完整的例子:
Select "DAV:href","urn:schemas.microsoft.com:fulltextqueryinfo:rank"
FROM Scope('DEEP TRAVERSAL OF ""')
WHERE FREETEXT('"program" OR "software"') RANK BY WEIGHT(1.0) OR CONTAINS('FORMSOF(INFLECTIONAL,"java") AND "vb"') RANK BY WEIGHT(0.5)
以上仅仅对有关全文查询相关的内容进行说明,完成的Store SQL的语法,参阅MSDN中的文档。
顺便说一句,Exchange主要提供协同服务,而非文档管理,所以在全文检索的支持上并非特别强大和灵活。MS的另一个服务器产品Sharepoint Portal Server,主要功能是文档管理,其重要应用之一即提供检索服务,所以它的全文检索的功能更加强大一点。除了上述内容外,还提供了NEAR、ISABOUT、RANKMETHOD等修饰项(Term),可以更好的控制查询条件。其中细节也请自行参阅相关文档。
3-3、ADO & WebDAV
在执行查询时可以采用两种方法,ADO或者WebDAV。这里只列举实现的代码加以说明。
3-3-1、WebDAV。
通过向指定查询的URL发送HTTP的SEARCH请求,命令参数和相应的数据,都是一定格式的XML文档(参看WebDAV的参考手册)。示例如下:
private System.Xml.XmlDocument SendSearchRequest(System.String sUrl,System.String sQuery)
{
System.Net.HttpWebRequest oRequest = null;
System.Net.HttpWebResponse oResponse = null;
System.Net.NetworkCredential oCredential = null;
System.IO.Stream oStream = null;
System.Text.UTF8Encoding oEncoder = new System.Text.UTF8Encoding();
System.Byte[] abData = null;
System.Xml.XmlDocument xmldoc = null;
if ( sUrl == null || sUrl == String.Empty)
return null;
if (sQuery == null || sQuery == String.Empty)
return null;
abData = oEncoder.GetBytes(sQuery);
if (abData == null)
return null;
oCredential = new NetworkCredential("administrator","server",String.Empty);
oRequest = (System.Net.HttpWebRequest) WebRequest.Create(sUrl);
if (oRequest != null)
{
// preparing search request
oRequest.ProtocolVersion = HttpVersion.Version11;
oRequest.Method = @"SEARCH";
if (oCredential != null)
oRequest.Credentials = oCredential.GetCredential(new System.Uri(sUrl),String.Empty);
oRequest.ContentType = @"text/xml";
oRequest.ContentLength = abData.Length;
oStream = oRequest.GetRequestStream();
oStream.Write(abData,0,abData.Length);
oStream.Close();
// waiting for response
try
{
oResponse = (System.Net.HttpWebResponse) oRequest.GetResponse();
oRequest = null;
}
catch(System.Exception e)
{
Trace.WriteLine("SendSearchRequest: " + e.Message);
}
if (oResponse != null)
{
oStream = oResponse.GetResponseStream();
// get data from stream
if (oStream != null)
{
try
{
xmldoc = new XmlDocument();
if (xmldoc != null)
{
xmldoc.Load(oStream);
}
}
catch(System.Exception e)
{
Trace.WriteLine("SendSearchRequest: " + e.Message);
}
oStream.Close();
oStream = null;
}
}
oResponse = null;
}
return xmldoc;
}
传入参数分别为,需要查询的根路径(可以认为是表名,这里为HTTP URL)和查询语句(格式见上一节),返回的结果为包含查询结果的XMLDocument实例。
3-3-2、ADO。
Exchange提供了两种Provider,ExOLEDB和MSDAIPP,可供ADO调用,但是只有MSDAIPP这个Provider支持全文检索,而且当在Exchange安装的主机上采用MSDAIPP进行全文检索时,会产生一些不可知的错误,比如挂起之类,所以建议采用WebDAV。
ADO的示例如下:
private ADODB.RecordsetClass GetQueryResult(System.String sUrl,System.String sQuery)
{
ADODB.RecordsetClass rsResult = null;
ADODB.ConnectionClass cnnExchange = null;
ADODB.CommandClass cmdQuery = null;
System.Object objAffectedRecords = null,objParams = null;
if (sUrl == null || sUrl == String.Empty)
return null;
if (sQuery == null || sQuery == String.Empty)
return null;
try
{
cnnExchange = new ConnectionClass();
if (cnnExchange == null)
return null;
cnnExchange.Provider = "provider=msdaipp.dso";
cnnExchange.Open(sUrl,String.Empty,String.Empty,0);
}
catch (System.Exception e)
{
Trace.WriteLine("GetQueryResult: Create connection failed! " + e.Message);
cnnExchange = null;
return null;
}
cmdQuery = new CommandClass();
if (cmdQuery != null)
{
cmdQuery.ActiveConnection = cnnExchange;
cmdQuery.CommandType = CommandTypeEnum.adCmdText;
cmdQuery.CommandText = sQuery;
try
{
rsResult = (ADODB.RecordsetClass) cmdQuery.Execute(out objAffectedRecords,ref objParams,0);
}
catch (System.Exception e)
{
Trace.WriteLine("GetQueryResult: Query data failed! " + e.Message);
}
cmdQuery = null;
}
cnnExchange = null;
return rsResult;
}
传入参数分别为,需要查询的根路径(可以认为是表名)和查询语句,返回的结果为包含查询结果的Recordset实例。需要强调的是MSADIPP的Provider不支持在打开连接时指定访问用户名和口令。
以上就是在Exchange下全文检索的简要介绍。有关IFilter和存储引擎开发的要点,等有机会再进一步阐述。
参考文档
,