项目背景:
A软件开发公司曾给B公司开发了一套信息管理系统。随着时间的积累,B公司的服务上积存了大量的文档。原来系统只提供了按照文档标题和设定的有限个关键字查询的功能,没有提供根据文档任意给定内容检索的功能。现在按照这种方式查询已经满足不了客户的要求,更要命的是查询出来结果太少,漏查了很多记录。因此客户希望能在原来的系统基础上开发出一套强大的文档检索系统。
开发时间:两个月
开发人数:三人(老刘,小明和我)

由于之前我们没有文档检索的开发经验,我们三人完全是从零开始摸索。所以这对于我们这个小开发团队来说,时间很紧,任务很重!

那么我们如何对付这项任务呢?

于是第一天上午,我们开了个会。 主要讨论了现在可以采取哪些方案来解决这个问题。大致我们列出了以下几种可能采用的方案:

一)自己开发一套搜索系统;
二)下载并修改现成开源的搜索系统;
三)基于现成的产品(如MSN desktop search, google desktop, sharepoint)做二次开发;

对于方案一,考虑了我们现有的人力资源和所有的时间,显然是不行的。即使时间很充足,开发搜索系统有很高的技术难度。 放弃!

对于方案二,我们当初找了SQLET,Lucene等几款开源的项目。由于项目组其他两人对使用开源不太放心,而且我也对其没有充分的把握。 很快,方案二也被否定了。

接下来我们只好探讨研究方案三。我们下载安装了MSN desktop search, 待搜索爬虫整理信息完毕。输入一个关键字去搜索,发现居然如此好用。反复测试后,觉得他完全符合我们检索文档的标准,不觉大家心里为之一震。可惜继续往下研究发现,Live Desktop Search并没有给开发人员提供可编程的接口。此路照样不通!

此时时间不只不觉已经过去1周。正当我们处于焦急无奈时,老刘从微软的官方网站上找到用"sharepoint"提供了"全文检索"方面的功能,SPS提供了sdk,开发人员能够对此进行扩展编程,而且找到***公司曾用sharepoint有过成功的文档检索案例。这更加坚定了我们的信心。经过一番仔细研究调查,决定使用SharePointQueryEx Web Service搜索功能。

此时,虽然时间已经过了2周了。但我们为找到了一个正确的方案,仍然对能够完成任务非常有信心。

接下去,我们就进入了利用SharePoint 2003的Web服务搜索功能进行开发之旅:

第一步: 安装SharePoint并建立门户网站。

就这一步,我们就遇到了很多的麻烦。好几次在建立门户网站的时候,当安装进度条走到51%的时候,创建站点都失败了。经过反复好几次的安装,始终报错!如何是好? 后来又仔细阅读了安装说明,干脆重装系统,然后完全按照说明书进行安装。功夫不负有心人,最后个人门户终于安装成功了。在这里要特别提醒:安装SharePoint必须按照《快速安装指南》的系统要求去做。

第二步:配置搜索

1)添加一个内容索引(spsindex)
2)添加一个内容源(ss),在选择内容索引中选择刚才建立的索引(spsindex)在内容源中选择"文件共享"。表示要搜索的是共享的文件。
3)  指定内容源为增量更新。
4)  选择完全更新。

待内容索引更新完毕后,返回门户首页。在首页的搜索栏里选择内容源后输入某个关键字进行搜索。

如果能搜索出类似如下结果,那么说明前面的配置都没问题,可以继续往下进行扩展开发了。



第三步:利用QueryEx Web Service进行编程

SPS的提供的Web服务有这样一个方法:QueryEx(string)。他按照构造出的string条件进行查询并返回一个DataSet。

主要的代码如下所示[其中 strIndex就是刚才创建的内容索引(spsindex), SearchKeyWords就是搜索的关键字]:

 1 public DataSet GetQueryResult(string strIndex, string SearchKeyWords)
 2    {
 3        string sql = "SELECT \"DAV:href\",";
 4        sql += "\"DAV:displayname\",";
 5        sql += "\"DAV:getlastmodified\",";
 6        sql += "\"DAV:getcontentlength\",";
 7        sql += "\"urn:schemas-microsoft-com:office:office#Author\",";
 8        sql += "\"urn:schemas.microsoft.com:fulltextqueryinfo:description\",";
 9        sql += "\"urn:schemas.microsoft.com:fulltextqueryinfo:displaytitle\",";
10        sql += "\"urn:schemas.microsoft.com:fulltextqueryinfo:rank\",";
11        sql += "\"urn:schemas.microsoft.com:fulltextqueryinfo:sitename\",";
12        sql += "\"DAV:getcontenttype\",";
13        sql += "\"urn:schemas.microsoft.com:fulltextqueryinfo:sdid\",";
14        sql += "\"urn:schemas-microsoft-com:sharepoint:portal:objectid\"";
15        sql += "  from ( TABLE Portal_Content..Scope() UNION ALL TABLE "+strIndex+")";
16        sql += " where   WITH 
17
18(\"DAV:contentclass\":0,\"urn:schemas.microsoft.com:fulltextqueryinfo:description\":0,\"urn:schemas.microsoft.com:fulltextque
19
20ryinfo:sourcegroup\":0,\"urn:schemas.microsoft.com:fulltextqueryinfo:cataloggroup\":0,\"urn:schemas-microsoft-
21
22com:office:office#Keywords\":1.0,\"urn:schemas-microsoft-com:office:office#Title\":0.9,\"DAV:displayname\":0.9,\"urn:schemas
23
24-microsoft-com:publishing:Category\":0.8,\"urn:schemas-microsoft-com:office:office#Subject\":0.8,\"urn:schemas-microsoft-
25
26com:office:office#Author\":0.7,\"urn:schemas-microsoft-com:office:office#Description\":0.5,\"urn:schemas-microsoft-
27
28com:sharepoint:portal:profile:PreferredName\":0.2,contents:0.1,*:0.05) AS #WeightedProps ";
29        sql += " ((\"urn:schemas-microsoft-com:publishing:HomeBestBetKeywords\"= some array ['" + SearchKeyWords + "'])";
30        sql += " OR (FREETEXT(\"urn:schemas-microsoft-com:sharepoint:portal:profile:PreferredName\", '" + SearchKeyWords + 
31
32"'))";
33        sql += " OR FREETEXT(#WeightedProps, '" + SearchKeyWords + "'))";
34
35        DataSet ds = new DataSet();
36
37        QueryService.QueryService myQuery;
38        string QueryString;
39        try
40        {
41            myQuery= new QueryService.QueryService();
42            myQuery.PreAuthenticate = true;
43            myQuery.Credentials = new NetworkCredential(@"administrator""password""domainname");
44
45            if (myQuery.Status().ToUpper() == "ONLINE")
46            {
50                    QueryString = @"<QueryPacket xmlns='urn:Microsoft.Search.Query' revision='1'>"
51                    + "<Query>"
52                    + "<QueryId>Client Query ID</QueryId>"
53                    + "<OriginatorId>SmartSearch Ver.1</OriginatorId>"
54                    + "<SupportedFormats>"
55                    + "<Format>urn:Microsoft.Search.Response.Document.Document</Format>"
56                    + "</SupportedFormats>"
57                    + "<Context>"
58                    + "<QueryText language='en-US' type='MSSQLFT'>"
59                    + "<![CDATA["
60                    + sql
61                    + "]]>"
62                    + "</QueryText>"
63                    + "<LanguagePreference>en-US</LanguagePreference>"
64                    + "</Context>"
65                    + "<Range>"
66                    + "<StartAt>1</StartAt>"
67                    + "<Count>500000</Count>"
68                    + "</Range>"
69                    + "</Query>"
70                    + "</QueryPacket>";
71
72                ds = myQuery.QueryEx(QueryString);
73            }

74            else
75            {
76                throw new Exception("ERROR!");
77            }

78        }

79        catch(Exception e)
80        {
81            this.Response.Write(e.ToString());
82        }

83        return ds;
84    }

85

GetQueryResult(string, string)返回的是一个数据集。我们可以遍历这个数据集,将每条记录加以权限过滤,得到的就是想要的结果。

这个项目经过2个月零一周的开发,终于顺利地在客户的服务器上正常跑起来了。虽然项目的完成时间比预期拖后了一周,但是客户用起来效果还不错,他们也体谅我们很辛苦,对此也很满意!


经过这次项目,我总结了一下我们几点经验:

一)团队合作
        关于团队合作,有句俗话说"三个臭皮匠,顶个诸葛亮",又有广为流传的说法"一个和尚有水喝,两个和尚抬水喝,三个和尚没水喝"。那么如何使团队的成员成为三个臭皮匠,而不是三个和尚呢?
       首先,团队的成员需要有一个共同的目标。有了共同的目标,大家的作用力才会沿着一个方向,而不被分解。
       其次,团队的成员互相支持,互相信任。特别在个人遇到困难的时候,得到同事的支持和帮助对于个人的鼓励极大。
        在本次项目过程中,我们三人的小团队也算是做到了这两点,因此我们最终没有被困难所吓倒,大家围绕着共同的目标展开工作,团结一致,努力战胜了困难。

二)制定计划
        在项目开始前,我们要制定一个详细的计划,制定的计划越详细越好。不要怕耽误写计划和讨论计划所划费的那点时间。"坎材不误磨刀功", 实践已经证明,制定一个周密的计划对于项目按正常的进度进展有百利而无一害。有了计划我们可以按照和实际的实施作对比,及时对开发实施做正确的微调,从而有效控制项目开发的时间和成本。假如没有计划,盲目开发,那往往是陷入"脚踏西瓜皮,溜到哪里算哪里"的糟糕情况。有时不但没有随着西瓜皮往前溜,  甚至西瓜皮往后溜,人还摔了重重的一跤。可谓是"赔了夫人有折兵"!
        在本次项目实施中,我们就因没有制定一个好的计划,  结果后期加了不少的班,最后项目还出现了稍微延期的情况。虽然我们也大致制定了计划,使我们的进展没有偏离太远,否则很可能会吃更大的亏了。

三)不要在一棵树上吊死
        做项目途中要是遇到难题,一时没有解决,不妨休息一下,换一种思维去思考一下问题。如果实在有问题解决不了,不如干脆另辟蹊径。当你"山穷水复疑无路"时,换一种方式去思考问题,往往会"柳暗花明又一村"。

四)要有自信心
        自信心太重要了!假如面对困难,连最起码的信心都没有,那么怎么能够战胜困难呢?!


以上就是这次开发的一点总结,写出来愿与大家共享!