























http://www.blogcn.com/User8/flier_lu/blog/20045179.html
最近实在太忙,没空更新blog,只好贴点代码先,呵呵
在建立
企业内容管理服务器时,一个很大的问题是如何处理种类繁多、数量巨大的文件检索和定位问题。虽然大部分关系型数据库提供了 LIKE 语句或面向字段的全
文检索支持,但并非所有数据都适合保存在数据库中。例如一个 SharePoint 建立的知识共享服务器上,存在 DOC, PPT, PDF,
CHM 等不同格式文档文件,面向内容的全文检索如 Lucene 或 MS Search 都无法提供直接的完善支持。而且对检索内容的更新和历史追
述也是大问题,特别是基于文件方式共享时,很难对更新文档的索引实效性和更新的历史做维护。
好在现在有了 Google 桌面搜索(GDS - Google Desktop Search),让这些恼人的问题可以告一段落了,呵呵。
我们所需要做的只是在服务器上安装一套 Google 桌面搜索,配置好允许它索引哪些目录,下载或开发一些特定格式文件的支持。Google 桌面搜索
自身就支持 Word, Excel, Pointpoint, Html, Pdf 等常见格式,通过免费的插件还可以支持诸如 CHM 等特定格式,
也可以很方便开发支持自己特定格式的索引插件。(本系列文章下一篇将以 foxmail 的索引器为例介绍如何开发索引器)而 Google 桌面搜索会
自己处理诸如索引实效性和历史版本(快照)的问题。
而使用 GDS 结果的最简单方法,莫过于直接访问 localhost:4664
的 Web 搜索界面。可以通过 Apache 的 mod_proxy 或类似功能将之转发到对外的页面上。但一个较大问题是这样返回的搜索结果是较难
重用的 HTML 格式,而且路径指向的是服务器上的文件路径,对远程访问不可用。
好在 GDS 提供了查询服务支持,我们可以通过此接口获取我们感兴趣的查询结果,并翻译成远程可用的地址链接。
主要流程如下:
1.检测 GDS 的安装情况,获取其查询请求的key
2.向 GDS 以特定格式发送查询请求
3.解析 GDS 返回的 XML 格式查询结果
4.将查询结果翻译成远程可用形式,并在门户上展示
第一步检测 GDS 的安装实现很简单,只需要测试特定注册表键的存在与否,以及其指向目录的有效性即可。
namespace com.nsfocus.google.search
{
public class SearchEngine
{
private static readonly DirectoryInfo _installDir, _dataDir;
private static readonly String _url = null;
static SearchEngine()
{
using(RegistryKey reg = Registry.CurrentUser.OpenSubKey(@"Software\Google\Google Desktop"[img]/images/wink.gif[/img])
{
try
{
_installDir = new DirectoryInfo((String)reg.GetValue("install_dir"[img]/images/wink.gif[/img]);
_dataDir = new DirectoryInfo((String)reg.GetValue("data_dir"[img]/images/wink.gif[/img]);
if(!_installDir.Exists) _installDir = null;
if(!_dataDir.Exists) _dataDir = null;
_url = (string)reg.OpenSubKey("API"[img]/images/wink.gif[/img].GetValue("search_url"[img]/images/wink.gif[/img];
}
catch(Exception)
{
_url = null;
}
}
}
public static bool IsInstalled
{
get
{
return _installDir != null && _dataDir != null && _url != null;
}
}
public static DirectoryInfo InstallDirectory
{
get
{
return _installDir;
}
}
public static DirectoryInfo DataDirectory
{
get
{
return _dataDir;
}
}
}
}
在得到搜索url后,就可以以特定格式组织查询请求,在一个GET请求中提交所有需要查询的内容。一般来说,查询参数可以分为查询字符串、查询标记、起始记录、返回记录数和返回格式。
查询字符串指定你要搜索的内容,如果内容中有空格,需要以 + 号替代;如果希望搜索一个连续
匹配,需要用 " 号扩起来,并在发送时编码为 %25。查询字符串的编码支持如下:
namespace com.nsfocus.google.search
{
public class SearchEngine
{
public static string EncodeQuery(String query)
{
StringBuilder sb = new StringBuilder(query.Length);
foreach(char ch in query)
{
switch(ch)
{
case ' ':
{
sb.Append('+');
break;
}
case '\"':
{
sb.Append("%22"[img]/images/wink.gif[/img];
break;
}
default:
{
sb.Append(ch);
break;
}
}
}
return sb.ToString();
}
}
[TestFixture]
public class SearchEngineTester
{
[Test]
public void testEncodeQuery()
{
Assert.AreEqual("test", SearchEngine.EncodeQuery("test"[img]/images/wink.gif[/img]);
Assert.AreEqual("test+test", SearchEngine.EncodeQuery("test test"[img]/images/wink.gif[/img]);
Assert.AreEqual("%22test+test%22+%22test+test%22", SearchEngine.EncodeQuery("\"test test\" \"test test\""[img]/images/wink.gif[/img]);
}
}
}
namespace com.nsfocus.google.search
{
[Flags]
public enum SearchFlags : int
{
Default = 0,
SortByDate = 8,
SortByRelevance = 40,
Chat = 128,
Web = 256,
File = 512,
Email = 1024,
}
}
namespace com.nsfocus.google.search
{
public class SearchEngine
{
internal XmlDocument Search(SearchQuery query)
{
StringBuilder sb = new StringBuilder(_url);
sb.Append(EncodeQuery(query.Query));
if(query.Format != null)
sb.Append("&format="[img]/images/wink.gif[/img].Append(query.Format);
if(query.Start > 0)
sb.Append("&start="[img]/images/wink.gif[/img].Append(query.Start);
if(query.Number > 0)
sb.Append("&num="[img]/images/wink.gif[/img].Append(query.Number);
if(query.Flags != 0)
sb.Append("&flags="[img]/images/wink.gif[/img].Append(query.Flags);
XmlDocument doc = new XmlDocument();
using(Stream stream = _client.OpenRead(sb.ToString()))
{
doc.Load(stream);
}
return doc;
}
}
}
以下为引用:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<results count="24945">
<result>
<category>file</category>
<id>46384</id>
<title>SDK Developer Documentation</title>
<url>C:\Documents and Settings\me\My Documents\developerguide.html~</url>
<time>127543068856350000</time>
<snippet>SDK Developer Documentation <b>Google</b> Desktop Search SDK Developer Guide For Users Download Plug-ins Desktop Search Forum For Developers</snippet>
<icon>/file.gif</icon>
<cache_url>http://127.0.0.1:4664/... </cache_url>
</result>...
</results>
namespace com.nsfocus.google.search
{
public enum ResultCategory
{
Email,
Chat,
Web,
File
}
public interface SearchResult
{
ResultCategory Category { get; }
int ID { get; }
String Title { get; }
Uri Url { get; }
DateTime Time { get; }
String Snippet { get; }
String Icon { get; }
Uri CacheUrl { get; }
}
public interface SearchResultSet : IEnumerable
{
SearchQuery Query { get; }
int Count { get; }
int TotalNumber { get; }
SearchResult this[int idx] { get; }
}
}
[STAThread]
static void Main(string[] args)
{
ShowCopyright();
SearchEngine engine = new SearchEngine();
SearchResultSet results = engine.Search(args[0]);
Console.Out.WriteLine("共搜索到 {0} 条匹配记录", results.TotalNumber);
Console.Out.WriteLine();
Console.Out.WriteLine("其中从第 {0}
条开始的 {1} 条记录为:", results.Query.Start,
results.Query.Number);
Console.Out.WriteLine();
foreach(SearchResult result in results)
{
Console.Out.WriteLine("========================================"[img]/images/wink.gif[/img];
Console.Out.WriteLine("Title: " + result.Title);
Console.Out.WriteLine();
Console.Out.WriteLine("URL: " + result.Url.ToString());
Console.Out.WriteLine();
Console.Out.WriteLine(result.Snippet);
}
}
下面是完整的结果集解析代码:
namespace com.nsfocus.google.search.desktop
{
public class SearchResultImpl : SearchResult
{
private readonly SearchResultSetImpl _resultSet;
private readonly ResultCategory _cat;
private readonly int _id;
private readonly String _title, _snippet, _icon;
private readonly Uri _url, _cacheUrl;
private readonly DateTime _time;
public SearchResultImpl(SearchResultSetImpl resultSet, XmlNode result)
{
_resultSet = resultSet;
foreach(XmlNode attr in result.ChildNodes)
{
String value = attr.InnerText.Trim();
switch(attr.Name)
{
case "category":
{
switch(value)
{
case "email":
{
_cat = ResultCategory.Email;
break;
}
case "chat":
{
_cat = ResultCategory.Chat;
break;
}
case "web":
{
_cat = ResultCategory.Web;
break;
}
default:
{
_cat = ResultCategory.File;
break;
}
}
break;
}
case "id":
{
_id = Int32.Parse(value);
break;
}
case "title":
{
_title = value;
break;
}
case "url":
{
_url = new Uri(value);
break;
}
case "time":
{
_time = DateTime.FromFileTime(Int64.Parse(value));
break;
}
case "snippet":
{
_snippet = value;
break;
}
case "icon":
{
_icon = value;
break;
}
case "cache_url":
{
_cacheUrl = new Uri(value);
break;
}
}
}
}
public ResultCategory Category
{
get { return _cat; }
}
public int ID
{
get { return _id; }
}
public String Title
{
get { return _title; }
}
public Uri Url
{
get { return _url; }
}
public DateTime Time
{
get { return _time; }
}
public String Snippet
{
get { return _snippet; }
}
public String Icon
{
get { return _icon; }
}
public Uri CacheUrl
{
get { return _cacheUrl; }
}
}
public class SearchResultSetImpl : SearchResultSet, IEnumerable
{
private readonly SearchEngine _engine;
private readonly SearchQuery _query;
private int _num = 0;
private ArrayList _results = new ArrayList();
public SearchResultSetImpl(SearchEngine engine, SearchQuery query)
{
_engine = engine;
_query = query;
parse(_engine.Search(_query));
}
protected void parse(XmlDocument doc)
{
_num = Int32.Parse(doc.GetElementsByTagName("results"[img]/images/wink.gif[/img][0].Attributes["count"].Value);
_results.Clear();
foreach(XmlNode result in doc.GetElementsByTagName("result"[img]/images/wink.gif[/img])
{
try
{
_results.Add(new SearchResultImpl(this, result));
}
catch(Exception)
{
// do nothing
}
}
}
public SearchQuery Query
{
get { return _query; }
}
public int Count
{
get { return _results.Count; }
}
public int TotalNumber
{
get { return _num; }
}
public SearchResult this[int idx]
{
get { return (SearchResult)_results[idx]; }
}
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return _results.GetEnumerator();
}
#endregion
}
}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。