|
|
Posted on 2005-04-06 23:18 Flier Lu 阅读(3543) 评论(7) 编辑 收藏 网摘
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; } } } }
|
|
注意 GDS 处于安全性或某方面的考虑,在安装时会为每台机器生成一个唯一的查询
key,在发送查询请求时必须附带此 key,否则查询会失败。这个 key 的内容可以通过
search_url 获取到,如笔者机器上 search_url 内容如下:
在得到搜索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, } }
|
| 起始记录、返回记录数分别定义返回的查询结果从哪条记录开始,一次返回多少条,跟数据库操作中的分页很类似。返回格式则直接指定为便于二次分析处理的 xml 格式。 具体完成搜索请求的代码如下:
以下内容为程序代码:
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; } } }
|
| GDS 会根据搜索请求,返回一段 XML 格式的搜索结果,如
以下为引用: <?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; } } }
|
| 然后就可以很方便的使用此封装库进行 GDS 查询,如
以下内容为程序代码:
[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); } }
|
| 对企业搜索服务来说,可以将 URL 转换为远程可用的 Web 地址。
下面是完整的结果集解析代码:
以下内容为程序代码:
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 } }
|
| to be continue... GDS [2] 为 Google 桌面搜索增加 Foxmail 支持
Feedback
>>GDS [2] 为 Google 桌面搜索增加 Foxmail 支持
这个下文什么时候出啊?期待你的大作。
读了你的文章,启发很大
但还是有很多地方不明白
如在何处使用google提供的借口,还有代码中的_client是在何处定义的
敬请赐教!
期待你的大作
有没有可供安装的插件下载啊,wangst0654@gmail.com
hao dong dong,wo yao shi shi
|