官术网_书友最值得收藏!

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。

主站蜘蛛池模板: 东辽县| 安丘市| 广汉市| 山阳县| 清水河县| 赤水市| 积石山| 安宁市| 乌苏市| 台前县| 沧源| 奉化市| 卓尼县| 怀柔区| 牟定县| 德州市| 慈利县| 博野县| 呼图壁县| 耒阳市| 兴业县| 麟游县| 黎川县| 武穴市| 清远市| 十堰市| 肇源县| 上饶市| 尼木县| 怀安县| 鄢陵县| 岳阳县| 大方县| 永登县| 武川县| 株洲市| 平远县| 怀来县| 大港区| 舟曲县| 桂林市|