- 自己動手寫分布式搜索引擎
- 羅剛
- 1495字
- 2020-11-28 15:52:45
3.2.3 向索引庫中添加索引文檔
先增加文檔,然后提交更新到索引庫。和數(shù)據(jù)庫表一樣,索引庫是結(jié)構(gòu)化的,一個文檔往往有很多列,例如標題和內(nèi)容列等。往索引添加數(shù)據(jù)時涉及的幾個類的關(guān)系如圖3-5所示。

圖3-5 往索引中添加文檔
TextField是一個快捷類,它不存儲詞向量。如果你需要詞向量,就只使用Field類。這需要更多的代碼,因為首先要創(chuàng)建一個FieldType類的實例,然后設置storeTermVectors和tokenizer為true,并在Field構(gòu)造器中使用這個FieldType實例。
FieldType t = new FieldType(); t.setStoreTermVectorOffsets(true); t.setTokenized(true); fieldTitle = new Field("title", "標題", t);
使用StringField:
field = new StringField("url", "bar", Store.YES);
使用FieldType實現(xiàn)的等價代碼:
FieldType fieldType = new FieldType(); fieldType.setStored(true); fieldType.setTokenized(false); fieldType.setIndexed(true); field = new Field("url", "lietu.com", fieldType);
下面這段程序是向索引庫中添加網(wǎng)頁地址、標題和內(nèi)容列:
//如果初次使用Lucene,往索引中寫入的每條記錄最好都新創(chuàng)建一個Document與之對應, //也就是說不要重復使用Document對象,否則可能會出現(xiàn)意想不到的錯誤 Document doc = new Document(); //創(chuàng)建網(wǎng)址列 Field f = new Field("url", news.URL , Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO); doc.add(f); //創(chuàng)建標題列 f = new Field("title", news.title , Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(f); //創(chuàng)建內(nèi)容列 f = new Field("body", news.body.toString() , Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(f); index.addDocument(doc);
下面兩種寫法是等價的:
new Field("title", "標題", TextField.TYPE_STORED); new TextField("title", "標題", Store.YES );
和一般的數(shù)據(jù)庫不一樣,一個文檔的一個列可以有多個值。例如一篇文檔既可以屬于互聯(lián)網(wǎng)類,又可以屬于科技類。
Lucene中的API相對數(shù)據(jù)庫來說比較靈活,沒有類似數(shù)據(jù)庫先定義表結(jié)構(gòu)后再使用的過程。如果前后兩次寫索引時定義的列名稱不一樣,Lucene會自動創(chuàng)建新的列,所以Field的一致性需要我們自己掌握。
列選項組合見表3-2。
表3-2 Field的類型

這里的POSITIONS表示以詞為單位的位置,也就是語義位置,而OFFSETS表示詞在文本中的實際物理位置。
FieldType類設置這些組合。例如:
Analyzer ca = new CJKAnalyzer(); IndexWriterConfig indexWriterConfig = new IndexWriterConfig(ca); FieldType nameType = new FieldType(); nameType.setIndexed(true); nameType.setStored(true); IndexWriter indexWriter = new IndexWriter(DIRECTORY, indexWriterConfig); Document document = new Document(); document.add(new Field("name", "我購買了道具和服裝", nameType)); indexWriter.addDocument(document);
在增加文檔的階段,給新的詞分配TokenID,新的文檔分配DocID。
增加索引后,記得提交索引,否則reader不一定能搜索到。
writer.commit(); //提交更新到索引庫
索引創(chuàng)建完成后可以用索引查看工具Luke(https://github.com/dmitrykey/luke)來查看索引內(nèi)容并維護索引庫。Luke是一個可以執(zhí)行的jar包,是用Java實現(xiàn)的Windows程序。在Windows下可以雙擊lukeall-1.0.1.jar,啟動Luke。然后,可以選擇菜單File→Open Lucene index,打開data/index文件夾,然后可以在窗口看到索引創(chuàng)建的詳細信息。
為了提高索引速度,可以重用Field,而不是每次都創(chuàng)建新的。從Lucene 2.3開始,有新的setValue()方法,可以改變Field的值。這樣可以在增加許多Document的時候重用單個的Field實例,從而節(jié)省許多垃圾回收消耗的時間。
開始新建一個獨立的Document實例,然后增加許多Field實例,并且增加每個文檔到索引的時候都重用這些Field。例如,有一個idField、bodyField和nameField等。當加入一個Document后,可以通過idField.setValue()等直接改變Field的值,然后再增加文檔實例。下面是一個重用Field的例子。
//創(chuàng)建索引 IndexWriter writer = new IndexWriter(directory, analyzer); //創(chuàng)建文檔結(jié)構(gòu) Document doc = new Document(); //定義id重用列 Field idField = new Field("id", Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.NO); doc.add(idField); //定義標題重用列 Field titleField = new Field("title", null , Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(titleField); //定義內(nèi)容重用列 Field bodyField = new Field("body", null , Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS); doc.add(bodyField); for(all document) { //處理所有的文檔,填充Field值,并索引文檔 Article a = parse(document); idField.setValue(a.id); titleField.setValue(a.title); bodyField.setValue(a.body); writer.addDocument(doc); //doc僅僅是一個容器 } writer.close();
注意,不能在文檔中重用單個Field實例,不應該改變列的值,直到包含這個Field的Document已經(jīng)加入到索引庫。
可以同時增加多個文檔到索引:addDocuments()。
List<Document> docs = new ArrayList<Document>(); Document doc1 = new Document(); doc1.add(new Field("content", "獵兔搜索", Field.Store.YES, Field.Index.ANALYZED)); Document doc2 = new Document(); doc2.add(new Field("content", "中文分詞", Field.Store.YES, Field.Index.ANALYZED)); Document doc3 = new Document(); doc3.add(new Field("content", "中國", Field.Store.YES, Field.Index.ANALYZED)); Document doc4 = new Document(); doc4.add(new Field("content", "NBA", Field.Store.YES, Field.Index.ANALYZED)); docs.add(doc1); docs.add(doc2); docs.add(doc3); docs.add(doc4); writer.addDocuments(docs);
當在Windows系統(tǒng)下使用的時候,最好取消勾選殺毒軟件的“自動刪除已感染病毒文件”選項,否則當索引帶病毒特征的文檔時,殺毒軟件可能會破壞Lucene的索引文件。
每一個添加的文檔都被傳遞給DocConsumer類,它處理該文檔并且與索引鏈表(indexing chain)中其他的consumers相互發(fā)生作用。確定的consumers,就像StoredFieldWriter和TermVectorsTermsWriter,提取一個文檔中的詞,并且馬上把字節(jié)寫入文件。
IndexWriter.setRAMBufferSizeMB方法可以設置當更新文檔使用的內(nèi)存達到指定大小之后才寫入硬盤。這樣可以提高寫索引的速度,尤其是在批量創(chuàng)建索引的時候。
在Lucene 2.3版本之前,存入索引的每個Token都是新創(chuàng)建的。重復利用Token可以加快索引速度。新的Tokenizer類可以回收利用已用過的Token。
- 性能測試從零開始
- VR、AR與MR項目開發(fā)實戰(zhàn)
- 從零開始:AutoCAD 2015中文版機械制圖基礎培訓教程
- Alice 3 Cookbook
- 中文版Maya 2014案例教程
- Getting Started With Oracle SOA Suite 11g R1 – A Hands/On Tutorial
- 二維計算機繪圖教程:二維CAD工程師取證全程指導
- Joomla! 1.5 Site Blueprints: LITE
- iPad+Procreate室內(nèi)設計手繪表現(xiàn)技法
- Magento: Beginner's Guide
- 電腦寫作與定制五筆(第2版)
- Photoshop后期強:多重曝光專業(yè)技法寶典
- HBase企業(yè)應用開發(fā)實戰(zhàn)
- Apache Solr High Performance
- 剪映專業(yè)版:PC端短視頻制作(全彩慕課版)