您的位置:首页 > 其它

【北大天网搜索引擎TSE学习笔记】第8节——检索关键词+结果排序

2013-02-02 12:39 441 查看
        这一节将介绍搜索功能入口程序TSESearch.cpp的第四步和第五步——检索关键词+结果排序。

        其实,检索关键词非常简单,因为已经建立了倒排表mapBuckets,它是一个map结构,检索某个关键词,就是用map的find方法查询(下面代码中的第27行),没有什么需要介绍的。也因此,TSE中将检索关键词和结果排序在一起实现,也就是main函数中调用的函数:iQuery.GetRelevantRst(vecTerm, mapBuckets, setRelevantRst)。

下面就看该函数的源代码,分析TSE中检索关键词和结果排序的实现。TSE的结果排序方法很简单,就是采用的词频(Term frequency)进行排序的,关于词频排序可以到网上搜索TF-IDF词条进行学习,这里不做详细说明。简单说,词频排序就是:网页中某个关键词出现次数(词频)越高,说明该网页与该关键词相关性越高,因而在结果排序中越优。

        下面的代码中加入了详细的注释(以“LB_C”开始的注释为我加入的)进行说明。

//LB_c: vecTerm中存储的用户查询字串分词以后的关键词,mapBucket为倒排表,setRelevantRst存储最终的查询的结果(里面存储
// 的是结果网页的docid)
bool CQuery::GetRelevantRst(vector<string> &vecTerm,
map<string,string> &mapBuckets,
set<string> &setRelevantRst) const
{
//LB_c: 临时存储已经查询的结果
set<string> setSRst;
bool bFirst=true;

//LB_c: 分别对vecTerm中的每一个关键词进行查询
vector<string>::iterator itTerm = vecTerm.begin();
for ( ; itTerm != vecTerm.end(); ++itTerm ){

//LB_c: setRelevantRst为已查询的结果,将setRelevantRst存入setSRst中,后面将用到。
setSRst.clear();
copy(setRelevantRst.begin(), setRelevantRst.end(),
inserter(setSRst,setSRst.begin()));

//LB_c: mapRstDoc是一个用于临时统计的map,string对应一个docID,int是该docID出现的次数(也就是当前关键词在docid
// 的网页中出现的次数,也成为"词频",后面将称为"词频"), 后面是根据词频值对搜索结果进行排序的(即关键词出现次数
// 越多的网页应该越"优")。
map<string,int> mapRstDoc;
string docid;
int doccnt;
//LB_c: 在倒排表中查询关键词(*itTerm)
map<string,string>::iterator itBuckets = mapBuckets.find(*itTerm);
//LB_c: 在倒排表中找到了该关键词
if (itBuckets != mapBuckets.end()){

//LB_c: 获取该关键词出现的文档ID列表(即倒排表记录的第二项,忘记了的朋友可以看看第2节中倒排文件的结构)
string strBucket = (*itBuckets).second;

string::size_type idx;
idx = strBucket.find_first_not_of(" ");
strBucket = strBucket.substr(idx);

//LB_c: 循环从文档ID列表字符串中获取一个文档ID,并计算词频,插入mapRstDoc中
while ( (idx = strBucket.find(" ")) != string::npos ) {
docid = strBucket.substr(0,idx);
if (docid.empty()) continue;
doccnt = 0;	//LB_c: 计算词频
//LB_c: 到mapRstDoc中查询该docid是否出现过
map<string,int>::iterator it = mapRstDoc.find(docid);
//LB_c: 如果docid出现过
if ( it != mapRstDoc.end() ){
//LB_c: 获取词频((*it).second)加1存入doccnt
doccnt = (*it).second + 1;
//LB_c: 从mapRstDoc把该条记录删除,下面将重新插入
mapRstDoc.erase(it);
}

//LB_c: 将该条记录重新插入到mapRstDoc,其实先删除再插入这条记录的结果就是docid的词频加了1
//LB_c: 这里应该有点问题! 如果docid没出现过,那么doccnt的值为0,则插入到mapRstDoc的对应于docid
// 的词频0,所以前面doccnt的初值是不是应该为1呢?
mapRstDoc.insert( pair<string,int>(docid,doccnt) );

//LB_c: 去掉分析过的docid更新strBucket,继续分析下一个文档ID
strBucket = strBucket.substr(idx+1);
}

//LB_c: 下面这部分是处理strBucket中最后一个docid,因为while循环结束时,最后一个docid还没有处理
// remember the last one
docid = strBucket;
doccnt = 0;
map<string,int>::iterator it = mapRstDoc.find(docid);
if ( it != mapRstDoc.end() ){
doccnt = (*it).second + 1;
mapRstDoc.erase(it);
}
mapRstDoc.insert( pair<string,int>(docid,doccnt) );
}
//LB_c: 这一部分处理完后,mapRstDoc存储的是一系列docid和该docid的词频。

// sort by term frequencty
//LB_c: 这部分是对刚才的带有词频的文档查询结果mapRstDoc进行了排序,排序结果存入到newRstDoc中。
//LB_c: 注意newRstDoc的类型,第一个域为int表示docid的词频,第二个域是string表示docid,第三个域
// 是排序规则----以键值(词频)的降序排列,注意newRstDoc是multimap,也就是键值可以重复。
multimap<int, string, greater<int> > newRstDoc;
map<string,int>::iterator it0 = mapRstDoc.begin();
for ( ; it0 != mapRstDoc.end(); ++it0 ){
newRstDoc.insert( pair<int,string>((*it0).second,(*it0).first) );
}

//LB_c: 这部分是将当前关键词(*itTerm)的排序查询结果newRstDoc插入到最终的查询结果setRelevantRst中,
// 这里要参考前面的关键词查询结果。
multimap<int,string>::iterator itNewRstDoc = newRstDoc.begin();
//LB_c: 将最终的查询结果setRelevantRst清空
setRelevantRst.clear();
//LB_c: 循环读取newRstDoc中的每一条记录(这些记录是按docid的词频排序的),根据情况插入到最终结果中
for ( ; itNewRstDoc != newRstDoc.end(); ++itNewRstDoc ){

//LB_c: 获取该条记录的docid
string docid = (*itNewRstDoc).second;
//LB_c: 如果当前关键词是第一个查询的关键词,则直接插入到结果集中
if (bFirst==true) {
setRelevantRst.insert(docid);
continue;
}

//LB_c: 如果不是第一个关键词查询,则看已查询结果集setSRst中是否有该docid,也就是前面查询的关键词
// 有没有出现在docid的网页中。这里也体现了TSE搜索的规则: 只有所有关键词都出现的网页才有效,部分关键
// 词出现的网页不作为搜索结果。如果setSRst中有该docid说明docid的网页也包含前面查询的关键词, 则将
// docid插入到最终结果集setRelevantRst中。
if ( setSRst.find(docid) != setSRst.end() ){
setRelevantRst.insert(docid);
}
}

//LB_c: 这里思考一下! 首先将setRelevantRst清空,然后将当前关键词(*itTerm)的排序结果newRstDoc插入到最终结果集
// setRelevantRst中。也就是最终的结果排序是以最后一个查询关键词的词频排序的,即最后一个关键词出现次数多的网页
// 排在前面。

bFirst = false;
}

return true;
}


这一节之后,关于TSE查询服务子系统的介绍就只剩下最后一节——显示搜索结果了,本来想年前把它写完的,由于明天回家过年了,是在没有时间了,所以只能年后再继续了。回家happy去啦~~ 也预祝各位读者新年快乐,工作顺利~~

By:


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