Solr 创建索引的原理 源码分析

本次分析以Solr3.3是最新的版本为准,

Solr3.3接收客户端的查询索引,新建索引请求都是通过过滤器SolrDispatchFilter来提交给SolrCore的Eexecute方法的。而WEB

.xml文件里的Servlet都不取作用的。SolrDispatchFilter主要是根据当前请求路径的PATH,客户端发起的请求PATH主要如下几类:

1/admin以adinm开头的则到Solr的管理页面。

2/select,/update这类的为对应的Handler/select即搜索请求,对应的handler为SearchHandler,/update新建,更新索引对应的handler为XmlUpdateRequestHandler,这里主要讲一下Solr是怎么对新建索引的XML格式的请求做解析的。

新建索引的命令如下:

<add><docboost=2.1><fieldname="id">222</field></doc></add>

XmlUpdateRequestHandler类是ContentStreamHandlerBase的子类,处理的方法在ContentStreamHandlerBase类的

handleRequestBody方法,该方法的代码有:

//创建一个xml的解析器,实类为XMLLoaderXMLLoader继承抽象的ContentStreamLoader

ContentStreamLoaderdocumentLoader=newLoader(req,processor);

documentLoader.load(req,rsp,stream);//开始读取要创建的doc信息。

主要代码如下:

对<add>标签的处理如下:

StringcurrTag=parser.getLocalName();

if(currTag.equals(XmlUpdateRequestHandler.ADD)){

XmlUpdateRequestHandler.log.trace("SolrCore.update(add)");

addCmd=newAddUpdateCommand();

booleanoverwrite=true;//thedefault

BooleanoverwritePending=null;

BooleanoverwriteCommitted=null;

//判断add标签是否有其他的属性,主要包过如下几个:

//overwrite,allowDups,commitWithin,overwritePending,overwriteCommitted

for(inti=0;i<parser.getAttributeCount();i++){

StringattrName=parser.getAttributeLocalName(i);

StringattrVal=parser.getAttributeValue(i);

if(XmlUpdateRequestHandler.OVERWRITE.equals(attrName)){

overwrite=StrUtils.parseBoolean(attrVal);

}elseif(XmlUpdateRequestHandler.ALLOW_DUPS.equals(attrName)){

overwrite=!StrUtils.parseBoolean(attrVal);

}elseif(XmlUpdateRequestHandler.COMMIT_WITHIN.equals(attrName)){

addCmd.commitWithin=Integer.parseInt(attrVal);

}elseif(XmlUpdateRequestHandler.OVERWRITE_PENDING.equals(attrName)){

overwritePending=StrUtils.parseBoolean(attrVal);

}elseif(XmlUpdateRequestHandler.OVERWRITE_COMMITTED.equals(attrName)){

overwriteCommitted=StrUtils.parseBoolean(attrVal);

}else{

XmlUpdateRequestHandler.log.warn("Unknownattributeidinadd:"+attrName);

}

}

//checkiftheseflagsareset

if(overwritePending!=null&&overwriteCommitted!=null){

if(overwritePending!=overwriteCommitted){

thrownewSolrException(SolrException.ErrorCode.BAD_REQUEST,

"can'thavedifferentvaluesfor'overwritePending'and'overwriteCommitted'");

}

overwrite=overwritePending;

}

//通过下面三行代码,起作用的就是overwrite

addCmd.overwriteCommitted=overwrite;

addCmd.overwritePending=overwrite;

addCmd.allowDups=!overwrite;

对<doc>标签的处理如下:

elseif("doc".equals(currTag)){

XmlUpdateRequestHandler.log.trace("addingdoc...");

addCmd.clear();

addCmd.solrDoc=readDoc(parser);

}

readDoc方法主要是读取<doc>标签和<field>的读取<doc>代码如下:

//Solr把Luence的Doc封装为SolrInputDocument

SolrInputDocumentdoc=newSolrInputDocument();

Stringattrname="";

for(inti=0;i<parser.getAttributeCount();i++){

attrName=parser.getAttributeLocalName(i);

if("boost".equals(attrName)){

doc.setDocumentBoost(Float.parseFloat(parser.getAttributeValue(i)));

}else{

XmlUpdateRequestHandler.log.warn("Unknownattributedoc/@"+attrName);

}

}

从上面的代码可以看出,<doc>标签可以有属性boost,(<docboost="2">)即可以给该doc设置权重。以改变评分。

读取field的代码就不贴了,相信读者也会想到和上面的是一个模式,会读取field的name,boost,属性,像<doc>一样,

我们也可以给特定的field设置权重。遇到</field>标签时,代码如下:

doc.addField(name,text.toString(),boost);

boost=1.0f;

从上面两行代码可以看出,每一个field字段默认的boost为1.0.

Solr解析完<add>命令,创建一个SolrInputDocument对象,那Solr是怎么把SolrInputDocument对象转变为luence

的doc对象的呢,在创建时,Solr创建了一个process这个process是RunUpdateProcessor的实例,上面readDoc方法完成后,返回SolrInputDocument实例,然后调用RunUpdateProcessor的processAdd方法。把SolrInputDocument对象转变为luence

doc对象。代码如下:

publicvoidprocessAdd(AddUpdateCommandcmd)throwsIOException{

//转变doc对象

cmd.doc=DocumentBuilder.toDocument(cmd.getSolrInputDocument(),req.getSchema());

//主要是把luencedoc写到内存索引

updateHandler.addDoc(cmd);

super.processAdd(cmd);

}