关闭

开源搜索项目-倒排索引代码解析(一)

标签: lucenepathstring存储数据结构搜索引擎
4964人阅读 评论(0) 收藏 举报
分类:

转载:http://www.mingyuanfeng.co.cc/search/label/%E5%80%92%E6%8E%92%E7%B4%A2%E5%BC%95%EF%BC%8C%E5%BC%80%E6%BA%90%EF%BC%8C%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E


搜索引擎的底层索引使用一种叫“倒排索引”(inverted index)的索引结构,倒排索引的结构容易理解,一张词(term)列表,加相应的docID链表。
貌似“简单”的倒排索引实际实现起来却并不那么容易,在内存中如何设计数据结构,在文件块中如何存储,如何压缩,如何加速查询,这些绝不是纸上画一下倒排索引图就可以的。witten 在其大作 Managing Gigabytes: Compressing and Indexing Documents and Images 中洋洋洒洒写了一大本,JUSTIN ZOBEL也在其大作Inverted Files for Text Search Engines中写了50多页,可见倒排索引不是那么"简单"的....


实践出真知,lucene和lemur的代码比较"大",刚开始以小的项目入手,查了一下刚好有个适合的范本,http://www.oschina.net/ 开源中国里面有一个小的索引项目,叫卢福福搜索引擎 luyfsearch,名字倒是很可爱~~

google主页:

oschina主页:

正像作者自己所言,这个项目无法实际应用,但是作为学习范本确实不错,里面的编程方法都模仿了lucene的实现。


先看一个典型的做索引的API函数流程,有一个感性认识:
这个代码片段与用lucene建索引的过程是非常相近的。
IndexWriter *indexWriter=new IndexWriter(); //新建IndexWriter
indexWriter->open("/home/luyf/index/",true); //打开索引目录
indexWriter->setBufferDoc(2);
Document doc; //新建document 对象
Field *t=new Field();

/******域的构造*******************/
t->type= STORE_LONG;
t->is_store=STORE_YES;
t->token=STORE_KEYWORD;
t->name="t";
t->data="22";
std::string nn="t";
doc.addField(nn,t); //把域塞到document中
Field *tx=new Field();
tx->type= STORE_STRING;
tx->is_store=STORE_YES;
tx->token=STORE_KEYWORD;
tx->name="tx";
tx->data="you and me";
std::string nnx="tx";
doc.addField(nnx,tx); //该document中有二个域
Document doc1;
Field *t1=new Field();
t1->type= STORE_LONG;
t1->is_store=STORE_YES;
t1->token=STORE_KEYWORD;
t1->name="t";
t1->data="22";
doc1.addField(nn,t1);
Field *tx1=new Field();
tx1->type= STORE_STRING;
tx1->is_store=STORE_YES;
tx1->token=STORE_KEYWORD;
tx1->name="tx";
tx1->data="you and you";
doc1.addField(nnx,tx1);

indexWriter->add( doc); //添加document
indexWriter->add( doc1);
delete indexWriter; //消亡,同时写磁盘





document对象
document对象的本质是一个map,即 map m_fieldMap;
其中第一个string 表示 Field的名字。 Field可以理解为document的一个属性,例如,对于从网络中爬取的一篇文档,其属性包括网页内容,该网页的url,则可以建二个Field,一个Field命名为 context,代表网页内容相关的文本,一个Field命名为url,代表网页的url。

Field的声明:
class Field{
public:
int type; //存储类型
int is_store; //是否存储 ,这里的存储是指原文照单全存
int token; //是否切词, 与上面的存储的区别是仍掉的停用词
std::string name; //字段名字
std::string data; //字段数据

public:
Field();
virtual ~ Field();
};





IndexWriter 对象:
写索引对象,包含成员较多,说得直白点就是封装了一坨文件指针,然后操控这些文件指针存储索引。倒排索引在内存中的主要数据结构形式为map,在磁盘中以二进制文件存储。
先看 IndexWriter的主要成员函数,open(char* ,bool)与 add(document)



open函数是在IndexWriter初始化后,使用前必须调用的函数,其参数为索引文件所在文件夹位置。 create参数表示是新建索引,or追加索引

void IndexWriter::open(const char * filepath, bool create) {
m_strPath=string(filepath);
/**********三个总体指标****************/
docCount=0; //包含的document数量
fieldCount=0; //包含的域的数量
termCount=0; //term的数量
/*************************/
isCreate=create;
int len=strlen(filepath)+5;
path=new char[len];
strcpy(path, filepath);
p_segment=new char[len+20];
strcpy(p_segment,filepath);
strcat(p_segment,"segments"); //p_segment就是segments文件的路径,segments是一个独立的文
//件,在老版本的lucene索引文件中存在
//p_segment = filepath+"segments"
indexName=new char[10]; //indexName 才是当前操作的索引的名字,segments文件实际上是
//用来存储indexName的名字,segments文件内是一坨坨的indwxName
if(create)
strcpy(indexName,"1"); //indexName以“1”,"2","3","4","5","6"......这样的顺序命名
else
getSegName(indexName); //如果不是新建索引,就到segments文件中去寻找当前索引名,即编号

strcat(path, indexName); //path = path+indexName, indexName的作用显现
initSegment(create);
p_tii=new char[len+20];
strcpy(p_tii, path);
strcat(p_tii, ".tii"); //一个新的文件名,叫 path+indexName+“.tii
p_tiiOutput=new IndexOutput(p_tii,true); //建立该文件,p_tiiOutput 封装了文件指针
p_tiiOutput->writeLong(0); //初始值

p_del=new char[len+20];
strcpy(p_del, path);
strcat(p_del, ".del"); //新文件: path+indexName+".del"
p_delOutput=new IndexOutput(p_del,true);
p_delOutput->writeLong(0);
p_pro=new char[len+20];
strcpy(p_pro, path);
strcat(p_pro, ".pro"); //新文件,path+indexName+".pro"
p_proOutput=new IndexOutput(p_pro,true);

p_fdi=new char[len+20];
strcpy(p_fdi, path);
strcat(p_fdi, ".fdi"); //.fdi

p_fdiOutput=new IndexOutput(p_fdi,true);
p_fdiOutput->writeLong(0);//首先写总数,这个先写0,等写完后再回到开头之上覆盖

p_fdt=new char[len+20];
strcpy(p_fdt, path);
strcat(p_fdt, ".fdt"); //.fdt
p_fdtOutput=new IndexOutput(p_fdt,true);


p_fmn=new char[len+20];
strcpy(p_fmn, filepath);
strcat(p_fmn, "seg.fmn"); //.seg.fmn
if (!create)
loadfmn(p_fmn);
p_fmnOutput=new IndexOutput(p_fmn,create);
if (create)
p_fmnOutput->writeInt(0);
}




0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:3558002次
    • 积分:33312
    • 等级:
    • 排名:第146名
    • 原创:75篇
    • 转载:1124篇
    • 译文:0篇
    • 评论:191条
    最新评论