版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
【移動(dòng)應(yīng)用開發(fā)技術(shù)】Sphinx源碼分析——Indexer
Sphinx作為一款優(yōu)秀的全文檢索開源軟件確實(shí)是很不錯(cuò),最近工作需要,要求在其上進(jìn)行二次開發(fā),第一次接觸這樣一款開源軟件,興奮和緊張心情難免,作為一個(gè)剛畢業(yè)的應(yīng)屆生,看了一周的源代碼,現(xiàn)在奉上一篇博文來對其Indexer部分代碼進(jìn)行分析,共各位及自己做一個(gè)參考,其中只代表個(gè)人的一些粗淺看法,如果不對,請各位大神一定要指正,這樣才能提高,謝謝!
Indexer作為Sphinx的重要組成部分之一,其主要作用是將數(shù)據(jù)源進(jìn)行索引化操作,其中涉及到了索引結(jié)構(gòu)的問題,我會(huì)單獨(dú)開一篇博文去詳細(xì)講解這個(gè)問題,下面我們開始進(jìn)行代碼分析。//Indexer.cpp——intmain(intargc,char**argv)這一部分主要是命令行下參數(shù)的讀入,我們這里采用的是Indexer.exe-cd:/csft_mysql.conf-All命令,這里,argc=4,即有3個(gè)參數(shù),argv[1]:-c就是讀取指定config文檔,argv[2]:d:/csft_mysql.conf就配置文件config的位置,argv[3]就代表對config文件中指定的所有數(shù)據(jù)源進(jìn)行索引構(gòu)建//Doargv[i]解析命令行
constchar*sOptConfig=NULL;
boolbMerge=false;
CSphVector<CSphFilterSettings>dMergeDstFilters;
CSphVector<constchar*>dIndexes;
boolbIndexAll=false;
boolbMergeKillLists=false;
inti;
for(i=1;i<argc;i++)//依次處理命令行中的每一個(gè)部分
{
if((!strcmp(argv[i],"--config")||!strcmp(argv[i],"-c"))&&(i+1)<argc)
{
sOptConfig=argv[++i];
if(!sphIsReadable(sOptConfig))
sphDie("configfile'%s'doesnotexistorisnotreadable",sOptConfig);
}
elseif(strcasecmp(argv[i],"--merge")==0&&(i+2)<argc)
{
bMerge=true;
dIndexes.Add(argv[i+1]);
dIndexes.Add(argv[i+2]);
i+=2;
}
elseif(bMerge&&strcasecmp(argv[i],"--merge-dst-range")==0&&(i+3)<argc)
{
dMergeDstFilters.Resize(dMergeDstFilters.GetLength()+1);
dMergeDstFilters.Last().m_eType=SPH_FILTER_RANGE;
dMergeDstFilters.Last().m_sAttrName=argv[i+1];
dMergeDstFilters.Last().m_uMinValue=(SphAttr_t)strtoull(argv[i+2],NULL,10);
dMergeDstFilters.Last().m_uMaxValue=(SphAttr_t)strtoull(argv[i+3],NULL,10);
i+=3;
}
elseif(strcasecmp(argv[i],"--buildstops")==0&&(i+2)<argc)
{
g_sBuildStops=argv[i+1];
g_iTopStops=atoi(argv[i+2]);
if(g_iTopStops<=0)
break;
i+=2;
}elseif(strcasecmp(argv[i],"--rotate")==0)
{
g_bRotate=true;
}elseif(strcasecmp(argv[i],"--buildfreqs")==0)
{
g_bBuildFreqs=true;
}elseif(strcasecmp(argv[i],"--quiet")==0)
{
g_bQuiet=true;//這里quiet就是只輸出error信息其余信息都不輸出
sphSetQuiet(true);
}elseif(strcasecmp(argv[i],"--noprogress")==0)
{
g_bProgress=false;
}elseif(strcasecmp(argv[i],"--all")==0)
{
bIndexAll=true;//表明所有數(shù)據(jù)源都要進(jìn)行索引構(gòu)建
}elseif(strcasecmp(argv[i],"--merge-killlists")==0)
{
bMergeKillLists=true;
}elseif(sphIsAlpha(argv[i][0]))
{
dIndexes.Add(argv[i]);
}else
{
break;
}
}
if(!g_bQuiet)
fprintf(stdout,SPHINX_BANNER);//如果不是quiet模式則輸出歡迎信息
if(!isatty(fileno(stdout)))
g_bProgress=false;
if(i!=argc||argc<2)
{
if(argc>1)
{
fprintf(stdout,"ERROR:malformedorunknownoptionnear'%s'.\n",argv[i]);
}else
{
fprintf(stdout,
"Usage:indexer[OPTIONS][indexname1[indexname2[...]]]\n"
"\n"
"Optionsare:\n"
"--config<file>\t\treadconfigurationfromspecifiedfile\n"
"\t\t\t(defaultiscsft.conf)\n"
"--all\t\t\treindexallconfiguredindexes\n"
"--quiet\t\t\tbequiet,onlyprinterrors\n"
"--noprogress\t\tdonotdisplayprogress\n"
"\t\t\t(automaticallyonifoutputisnottoatty)\n"
#if!USE_WINDOWS
"--rotate\t\tsendSIGHUPtosearchdwhenindexingisover\n"
"\t\t\ttorotateupdatedindexesautomatically\n"
#endif
"--buildstops<output.txt><N>\n"
"\t\t\tbuildtopNstopwordsandwritethemtogivenfile\n"
"--buildfreqs\t\tstorewordsfrequenciestooutput.txt\n"
"\t\t\t(usedwith--buildstopsonly)\n"
"--merge<dst-index><src-index>\n"
"\t\t\tmerge'src-index'into'dst-index'\n"
"\t\t\t'dst-index'willreceivemergeresult\n"
"\t\t\t'src-index'willnotbemodified\n"
"--merge-dst-range<attr><min><max>\n"
"\t\t\tfilter'dst-index'onmerge,keeponlythosedocuments\n"
"\t\t\twhere'attr'isbetween'min'and'max'(inclusive)\n"
"--merge-killlists"
"\t\t\tmergesrcanddstkilllistsinsteadofapplyingsrckilllisttodst"
"\n"
"Examples:\n"
"indexer--quietmyidx1\treindex'myidx1'definedin'csft.conf'\n"
"indexer--all\t\treindexallindexesdefinedin'csft.conf'\n");
}
return1;
}
下一步則是進(jìn)行Config文件的讀取工作,這涉及到兩個(gè)重要的類,在后面的索引及查詢過程中會(huì)多次使用它們,一個(gè)是CSphConfigParser,一個(gè)是CSphConfig。首先看下CSphConfigParser的結(jié)構(gòu)://Sphinxutils.hclassCSphConfigParser
{
public:
CSphConfigm_tConf;
public:
CSphConfigParser();
boolParse(constchar*sFileName,constchar*pBuffer=NULL);
protected:
CSphStringm_sFileName;
intm_iLine;
CSphStringm_sSectionType;
CSphStringm_sSectionName;
charm_sError[1024];
intm_iWarnings;
staticconstintWARNS_THRESH=5;
protected:
boolIsPlainSection(constchar*sKey);
boolIsNamedSection(constchar*sKey);
boolAddSection(constchar*sType,constchar*sSection);
voidAddKey(constchar*sKey,char*sValue);
boolValidateKey(constchar*sKey);
#if!USE_WINDOWS
boolTryToExec(char*pBuffer,char*pEnd,constchar*szFilename,CSphVector<char>&dResult);
#endif
char*GetBufferString(char*szDest,intiMax,constchar*&szSource);
};它是解析Config文檔的主要的數(shù)據(jù)結(jié)構(gòu),其中存儲Config文檔信息的就是我們這里提到的第二個(gè)數(shù)據(jù)結(jié)構(gòu),它同樣也是CSphConfigParser中的一個(gè)成員變量,即CSphConfig,我們可以看到CSphConfig實(shí)際上是一個(gè)哈希表,通過代碼發(fā)現(xiàn)它是一個(gè)擁有256個(gè)鍵值對的哈希表,后面我們會(huì)講到,通過CSphConfigParser類函數(shù),將Config文件解析,讀取到某一Config名字插入CsphConfig哈希表中的Key值,讀取到該Config對應(yīng)的值插入到Value中,方便后面構(gòu)建索引時(shí)使用。//Sphinxutils.h///config(hashofsectiontypes)
typedefSmallStringHash_T<CSphConfigType>CSphConfig;
說完了這兩個(gè)數(shù)據(jù)結(jié)構(gòu)我們來看下Indexer是如何讀取Config信息的,其中主要是通過一個(gè)sphLoadConfig函數(shù)完成讀取操作,將相關(guān)Config信息以鍵值對的形式存入cp.m_tConf中,然后檢查重要的參數(shù)是否讀入且存在,例如Source相關(guān)信息,數(shù)據(jù)源是否被讀入,Sphinx中Mysql默認(rèn)Source對應(yīng)值為mysql,Indexer,即全局Index定義中是否定義了mem_limit的值,即索引過程中最大緩存限制,等等。//Indexer.cpp——intmain(intargc,char**argv)///////////////
//loadconfig
///////////////
CSphConfigParsercp;
CSphConfig&hConf=cp.m_tConf;
sOptConfig=sphLoadConfig(sOptConfig,g_bQuiet,cp);
if(!hConf("source"))
sphDie("noindexesfoundinconfigfile'%s'",sOptConfig);
g_iMemLimit=0;
if(hConf("indexer")&&hConf["indexer"]("indexer"))
{
CSphConfigSection&hIndexer=hConf["indexer"]["indexer"];
g_iMemLimit=hIndexer.GetSize("mem_limit",0);
g_iMaxXmlpipe2Field=hIndexer.GetSize("max_xmlpipe2_field",2*1024*1024);
g_iWriteBuffer=hIndexer.GetSize("write_buffer",1024*1024);
sphSetThrottling(hIndexer.GetInt("max_iops",0),hIndexer.GetSize("max_iosize",0));
}這其中,主要解析函數(shù)為CSphConfig中的Parser函數(shù),其里面比較復(fù)雜,大意就是按照字符流讀取Config文檔,遇到配置信息及其值就存儲到CSphconfig這個(gè)哈希表中//Sphinxutils.hconstchar*sphLoadConfig(constchar*sOptConfig,boolbQuiet,CSphConfigParser&cp)
{
//fallbacktodefaultsiftherewasnoexplicitconfigspecified
while(!sOptConfig)
{
#ifdefSYSCONFDIR
sOptConfig=SYSCONFDIR"/csft.conf";
if(sphIsReadable(sOptConfig))
break;
#endif
sOptConfig="./csft.conf";
if(sphIsReadable(sOptConfig))
break;
sOptConfig=NULL;
break;
}
if(!sOptConfig)
sphDie("noreadableconfigfile(lookedin"
#ifdefSYSCONFDIR
SYSCONFDIR"/csft.conf,"
#endif
"./csft.conf)");
if(!bQuiet)
fprintf(stdout,"usingconfigfile'%s'...\n",sOptConfig);
//loadconfig
if(!cp.Parse(sOptConfig))//Parser為實(shí)際解析函數(shù)
sphDie("failedtoparseconfigfile'%s'",sOptConfig);
CSphConfig&hConf=cp.m_tConf;
if(!hConf("index"))
sphDie("noindexesfoundinconfigfile'%s'",sOptConfig);
returnsOptConfig;
}當(dāng)我們順利的讀取完Config信息后,我們進(jìn)入構(gòu)建索引階段,前面我們提到了第三個(gè)參數(shù),我們選用的是ALL即為所有的數(shù)據(jù)源構(gòu)建索引,故bMerge(合并索引)為false,bIndexALL為true,我們開始為每一數(shù)據(jù)源構(gòu)建索引,程序會(huì)開始在類型為CSphConfig的hConf哈希表中搜索Key為index的值,即需要構(gòu)建的索引,然后取出該索引的名稱,結(jié)合數(shù)據(jù)源Source信息構(gòu)建索引,執(zhí)行DoIndex函數(shù)。//Indexer.cpp——intmain(intargc,char**argv)/////////////////////
//indexeachindex
////////////////////
sphStartIOStats();
boolbIndexedOk=false;//ifanyoftheindexesareok
if(bMerge)
{
if(dIndexes.GetLength()!=2)
sphDie("theremustbe2indexestomergespecified");
if(!hConf["index"](dIndexes[0]))
sphDie("nomergedestinationindex'%s'",dIndexes[0]);
if(!hConf["index"](dIndexes[1]))
sphDie("nomergesourceindex'%s'",dIndexes[1]);
bIndexedOk=DoMerge(
hConf["index"][dIndexes[0]],dIndexes[0],
hConf["index"][dIndexes[1]],dIndexes[1],dMergeDstFilters,g_bRotate,bMergeKillLists);
}elseif(bIndexAll)
{
hConf["index"].IterateStart();
while(hConf["index"].IterateNext())
bIndexedOk|=DoIndex(hConf["index"].IterateGet(),hConf["index"].IterateGetKey().cstr(),hConf["source"]);//在這里構(gòu)建索引,核心函數(shù)為DoIndex
}else
{
ARRAY_FOREACH(i,dIndexes)
{
if(!hConf["index"](dIndexes[i]))
fprintf(stdout,"WARNING:nosuchindex'%s',skipping.\n",dIndexes[i]);
else
bIndexedOk|=DoIndex(hConf["index"][dIndexes[i]],dIndexes[i],hConf["source"]);
}
}
sphShutdownWordforms();
constCSphIOStats&tStats=sphStopIOStats();
if(!g_bQuiet)
{
ReportIOStats("reads",tStats.m_iReadOps,tStats.m_iReadTime,tStats.m_iReadBytes);
ReportIOStats("writes",tStats.m_iWriteOps,tStats.m_iWriteTime,tStats.m_iWriteBytes);
}DoIndex為整個(gè)Indexer中最核心的函數(shù),下面我們來詳細(xì)分析下.//Indexer.cpp——DoIndex(constCSphConfigSection&hIndex,constchar*sIndexName,constCSphConfigType&hSource)首先判斷數(shù)據(jù)源類型是否為分布式,是否采用quiet模式只輸出error信息if(hIndex("type")&&hIndex["type"]=="distributed")
{
if(!g_bQuiet)
{
fprintf(stdout,"distributedindex'%s'cannotbedirectlyindexed;skipping.\n",sIndexName);
fflush(stdout);
}
returnfalse;
}
if(!g_bQuiet)
{
fprintf(stdout,"indexingindex'%s'...\n",sIndexName);
fflush(stdout);
}然后檢查hIndex信息中的配置信息是否齊全正確//checkconfig
if(!hIndex("path"))
{
fprintf(stdout,"ERROR:index'%s':key'path'notfound.\n",sIndexName);
returnfalse;
}
if((hIndex.GetInt("min_prefix_len",0)>0||hIndex.GetInt("min_infix_len",0)>0)
&&hIndex.GetInt("enable_star")==0)
{
constchar*szMorph=hIndex.GetStr("morphology","");
if(szMorph&&*szMorph&&strcmp(szMorph,"none"))
{
fprintf(stdout,"ERROR:index'%s':infixesandmorphologyareenabled,enable_star=0\n",sIndexName);
returnfalse;
}
}接著開始準(zhǔn)備分詞器,其中主要就是初始化一些參數(shù),例如在sphConfTokenizer中會(huì)根據(jù)Config配置文件中設(shè)置的charsetType類型選擇合適的編碼解析字符,以及采用何種中文分詞器來對中文進(jìn)行分詞操作。然后就是初始化參數(shù)后創(chuàng)建分析器實(shí)例指定分詞所用的詞典的地址位置等///////////////////
//spawntokenizer
///////////////////
CSphStringsError;
CSphTokenizerSettingstTokSettings;
if(!sphConfTokenizer(hIndex,tTokSettings,sError))
sphDie("index'%s':%s",sIndexName,sError.cstr());
ISphTokenizer*pTokenizer=ISphTokenizer::Create(tTokSettings,sError);
if(!pTokenizer)
sphDie("index'%s':%s",sIndexName,sError.cstr());
CSphDict*pDict=NULL;
CSphDictSettingstDictSettings;
if(!g_sBuildStops)
{
ISphTokenizer*pTokenFilter=NULL;
sphConfDictionary(hIndex,tDictSettings);
pDict=sphCreateDictionaryCRC(tDictSettings,pTokenizer,sError);
if(!pDict)
sphDie("index'%s':%s",sIndexName,sError.cstr());
if(!sError.IsEmpty())
fprintf(stdout,"WARNING:index'%s':%s\n",sIndexName,sError.cstr());
pTokenFilter=ISphTokenizer::CreateTokenFilter(pTokenizer,pDict->GetMultiWordforms());
pTokenizer=pTokenFilter?pTokenFilter:pTokenizer;
}然后是前綴后綴索引設(shè)置(這個(gè)地方研究的不細(xì)致,先把代碼貼出來,待補(bǔ)充)//prefix/infixindexing
intiPrefix=hIndex("min_prefix_len")?hIndex["min_prefix_len"].intval():0;
intiInfix=hIndex("min_infix_len")?hIndex["min_infix_len"].intval():0;
iPrefix=Max(iPrefix,0);
iInfix=Max(iInfix,0);
CSphStringsPrefixFields,sInfixFields;
if(hIndex.Exists("prefix_fields"))
sPrefixFields=hIndex["prefix_fields"].cstr();
if(hIndex.Exists("infix_fields"))
sInfixFields=hIndex["infix_fields"].cstr();
if(iPrefix==0&&!sPrefixFields.IsEmpty())
fprintf(stdout,"WARNING:min_prefix_len=0.prefix_fieldsareignored\n");
if(iInfix==0&&!sInfixFields.IsEmpty())
fprintf(stdout,"WARNING:min_infix_len=0.infix_fieldsareignored\n");然后設(shè)置boundary信息(詞組邊界符列表,此列表控制哪些字符被視作分隔不同詞組的邊界,每到一個(gè)這樣的邊界,其后面的詞的“位置”值都會(huì)被加入一個(gè)額外的增量,可以借此用近似搜索符來模擬詞組搜索。)//boundary
boolbInplaceEnable=hIndex.GetInt("inplace_enable",0)!=0;
intiHitGap=hIndex.GetSize("inplace_hit_gap",0);
intiDocinfoGap=hIndex.GetSize("inplace_docinfo_gap",0);
floatfRelocFactor=hIndex.GetFloat("inplace_reloc_factor",0.1f);
floatfWriteFactor=hIndex.GetFloat("inplace_write_factor",0.1f);
if(bInplaceEnable)
{
if(fRelocFactor<0.01f||fRelocFactor>0.9f)
{
fprintf(stdout,"WARNING:inplace_reloc_factormustbe0.01to0.9,clamped\n");
fRelocFactor=Min(Max(fRelocFactor,0.01f),0.9f);
}
if(fWriteFactor<0.01f||fWriteFactor>0.9f)
{
fprintf(stdout,"WARNING:inplace_write_factormustbe0.01to0.9,clamped\n");
fWriteFactor=Min(Max(fWriteFactor,0.01f),0.9f);
}
if(fWriteFactor+fRelocFactor>1.0f)
{
fprintf(stdout,"WARNING:inplace_write_factor+inplace_reloc_factormustbelessthan0.9,scaled\n");
floatfScale=0.9f/(fWriteFactor+fRelocFactor);
fRelocFactor*=fScale;
fWriteFactor*=fScale;
}
}接下來準(zhǔn)備數(shù)據(jù)源,其實(shí)發(fā)現(xiàn)Indexer在準(zhǔn)備這些工作時(shí)很繁瑣,一遍又一遍的檢查相關(guān)配置信息是否完全,前面檢查了后面還查,可能是出于嚴(yán)謹(jǐn)?shù)目紤]吧,這里提一下dSource是一個(gè)CSphSource的數(shù)組,每一個(gè)CSphSource類型的pSource對應(yīng)一個(gè)數(shù)據(jù)源,因?yàn)榕渲眯畔⒅锌赡軙?huì)存在多個(gè)數(shù)據(jù)源,所以會(huì)有多個(gè)pSource。程序會(huì)在hIndex中搜索Key值為Source的鍵值對,提取出對應(yīng)的值作為pSourceName,在本例中,我們只有配置文件中的一個(gè)Source即mysql。我們看一下CSphSource類型結(jié)構(gòu)。其中包含有三個(gè)大部分,第一大部分存儲文本分詞后的word信息,每一個(gè)word(也許是字也許是詞)對應(yīng)一個(gè)WordHit,這個(gè)WordHit描述該word的相關(guān)信息,唯一標(biāo)示該word。其中WordHit中又包含三部分,分別為word的文檔ID,表示該word屬于哪一篇文檔;word的ID,表示該word在字典中的對應(yīng)ID;Word的位置,表示該word在文檔中的偏移量。第二大部分存儲Source中文檔的相關(guān)信息,其中亦包含了三部分,分別問文檔ID;文檔中列的數(shù)目,以及列對應(yīng)的指針。第三大部分存儲的就是doc中的屬性字段信息。///genericdatasource
classCSphSource:publicCSphSourceSettings
{
public:
CSphVector<CSphWordHit>m_dHits;///<currentdocumentsplitintowords
CSphDocInfom_tDocInfo;///<currentdocumentinfo
CSphVector<CSphString>m_dStrAttrs;///<currentdocumentstringattrs123//parseallsources
CSphVector<CSphSource*>dSources;
boolbGotAttrs=false;
boolbSpawnFailed=false;
for(CSphVariant*pSourceName=hIndex("source");pSourceName;pSourceName=pSourceName->m_pNext)
{
if(!hSources(pSourceName->cstr()))
{
fprintf(stdout,"ERROR:index'%s':source'%s'notfound.\n",sIndexName,pSourceName->cstr());
continue;
}
constCSphConfigSection&hSource=hSources[pSourceName->cstr()];
CSphSource*pSource=SpawnSource(hSource,pSourceName->cstr(),pTokenizer->IsUtf8());//通過SpawnSource完成對于數(shù)據(jù)源的解析,其中包括了屬性列,需要構(gòu)建索引列等相關(guān)信息
if(!pSource)
{
bSpawnFailed=true;
continue;
}
if(pSource->HasAttrsConfigured())
bGotAttrs=true;//判斷數(shù)據(jù)源中是否有指定屬性項(xiàng)
pSource->SetupFieldMatch(sPrefixFields.cstr(),sInfixFields.cstr());
pSource->SetTokenizer(pTokenizer);//為每一個(gè)Source準(zhǔn)備一個(gè)分詞器
dSources.Add(pSource);//將解析好的某個(gè)Source加入Source數(shù)組中去,因?yàn)榭赡艽嬖诙鄠€(gè)Source
}Source信息準(zhǔn)備好后,開始準(zhǔn)備Index的構(gòu)建工作,首先檢測該Index是否被使用,即是否被上鎖,其次通過CSphIndexSettings類型的tSettings對創(chuàng)建好的pIndex進(jìn)行初始化,主要是一些索引構(gòu)建的信息,例如緩存大小,Boudary大小,停用詞初始化,分詞器初始化等等。準(zhǔn)備完相關(guān)信息后,重要的就是Build函數(shù),這是索引構(gòu)建的核心函數(shù),我們來仔細(xì)分析//doindex
CSphIndex*pIndex=sphCreateIndexPhrase(sIndexPath.cstr());
assert(pIndex);
//checklockfile
if(!pIndex->Lock())
{
fprintf(stdout,"FATAL:%s,willnotindex.Try--rotateoption.\n",pIndex->GetLastError().cstr());
exit(1);
}
CSphIndexSettingstSettings;
sphConfIndex(hIndex,tSettings);
if(tSettings.m_bIndexExactWords&&!tDictSettings.HasMorphology())
{
tSettings.m_bIndexExactWords=false;
fprintf(stdout,"WARNING:index'%s':nomorphology,index_exact_words=1hasnoeffect,ignoring\n",sIndexName);
}
if(bGotAttrs&&tSettings.m_eDocinfo==SPH_DOCINFO_NONE)
{
fprintf(stdout,"FATAL:index'%s':gotattributes,butdocinfois'none'(fixyourconfigfile).\n",sIndexName);
exit(1);
}
pIndex->SetProgressCallback(ShowProgress);
if(bInplaceEnable)
pIndex->SetInplaceSettings(iHitGap,iDocinfoGap,fRelocFactor,fWriteFactor);
pIndex->SetTokenizer(pTokenizer);
pIndex->SetDictionary(pDict);
pIndex->Setup(tSettings);
bOK=pIndex->Build(dSources,g_iMemLimit,g_iWriteBuffer)!=0;//Build函數(shù)是索引構(gòu)建的重點(diǎn),所有的核心操作都在其中
if(bOK&&g_bRotate)
{
sIndexPath.SetSprintf("%s.new",hIndex["path"].cstr());
bOK=pIndex->Rename(sIndexPath.cstr());
}
if(!bOK)
fprintf(stdout,"ERROR:index'%s':%s.\n",sIndexName,pIndex->GetLastError().cstr());
pIndex->Unlock();對于Build函數(shù)而言,它是單次處理一個(gè)數(shù)據(jù)源并為此構(gòu)建索引信息,//sphinx.cppBuild(constCSphVector<CSphSource*>&dSources,intiMemoryLimit,intiWriteBuffer)首先是準(zhǔn)備Source,還是把dSource中的每一個(gè)pSource檢查下是否都存在,詞典是否都準(zhǔn)備好,各種初始化是否都齊備//setupsources
ARRAY_FOREACH(iSource,dSources)
{
CSphSource*pSource=dSources[iSource];
assert(pSource);
pSource->SetDict(m_pDict);
pSource->Setup(m_tSettings);
}鏈接第一個(gè)數(shù)據(jù)源,獲取數(shù)據(jù)源的Schema信息,就是數(shù)據(jù)源的Doc中哪些是屬性,哪些列是要構(gòu)建索引的信息//connect1stsourceandfetchitsschema
if(!dSources[0]->Connect(m_sLastError)
||!dSources[0]->IterateHitsStart(m_sLastError)
||!dSources[0]->UpdateSchema(&m_tSchema,m_sLastError))
{
return0;
}后面就是初始化一些存儲結(jié)構(gòu),其中重點(diǎn)說下緩存出來的幾個(gè)臨時(shí)文件分別的作用。結(jié)尾時(shí)tmp0的存儲的是被上鎖的Index,有些Index正在被查詢使用故上鎖。tmp1,即對應(yīng)將來生成的spp文件,存儲詞匯的位置信息,包含該詞所在的文檔ID,該詞所在詞典對應(yīng)的ID,以及該詞在本文檔中的位置信息。tmp2,即對應(yīng)將來生成的spa文件存儲的是文檔信息,包含了DocID以及DocInfo信息。tmp7對應(yīng)的是多值查詢,感興趣的可以度娘,這是一種查詢方式,這里不做過多解釋//createtempfiles
CSphAutofilefdLock(GetIndexFileName("tmp0"),SPH_O_NEW,m_sLastError,true);
CSphAutofilefdHits(GetIndexFileName(m_bInplaceSettings?"spp":"tmp1"),SPH_O_NEW,m_sLastError,!m_bInplaceSettings);
CSphAutofilefdDocinfos(GetIndexFileName(m_bInplaceSettings?"spa":"tmp2"),SPH_O_NEW,m_sLastError,!m_bInplaceSettings);
CSphAutofilefdTmpFieldMVAs(GetIndexFileName("tmp7"),SPH_O_NEW,m_sLastError,true);
CSphWritertOrdWriter;
CSphStringsRawOrdinalsFile=GetIndexFileName("tmp4");下面具體處理每一個(gè)Source取出的每一個(gè)文檔,主要是通過這個(gè)IterateHitsNext實(shí)現(xiàn)的//fetchdocuments
for(;;)
{
//getnextdoc,andhandleerrors
if(!pSource->IterateHitsNext(m_sLastError))
return0;具體到該函數(shù)可以看到,該函數(shù)主要是有兩部分組成,即提取索引列(NextDocument),針對該索引列構(gòu)建索引(BuildHits)boolCSphSource_Document::IterateHitsNext(CSphString&sError)
{
assert(m_pTokenizer);
PROFILE(src_document);
BYTE**dFields=NextDocument(sError);//從數(shù)據(jù)源中提取需要構(gòu)建索引的列
if(m_tDocInfo.m_iDocID==0)
returntrue;
if(!dFields)
returnfalse;
m_tStats.m_iTotalDocuments++;
m_dHits.Reserve(1024);
m_dHits.Resize(0);
BuildHits(dFields,-1,0);//針對提取出的需要索引的列構(gòu)建索引
returntrue;
}具體看一下NexDocument的操作,通過Sql.h中的API——sqlFetchRow,取出一條記錄,驗(yàn)證該記錄是否合法//getnextnon-zero-idrow
do
{
//trytogetnextrow
boolbGotRow=SqlFetchRow();//首先嘗試能否正常取出一條記錄
//whentheparty'sover...
while(!bGotRow)//如果取不出來這條記錄,再繼續(xù)思考原因
{
//isthatanerror?
if(SqlIsError())
{
sError.SetSprintf("sql_fetch_row:%s",SqlError());
m_tDocInfo.m_iDocID=1;//0meanslegaleof
returnNULL;
}
//maybewecandonextstepyet?
if(!RunQueryStep(m_tParams.m_sQuery.cstr(),sError))
{
//ifthere'samessage,there'sanerror
//otherwise,we'rejustover
if(!sError.IsEmpty())
{
m_tDocInfo.m_iDocID=1;//0meanslegaleof
returnNULL;
}
}else
{
//stepwentfine;trytofetch
bGotRow=SqlFetchRow();
continue;
}
SqlDismi***esult();
//ok,we'reover
ARRAY_FOREACH(i,m_tParams.m_dQueryPost)
{
if(!SqlQuery(m_tParams.m_dQueryPost[i].cstr()))
{
sphWarn("sql_query_post[%d]:error=%s,query=%s",
i,SqlError(),m_tParams.m_dQueryPost[i].cstr());
break;
}
SqlDismi***esult();
}
m_tDocInfo.m_iDocID=0;//0meanslegaleof
returnNULL;
}
//gethim!//成功取得后
m_tDocInfo.m_iDocID=VerifyID(sphToDocid(SqlColumn(0)));//判斷ID是否為0,是否越界
m_uMaxFetchedID=Max(m_uMaxFetchedID,m_tDocInfo.m_iDocID);
}while(!m_tDocInfo.m_iDocID);將條記錄按照Schema分成Feild部分,即需要構(gòu)建索引的部分,以及Attribute部分,即排序需要用到的屬性部分ARRAY_FOREACH(i,m_tSchema.m_dFields)
{
#ifUSE_ZLIB
if(m_dUnpack[i]!=SPH_UNPACK_NONE)
{
m_dFields[i]=(BYTE*)SqlUnpackColumn(i,m_dUnpack[i]);
continue;
}
#endif
m_dFields[i]=(BYTE*)SqlColumn(m_tSchema.m_dFields[i].m_iIndex);
}
intiFieldMVA=0;
for(inti=0;i<m_tSchema.GetAttrsCount();i++)
{
constCSphColumnInfo&tAttr=m_tSchema.GetAttr(i);//shortcut
if(tAttr.m_eAttrType&SPH_ATTR_MULTI)
{
m_tDocInfo.SetAttr(tAttr.m_tLocator,0);
if(tAttr.m_eSrc==SPH_ATTRSRC_FIELD)
ParseFieldMVA(m_dFieldMVAs,iFieldMVA++,SqlColumn(tAttr.m_iIndex));
continue;
}
switch(tAttr.m_eAttrType)
{
caseSPH_ATTR_ORDINAL:
//memorizestring,fixupNULLs
m_dStrAttrs[i]=SqlColumn(tAttr.m_iIndex);
if(!m_dStrAttrs[i].cstr())
m_dStrAttrs[i]="";
m_tDocInfo.SetAttr(tAttr.m_tLocator,0);
break;
caseSPH_ATTR_FLOAT:
m_tDocInfo.SetAttrFloat(tAttr.m_tLocator,sphToFloat(SqlColumn(tAttr.m_iIndex)));//FIXME?reportconversionerrorsmaybe?
break;
caseSPH_ATTR_BIGINT:
m_tDocInfo.SetAttr(tAttr.m_tLocator,sphToInt64(SqlColumn(tAttr.m_iIndex)));//FIXME?reportconversionerrorsmaybe?
break;
default:
//juststoreasuintbydefault
m_tDocInfo.SetAttr(tAttr.m_tLocator,sphToDword(SqlColumn(tAttr.m_iIndex)));//FIXME?reportconversionerrorsmaybe?
break;
}
}
returnm_dFields;提取出相關(guān)數(shù)據(jù)后,針對每一條需要索引的item開始構(gòu)建索引,進(jìn)入BuildHit函數(shù),首先先初始化相關(guān)參數(shù),準(zhǔn)備分詞器緩存ARRAY_FOREACH(iField,m_tSchema.m_dFields)
{
//BYTE*sField=dFields[iField];
BYTE*sField=GetField(dFields,iField);//取出索引字段
if(!sField)
continue;
if(m_bStripHTML)
m_pStripper->Strip(sField);
intiFieldBytes=(int)strlen((char*)sField);
m_tStats.m_iTotalBytes+=iFieldBytes;
m_pTokenizer->SetBuffer(sField,iFieldBytes);//設(shè)置分詞器緩存,實(shí)際上就是索引字段大小,準(zhǔn)備針對索引字段進(jìn)行分詞
BYTE*sWord;
intiPos=HIT_PACK(iField,0);
intiLastStep=1;
boolbPrefixField=m_tSchema.m_dFields[iField].m_eWordpart==SPH_WORDPART_PREFIX;
boolbInfixMode=m_iMinInfixLen>0;
BYTEsBuf[16+3*SPH_MAX_WORD_LEN];然后開始分詞,分詞的過程在這里不具體講了,這不屬于Sphinx的主要涉足領(lǐng)域,當(dāng)我們把iField即要索引的字段放入分詞器中依次解析,然后將分出的詞賦值給sWord,將sWord的位置計(jì)算后賦值給ipos//indexwordsonly
while((sWord=m_pTokenizer->GetToken())!=NULL)
{
iPos+=iLastStep+m_pTokenizer->GetOvershortCount()*m_iOvershortStep;
if(m_pTokenizer->GetBoundary())
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年全國大學(xué)生就業(yè)創(chuàng)業(yè)知識競賽試題庫及答案
- 安全生產(chǎn)應(yīng)知應(yīng)會(huì)采煤部分模擬試題(含參考答案)
- 銀行業(yè)務(wù)員考試題及答案
- 低頻電療法操作考試題及答案
- 大修電廠安全試題及答案
- 2026黑龍江鶴崗市鶴北人民法院招聘聘用制人員3人參考題庫必考題
- 豐城市行政事業(yè)單位編外人員招聘【5人】備考題庫附答案
- 興國縣2025年招聘城市社區(qū)專職網(wǎng)格員【23人】參考題庫附答案
- 四川能投高縣綜合能源有限公司2025年招聘工作人員備考題庫必考題
- 廣安區(qū)2025年社會(huì)化選聘新興領(lǐng)域黨建工作專員的備考題庫附答案
- 工廠驗(yàn)收測試(FAT)
- 麻醉藥品、精神藥品月檢查記錄
- 高職單招數(shù)學(xué)試題及答案
- 基礎(chǔ)化學(xué)(本科)PPT完整全套教學(xué)課件
- 蕉嶺縣幅地質(zhì)圖說明書
- 玻璃幕墻分項(xiàng)工程質(zhì)量驗(yàn)收記錄表
- 電梯控制系統(tǒng)論文
- (完整word版)人教版初中語文必背古詩詞(完整版)
- 湖北省地質(zhì)勘查坑探工程設(shè)計(jì)編寫要求
- GB/T 4310-2016釩
- GB/T 28799.3-2020冷熱水用耐熱聚乙烯(PE-RT)管道系統(tǒng)第3部分:管件
評論
0/150
提交評論