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

转载 2012年03月31日 16:08:55

转载: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);
}




相关文章推荐

倒排索引的java实现

假设有3篇文章,file1, file2, file3,文件内容如下:    文件内容代码   file1 (单词1,单词2,单词3,单词4....)      file2 (单词a,单词...

倒排索引算法的Java实现

/* 需求:为文件建立倒排索引 step1: |--为所有的文件建立索引号 FileID_Number |--首先查找到所有的文件目录 file.list[] |--将...
  • lanonjj
  • lanonjj
  • 2016年03月03日 22:29
  • 2870

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

C++ 倒排索引的实现

1.1基本介绍  倒排索引的概念很简单:就是将文件中的单词作为关键字,然后建立单词与文件的映射关系。当然,你还可以添加文件中单词出现的频数等信息。倒排索引是搜索引擎中一个很基本的概念,几乎所有的搜索...

倒排索引详解及C++实现

1.介绍  倒排索引是现代搜索引擎的核心技术之一,其核心目的是将从大量文档中查找包含某些词的文档集合这一任务用O(1)或O(logn)的时间复杂度完成,其中n为索引中的文档数目。也就是说,利用倒排索引...

c# 扩展方法奇思妙用基础:Dictionary<TKey, TValue> 扩展

c# 扩展

文本文件单词的检索及计数

要求编程建立一个文本文件,每个单词不包括空格及跨行,单词由字符序列构成且区分大小写,完成以下功能:统计给定单词在文本文件中出现的总次数、检索输出某单词在文本文件中首次出现的行号及位置。     思路:...
  • linnzl
  • linnzl
  • 2016年05月22日 23:33
  • 1204

mapreduce之倒排索引代码

倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inve...

全文搜索Lucene——之倒排索引

全文搜索Lucene——之倒排索引 关系数据库不适合做全文搜索:     like '%xxx%'效率很慢,建的索引将无效,查询的时候会像翻书一样一页一页的翻     返回的结果没有匹配度的...

solr中文搜索倒排索引和数据存储结构

作为搜索,本文章原地址:http://blog.csdn.net/chunlei_zhang/article/details/38520315我们传统的方式(正排索引)是从关键点出发,然后再通过关键点...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:开源搜索项目-倒排索引代码解析(一)
举报原因:
原因补充:

(最多只允许输入30个字)