您的位置:首页 > 其它

【北大天网搜索引擎TSE学习笔记】第9节——显示搜索结果

2013-03-05 20:44 501 查看
这一节将介绍搜索功能入口程序TSESearch.cpp的第六步——显示搜索结果。该部分中首先定义了一个结果显示类CDisplayRst的对象iDisplayRst,然后调用了该类的三个成员函数ShowTop,ShowMiddle和ShowBelow分别显示结果页面的头部、中部和底部(即图1中标注①②③的区域)。下面来看一下这三个函数的源代码,代码中加入了详细的注释(以“LB_C”开始的注释为我加入的)进行说明,另外再源代码之后还有一些问题的分析。



1、ShowTop

bool CDisplayRst::ShowTop()
{
string strHost = string(getenv("HTTP_HOST"));
//LB_c: 结果网页的body标签
cout << "<body bgcolor=#ffffff topmargin=2 marginheight=2>"
<< "<table class=border=0 width=100% cellspacing=0 cellpadding=0 height=29>" << endl
<< "<tr>" << endl
//LB_c: 左上角的"天网搜索"的logo图片,给该图片加了一个到strHost/yc/TSE/的链接(strHost是网站root目录,即apache配置的网站目录,
//前面已经设置为/var/www/html/)。这样,点击logo会打开strHost/yc/TSE/中的index.html,该网页与strHost中的index.html实际上是一样的,
//也就是搜索主页,不知道为什么不直接链接到strHost中的index.html?
<< "<td width=36% rowspan=2 height=1>" << "<a href=http://"
<< strHost << "/yc/TSE/><img border=0 src=/yc/TSE/tsetitle.JPG width=308 height=65></a></td>" << endl
//LB_c: 顶部的"搜索主页"和"使用帮助"链接,前者与上面的logo链接一样,后者链接到一个网络上的帮助手册。
<< "<td width=64% height=33 ><font size=2><a href=http://"
<< strHost << "/yc/TSE/>搜索主页</a>| <a href=http://e.pku.edu.cn/gbhelp.htm>使用帮助</a> </font><br></td>" << endl
<< "</tr>" << endl;

//LB_c: 这里在网页顶部构建了一个新查询的form,包括搜索框、搜索按钮和隐含的显示页码键值对
cout << "<tr>" << endl
<< "<td><p align=\"left\">" << endl
<< "<form method=\"get\" action=\"/yc-cgi-bin/index/TSESearch\" name=\"tw\">" << endl
//LB_c: 搜索输入框
<< "<input type=\"text\" name=\"word\" size=\"55\">" << endl
//LB_c: 新查询按钮,注意该input标签中没有设置name
<< "<INPUT TYPE=\"submit\" VALUE=\" 新查询 \"> " << endl
//LB_c: 附加的键值对,键名为"start",值为1,这里是指示显示搜索结果的第1页。
<< "<input type=\"hidden\" name=\"start\" value=\"1\">" << endl
<< "</form>" << endl
<< "</tr>" << endl
<< "</table>" << endl;

//LB_c: 中间蓝色的横条,上面写有"图片"
cout << "<table border=0 width=100% cellspacing=1 cellpadding=0 height=1>" << endl
<< "<tr>" << endl
<< "<td width=68 align=center bgcolor=#000066 valign=middle><font size=2><b><font color=#FFFFFF>图  片</font></b></font></td>"
<< endl
<< "</tr>" << endl
<< "<tr>"
<< "<td width=100% align=left colspan=3 height=0>"
<< "</td></tr>" << endl
<< "</table>" << endl;

return true;
}


【分析1】:第6节中提到过如果从搜索结果页面中进行新的搜索,显示结果是正常的,为什么呢?看一下代码中的新查询的form,form中有三个input,而只有两个input有name,所以用户点击"新查询"按钮后,提交的URL中有两个键值对(word和start),例如查询"北京大学"得到
http://localhost:8080/yc-cgi-bin/index/TSESearch?word=%B1%B1%BE%A9%B4%F3%D1%A7&start=1, 因此HtmlInputs[1]就对应start键值对,HtmlInputs[1].Value的值为1,所以m_iStart设置为1,即显示结果集的第1页。这里如果在"新查询"按钮的input标签中定义了name,则URL会有三个键值对,第二个键值对对应"新查询"按钮,HtmlInputs[1]就不是start键值对了,所以也会出错! 读者朋友们可以测试一下。

2、ShowMiddle

//LB_c: strQuery为原始的用户查询串,fUsedMsec为搜索耗时,iRstNum为搜索结果的总条数,start为显示结果集的第几页
bool CDisplayRst::ShowMiddle(string strQuery, float fUsedMsec, unsigned iRstNum, unsigned start)
{
//LB_c: iPageNum为结果总页数,RstPerPage为每页显示的条数,是一个常量
unsigned iPageNum = 0;
if (iRstNum%RstPerPage == 0){
iPageNum = iRstNum/RstPerPage;
} else {
iPageNum = iRstNum/RstPerPage + 1;
}

//LB_c: 显示提示信息: 用户查询的串,搜索耗时,共有多少条结果,当前显示的是x到y条
cout << "<title>TSE Search</title>\n"
<< "<font  color=#008080 size=2>" << endl
<< "查找: <b><font color=\"#000000\" size=\"2\">"
<< strQuery << "</b></font>" << endl
<< "费时<b><font color=\"#000000\" size=\"2\">"
<< fUsedMsec
<< "</font></b> 毫秒,共找到<b><font color=\"#000000\" size=\"2\">"
<< iRstNum
<< "</font></b> 篇文档,下面是第 <b><font color=\"#000000\" size=\"2\">";
if (iRstNum == 0){
cout << "0</font></b>到第 <b><font color=\"#000000\" size=\"2\">"
<< "0</font></b>个<br>" << endl;
return true;
}
cout << (start-1)*RstPerPage + 1 << "</font></b>到第 <b><font color=\"#000000\" size=\"2\">";
if (iRstNum >= start*RstPerPage ) {
cout << start*RstPerPage << "</font></b>个    " << endl;
} else {
cout << iRstNum << "</font></b>个    " << endl;
}

//LB_c: 供用户选择的结果页的链接,
cout << "选择页面: " ;
for (unsigned i=0; i<iPageNum; i++) {
//LB_c: 当前页面的页号没有链接
if (i+1 == start ) {
cout << i+1 << "</a>  ";
}
//LB_c: 其他页号提供链接,注意链接是"/yc-cgi-bin/index/TSESearch?word=***&start=***",后面进行说明【分析2】
else{
cout << "<a href=\"/yc-cgi-bin/index/TSESearch?word="
<< strQuery << "&start=" << i+1 << "\">"
<< i+1 << "</a>  ";
}
}

return true;
}


【分析2】:第6节中提到过如果从搜索结果页面中进行新的搜索,显示结果是正常的,为什么呢?上面代码中说明了页号链接为"yc-cgi-bin/index/TSESearch?word=***&start=***"形式,显然第二个键值对是start,所以HtmlInputs[1].Value就是用户点击的页号,所以m_iStart的值也是正确的,因此显示结果是正常的。

另外,由页号链接可以看出,用户点击页号以后又执行了一次cgi程序/yc-cgi-bin/index/TSESearch,即又重新搜索了一次,而不是直接在上次的结果集中取出对应页的内容进行显示,这是为何呢? 这似乎太不合理了,搜索结果集都已经有了,只需简单的实现显示不同的页就行了。

3、ShowBelow

bool CDisplayRst::ShowBelow(vector<string>&vecQuery, set<string> &setRelevantRst,
vector<DocIdx> &vecDocIdx, unsigned start)
{
cout << "<ol>" << endl;

set<string>::iterator it= setRelevantRst.begin();
unsigned iDocNumber=0;
//LB_c: start为用户选择的显示结果集的页号,RstPerPage为每页显示的记录条数,所以这里计算要显示的结果起止序号,
// 即显示iRstBegin到iRstEnd的结果记录。这里也可以看出start页号应该是从1开始的。
unsigned iRstBegin = (start-1)*RstPerPage;
unsigned iRstEnd = start*RstPerPage - 1;

vector<string> vecRefUrl;
vector<string>::iterator itVecRefUrl;
cout << "<tr bgcolor=#e7eefc>";
bool bColor = true;

//LB_c: 打开原始的原始网页数据库,用户点击"网页快照"时要从中读出网页并显示出来,这里也说明网页快照是
//存在服务器的历史数据,而不是打开网址得到的实时网页。
ifstream ifs(RAWPAGE_FILE_NAME.c_str());
if (!ifs) {
cout << "Cannot open " << RAWPAGE_FILE_NAME << " for input\n";
return false;
}

for ( ; it!=setRelevantRst.end(); ++it,iDocNumber++ ){
//LB_c: 这两行判断序号,在setRelevantRst中取出第iRstBegin到第iRstEnd条记录。
if (iDocNumber < iRstBegin ) continue;
if (iDocNumber > iRstEnd ) break;

cout << "<li><font color=black size=2>" << endl ;
//LB_c: 获取结果记录的docid
int docId = atoi( (*it).c_str() );
//LB_c: vecDocIdx在main函数中说明过,是网页索引表(记录docid到offset的映射),这里获取前后两个网页在
//原始网页数据库中的offset,相减得到该网页的长度。
int length = vecDocIdx[docId+1].offset - vecDocIdx[docId].offset;

//LB_c: 建立缓冲区pContent,从原始网页数据库文件中读出该网页数据
char *pContent = new char[length+1];
memset(pContent, 0, length+1);
ifs.seekg(vecDocIdx[docId].offset);
ifs.read(pContent, length);

char *s;
s = pContent;
string url,tmp = pContent;
string::size_type idx1 = 0, idx2=0;

//LB_c: 从网页数据中把url提取出来
idx1 = tmp.find("url: ");
if( idx1 == string::npos ) continue;
idx2 = tmp.find("\n", idx1);
if( idx1 == string::npos ) continue;
url = tmp.substr(idx1+5, idx2 - idx1 - 5);

//LB_c: vecQuery在main函数中介绍过,是搜索串分割以后的关键词,这里将这些关键词用"+"连接起来
//在网页快照中显示,用于提示用户。
string word;
for(unsigned int i=0; i< vecQuery.size(); i++){
word = word + "+" + vecQuery[i];
}
word = word.substr(1);

//========================================================================================================
//LB_c: 以下输出每条结果记录的具体内容,包括: 网页的链接,网页长度,网页快照链接和网页内容摘要

//LB_c: 网页快照链接到另一个cgi程序: /yc-cgi-bin/index/Snapshot,即点击"网页快照"后,由cgi程序
///yc-cgi-bin/index/Snapshot来处理。后面进行说明【分析3】
cout << "<a href=" << url << ">" << url << "</a>, "
<< length << "<font  color=#008080>字节</font>" << ", "
<< "<a href=/yc-cgi-bin/index/Snapshot?"
<< "word=" << word << "&"
<< "url="<< url
<< " target=_blank>"
<< "[网页快照]</a>"
<< endl << "<br>";

if (length > 400*1024) {    // if more than 400KB
delete[] pContent;
continue;
}

//LB_c: 以下是从网页数据中提取正文,然后从正文中提取网页摘要,并进行显示。这里不详细解释。
// skip HEAD
int bytesRead = 0,newlines = 0;
while (newlines != 2 && bytesRead != HEADER_BUF_SIZE-1) {
if (*s == '\n')
newlines++;
else
newlines = 0;
s++;
bytesRead++;
}
if (bytesRead == HEADER_BUF_SIZE-1) continue;

// skip header
bytesRead = 0,newlines = 0;
while (newlines != 2 && bytesRead != HEADER_BUF_SIZE-1) {
if (*s == '\n')
newlines++;
else
newlines = 0;
s++;
bytesRead++;
}
if (bytesRead == HEADER_BUF_SIZE-1) continue;

CDocument iDocument;
iDocument.RemoveTags(s);
iDocument.m_sBodyNoTags = s;
delete[] pContent;
string line = iDocument.m_sBodyNoTags;
CStrFun::ReplaceStr(line, " ", " ");
CStrFun::EmptyStr(line); // set " \t\r\n" to " "

// abstract
string reserve;
if ((unsigned char)line.at(48) < 0x80) {
reserve = line.substr(0,48);
}else{
reserve = line.substr(0,48+1);
}
reserve = "[" + reserve + "]";
unsigned int resNum = 128;
if (vecQuery.size() == 1) resNum = 256;
for(unsigned int i=0; i< vecQuery.size(); i++){
string::size_type idx = 0, cur_idx;
idx = line.find(vecQuery[i],idx);
if (idx == string::npos) continue;
if (idx > resNum ) {
cur_idx = idx - resNum;
while ((unsigned char)line.at(cur_idx) > 0x80 && cur_idx!=idx) {
cur_idx ++;
}
reserve += line.substr(cur_idx+1, resNum*2);
}else{
reserve += line.substr(idx, resNum*2);
}
reserve += "...";
// highlight
string newKey = "<font color=#e10900>" + vecQuery[i] + "</font>";
CStrFun::ReplaceStr(reserve, vecQuery[i], newKey);
}
line = reserve;
cout << line << endl << endl;
//========================================================================================================
}

cout << "</ol>";
cout << "<br><br><hr><br>";
cout << "© 2004 北大网络实验室<br><br>\n";
cout << "</center></body>\n<html>";

return true;
}


【分析3】:关于网页快照功能的实现在Snapshot.cpp中,本系列文章中不展开进行详细解释。但是有一点在这里指出一下,从Snapshot.cpp源代码中得知,快照功能处理的cgi程序根据传入的网页url从原始网页数据库中读出网页数据显示出来,而查找网页数据也处理的很复杂,先加载url索引文件,再根据传入url的MD5值到文件中找出相应的docid,然后从原始网页数据库中找到该网页的数据再进行显示。为何这样处理呢?
在ShowBelow中不是已经得到结果网页的网页数据了吗,可以缓存下来,需要显示网页快照时直接取出进行显示不就可以了吗?

By:


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐