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

3.3.13 關(guān)鍵詞高亮顯示

因為搜索出來的文檔內(nèi)容可能比較長,所以不僅要檢索出命中的文本,還要提供查詢詞在文本中出現(xiàn)的位置,方便用戶直接看到想要找的信息。最終高亮顯示的是一個片段(包含高亮詞),而不是一個完整的列值。將來的瀏覽器應(yīng)該可以支持顯示全文的同時,先定位到查詢詞所在的位置。

在搜索結(jié)果中一般都有和用戶搜索關(guān)鍵詞相關(guān)的摘要。關(guān)鍵詞一般都會高亮顯示。從實現(xiàn)上說,就是把要突出顯示的關(guān)鍵詞前加上<B>標(biāo)簽,關(guān)鍵詞后加上</B>標(biāo)簽。Lucene的highlighter包可以做到這一點。

Lucene有兩個高亮顯示的實現(xiàn):一個是org.apache.lucene.search.highlight;還有一個是org.apache.lucene.search.vectorhighlight。下面是使用org.apache.lucene.search.highlight的例子:

        doSearching("汽車");
        //使用一個查詢初始化Highlighter對象
        Highlighter highlighter = new Highlighter(new QueryScorer(query));
        //設(shè)置分段顯示的文本長度
        highlighter.setTextFragmenter(new SimpleFragmenter(40));
        //設(shè)置最多顯示的段落數(shù)量
        int maxNumFragmentsRequired = 2;
        for (int i = 0; i < hits.length(); i++) {
            //取得索引庫中存儲的原始文本
            String text = hits.doc(i).get(FIELD_NAME);
            TokenStream tokenStream=analyzer.tokenStream(FIELD_NAME,
                                                        new StringReader(text));


            //取得關(guān)鍵詞加亮后的結(jié)果
            String result = highlighter.getBestFragments(tokenStream,
                                                    text,
                                                    maxNumFragmentsRequired,
                                                    "...");
            System.out.println("\t" + result);
        }

QueryScorer()設(shè)置查詢的query,這里還可以加上對字段列的限制,比如只對body條件的term高亮顯示,可以使用new QueryScorer(query, “body”)。對于模糊匹配,需要先找出要高亮顯示的詞。可以使用SpanScorer和SimpleSpanFragmenter,或者使用QueryScorer和SimpleFragmenter。

使用SpanScorer和SimpleSpanFragmenter生成高亮段落的代碼如下:

        TokenStream stream = TokenSources.getTokenStream(fieldName, fieldContents,
                            analyzer);
        SpanScorer scorer = new SpanScorer(query, fieldName,
                        new CachingTokenFilter(stream));
        Fragmenter fragmenter = new SimpleSpanFragmenter(scorer, 100);


        Highlighter highlighter = new Highlighter(scorer);
        highlighter.setTextFragmenter(fragmenter);
        String[] fragments = highlighter.getBestFragments(stream, fieldContents, 5);

為了實現(xiàn)關(guān)鍵詞高亮顯示,必須知道關(guān)鍵詞在文本中的位置。對英文來說,可以在搜索的時候?qū)崟r切分出位置。但是中文分詞的速度一般來說相對慢很多。在Lucene1.4.3以后的版本中,TermVector支持保存Token.getPositionIncrement()和Token.startOffset() 以及Token.endOffset() 信息。利用Lucene中新增加的Token信息保存結(jié)果以后,就不需要為了高亮顯示而在運行時解析每篇文檔。為了實現(xiàn)一列的高亮顯示,索引的時候通過Field對象保存該位置信息。

        //增加文檔時保存term位置信息
        private void addDoc(IndexWriter writer, String text) throws IOException{
            Document d = new Document();


            Field f = new Field(FIELD_NAME, text ,
                            Field.Store.YES, Field.Index.TOKENIZED,
                            Field.TermVector.WITH_POSITIONS_OFFSETS);
            d.add(f);
            writer.addDocument(d);
        }
        //利用term位置信息節(jié)省Highlight時間
        void doStandardHighlights() throws Exception{
            Highlighter highlighter =new Highlighter(this, new QueryScorer(query));
            highlighter.setTextFragmenter(new SimpleFragmenter(20));
            for (int i = 0; i < hits.length(); i++) {
                  String text = hits.doc(i).get(FIELD_NAME);
                  int maxNumFragmentsRequired = 2;
                  String fragmentSeparator = "...";
                  TermPositionVector tpv =
                    (TermPositionVector)reader.getTermFreqVector(hits.id(i), FIELD_NAME);
                  TokenStream tokenStream=TokenSources.getTokenStream(tpv);


                  String result = highlighter.getBestFragments(
                                      tokenStream,
                                      text,
                                      maxNumFragmentsRequired,
                                      fragmentSeparator);


                  System.out.println("\t" + result);
            }
        }

最后把highlight包中的一個額外的判斷去掉。對于中文來說沒有明顯的單詞界限,所以下面這個判斷是錯誤的:

        tokenGroup.isDistinct(token)

注意上面代碼中的highlighter.setTextFragmenter(new SimpleFragmenter(20)), SimpleFragmenter是一個最簡單的段落分割器,它把文章按20個字分成一個段落。這種方式簡單易行,但顯得比較初步。有時會有一些沒意義的符號出現(xiàn)在摘要的起始部分,例如逗號出現(xiàn)在摘要的開始位置。

RegexFragmenter是一個改進版本的段落分割器。它通過一個正則表達式匹配可能的熱點區(qū)域。但它是為英文定制的。我們可以讓它認(rèn)識中文的字符段。

        protected static final Pattern textRE = Pattern.compile("[\\w\u4e00-\u9fa5]+");

這樣使用highlighter就變成了:

        highlighter.setTextFragmenter(new RegexFragmenter(descLenth));

比如“我的媽媽”, Google搜索是這樣:“<em>我的</em> <em>媽媽</em>”。實際貌似Lucene都會變成“<em>我</em><em>的</em><em>媽媽</em>”,這樣對SEO(搜索引擎優(yōu)化)很不好。<em>標(biāo)簽算是權(quán)重很高的標(biāo)簽,這樣分使得頁面會降很低,因為詞都是分開的。另外,合并到一起,也省流量,對SEO有利。

不要高亮顯示太長的文本,因為這樣會影響搜索速度。

主站蜘蛛池模板: 额尔古纳市| 长治市| 桃园市| 凌源市| 筠连县| 建瓯市| 格尔木市| 永年县| 武城县| 武穴市| 邻水| 永德县| 银川市| 巫山县| 娄烦县| 巫山县| 思茅市| 浠水县| 台湾省| 呼和浩特市| 宁乡县| 高阳县| 保定市| 社旗县| 偏关县| 嵩明县| 龙井市| 台湾省| 安吉县| 沁阳市| 丰宁| 铜山县| 微博| 大石桥市| 郧西县| 黄石市| 长乐市| 铜陵市| 隆尧县| 佛冈县| 仁化县|