Lucene源代码之高亮显示《三》

 本篇为高亮显示源代码中最具重量级别的一个类。我现在只是把代码看了一遍,小的地方理解了一点,但仍然还未从整体上把握全局。
  1. package org.apache.lucene.search.highlight;
  2. import java.io.IOException;
  3. import java.io.StringReader;
  4. import java.util.ArrayList;
  5. import java.util.Iterator;
  6. import org.apache.lucene.analysis.Analyzer;
  7. import org.apache.lucene.analysis.TokenStream;
  8. import org.apache.lucene.util.PriorityQueue;
  9. /**
  10.  * Class used to markup highlighted terms found in the best sections of a
  11.  * text, using configurable(配置好的)tokenizers.
  12.  */
  13. public class Highlighter
  14. {
  15.     public static final  int DEFAULT_MAX_DOC_BYTES_TO_ANALYZE=50*1024;
  16.     private int maxDocBytesToAnalyze=DEFAULT_MAX_DOC_BYTES_TO_ANALYZE;
  17.     private Formatter formatter;
  18.     private Encoder encoder;
  19.     private Fragmenter textFragmenter=new SimpleFragmenter();
  20.     private Scorer fragmentScorer=null;
  21.     public Highlighter(Scorer fragmentScorer)
  22.     {
  23.         this(new SimpleHTMLFormatter(),fragmentScorer);
  24.     }
  25.     public Highlighter(Formatter formatter, Scorer fragmentScorer)
  26.     {
  27.         this(formatter,new DefaultEncoder(),fragmentScorer);
  28.     }
  29.     public Highlighter(Formatter formatter, Encoder encoder, Scorer fragmentScorer)
  30.     {
  31.         this.formatter = formatter;
  32.         this.encoder = encoder;
  33.         this.fragmentScorer = fragmentScorer;
  34.     }
  35.     /**
  36.      * Highlights chosen terms in a text, extracting the most relevant 
  37.      * section(提取最相关的部分).
  38.      *
  39.      * @param analyzer   the analyzer that will be used to split (分割)
  40.      * <code>text</code> into chunks(大块)
  41.      * @param text text to highlight terms in
  42.      * @param fieldName Name of field used to influence analyzer's 
  43.      * tokenization policy
  44.      *
  45.      * @return highlighted text fragment or null if no terms found
  46.      */
  47.     public final String getBestFragment(Analyzer analyzer, String fieldName,String text)
  48.     throws IOException
  49.     {
  50.         TokenStream tokenStream = analyzer.tokenStream(fieldName, new StringReader(text));
  51.         return getBestFragment(tokenStream, text);
  52.     }
  53.     /**
  54.      * Highlights chosen terms in a text, extracting the most relevant section.
  55.      * The document text is analysed in chunks to record hit statistics
  56.      * across the document. After accumulating stats(统计资料), the fragment with the highest score
  57.      * is returned
  58.      *
  59.      * @param tokenStream  a stream of tokens identified in the text parameter, including offset information.
  60.      * This is typically produced by an analyzer re-parsing a document's
  61.      * text. Some work may be done on retrieving(检索)TokenStreams more efficently
  62.      * by adding support for storing original text position data in the Lucene
  63.      * index but this support is not currently available (as of Lucene 1.4 rc2).
  64.      * @param text text to highlight terms in
  65.      *
  66.      * @return highlighted text fragment or null if no terms found
  67.      */
  68.     public final String getBestFragment(TokenStream tokenStream, String text)
  69.     throws IOException
  70.     {
  71.         String[] results = getBestFragments(tokenStream,text, 1);
  72.         if (results.length > 0)
  73.         {
  74.             return results[0];
  75.         }
  76.         return null;
  77.     }
  78.     /**
  79.      * Highlights chosen terms in a text, extracting the most relevant sections.
  80.      * This is a convenience method that calls
  81.      * {@link #getBestFragments(TokenStream, String, int)}
  82.      *
  83.      * @param analyzer   the analyzer that will be used to split <code>text</code>
  84.      * into chunks
  85.      * @param text text to highlight terms in
  86.      * @param maxNumFragments  the maximum number of fragments.
  87.      * 
  88.      * @deprecated This method incorrectly hardcodes the choice of fieldname. Use the
  89.      * method of the same name that takes a fieldname.(基本上不用了)
  90.      * @return highlighted text fragments (between 0 and maxNumFragments number of fragments)
  91.      */
  92.     public final String[] getBestFragments(
  93.             Analyzer analyzer,
  94.             String text,
  95.             int maxNumFragments)
  96.     throws IOException
  97.     {
  98.         TokenStream tokenStream = analyzer.tokenStream("field"new StringReader(text));
  99.         return getBestFragments(tokenStream, text, maxNumFragments);
  100.     }
  101.     /**
  102.      * Highlights chosen terms in a text, extracting the most relevant sections.
  103.      * This is a convenience method that calls
  104.      * {@link #getBestFragments(TokenStream, String, int)}
  105.      *
  106.      * @param analyzer   the analyzer that will be used to split <code>text</code>
  107.      * into chunks
  108.      * @param fieldName     the name of the field being highlighted (used by analyzer)
  109.      * @param text          text to highlight terms in
  110.      * @param maxNumFragments  the maximum number of fragments.
  111.      *
  112.      * @return highlighted text fragments (between 0 and maxNumFragments number of fragments)
  113.      */
  114.     public final String[] getBestFragments(
  115.             Analyzer analyzer,
  116.             String fieldName,
  117.             String text,
  118.             int maxNumFragments)
  119.     throws IOException
  120.     {
  121.         TokenStream tokenStream = analyzer.tokenStream(fieldName, new StringReader(text));
  122.         return getBestFragments(tokenStream, text, maxNumFragments);
  123.     }
  124.     /**
  125.      * Highlights chosen terms in a text, extracting the most relevant sections.
  126.      * The document text is analysed in chunks to record hit statistics
  127.      * across the document. 
  128.      * After accumulating stats, the fragments with the highest scores
  129.      * are returned as an array of strings in order of score (contiguous(连续的) fragments are merged into
  130.      * one in their original order to improve readability)
  131.      *
  132.      * @param text text to highlight terms in
  133.      * @param maxNumFragments  the maximum number of fragments.
  134.      *
  135.      * @return highlighted text fragments (between 0 and maxNumFragments number of fragments)
  136.      */
  137.     public final String[] getBestFragments(
  138.             TokenStream tokenStream,
  139.             String text,
  140.             int maxNumFragments)
  141.     throws IOException
  142.     {
  143.         maxNumFragments = Math.max(1, maxNumFragments); 
  144.         TextFragment[] frag =getBestTextFragments(tokenStream,text, true,maxNumFragments);
  145.         //Get text
  146.         ArrayList fragTexts = new ArrayList();
  147.         for (int i = 0; i < frag.length; i++)
  148.         {
  149.             if ((frag[i] != null) && (frag[i].getScore() > 0))
  150.             {
  151.                 fragTexts.add(frag[i].toString());
  152.             }
  153.         }
  154.         return (String[]) fragTexts.toArray(new String[0]);
  155.     }
  156.     /**
  157.      * Low level api to get the most relevant (formatted) sections of the document.
  158.      * This method has been made public to allow visibility of score information held in TextFragment objects.
  159.      * @param mergeContiguousFragments(合并临近的片段),
  160.      * @throws IOException
  161.      */
  162.     public final TextFragment[] getBestTextFragments(
  163.             TokenStream tokenStream,
  164.             String text,
  165.             boolean mergeContiguousFragments,
  166.             int maxNumFragments)
  167.     throws IOException
  168.     {
  169.         ArrayList docFrags = new ArrayList();
  170.         StringBuffer newText=new StringBuffer();
  171.         TextFragment currentFrag =  new TextFragment(newText,newText.length(), docFrags.size());
  172.         fragmentScorer.startFragment(currentFrag);
  173.         docFrags.add(currentFrag);
  174.         //实例化一个Fragment队列。
  175.         FragmentQueue fragQueue = new FragmentQueue(maxNumFragments);
  176.         try
  177.         {
  178.             org.apache.lucene.analysis.Token token;
  179.             String tokenText;
  180.             int startOffset;
  181.             int endOffset;
  182.             int lastEndOffset = 0;
  183.             textFragmenter.start(text);
  184.             TokenGroup tokenGroup=new TokenGroup();
  185.             //tokenStream 是实例中已经对text剖析过的。
  186.             token = tokenStream.next();
  187.             while ((token!= null)&&(token.startOffset()<maxDocBytesToAnalyze))
  188.             {
  189.                 if((tokenGroup.numTokens>0)&&(tokenGroup.isDistinct(token)))
  190.                 {
  191.                     startOffset = tokenGroup.matchStartOffset;
  192.                     endOffset = tokenGroup.matchEndOffset;
  193.                     tokenText = text.substring(startOffset, endOffset);
  194.                     String markedUpText=formatter.highlightTerm(encoder.encodeText(tokenText), tokenGroup);
  195.                     //把空格及前面沒有标记的字符串先加到newText中。
  196.                     if (startOffset > lastEndOffset)
  197.                         newText.append(encoder.encodeText(text.substring(lastEndOffset, startOffset)));
  198.                     newText.append(markedUpText);
  199.                     lastEndOffset=Math.max(endOffset, lastEndOffset);
  200.                     tokenGroup.clear();
  201.                     //check是否已经超过设定的字符数量,有新的片段生成。
  202.                     if(textFragmenter.isNewFragment(token))
  203.                     {
  204.                         currentFrag.setScore(fragmentScorer.getFragmentScore());
  205.                         //record stats for a new fragment
  206.                         currentFrag.textEndPos = newText.length();
  207.                         currentFrag =new TextFragment(newText, newText.length(), docFrags.size());
  208.                         fragmentScorer.startFragment(currentFrag);
  209.                         docFrags.add(currentFrag);
  210.                     }
  211.                 }
  212.                 tokenGroup.addToken(token,fragmentScorer.getTokenScore(token));
  213.                 token = tokenStream.next();
  214.             }
  215.             currentFrag.setScore(fragmentScorer.getFragmentScore());
  216.             if(tokenGroup.numTokens>0)
  217.             {
  218.                 /**
  219.                  * flush the accumulated text (same code as in above loop)
  220.                  * 闪存余下的的字段。
  221.                  */
  222.                 startOffset = tokenGroup.matchStartOffset;
  223.                 endOffset = tokenGroup.matchEndOffset;
  224.                 tokenText = text.substring(startOffset, endOffset);
  225.                 String markedUpText=formatter.highlightTerm(encoder.encodeText(tokenText), tokenGroup);
  226.                 if (startOffset > lastEndOffset)
  227.                     newText.append(encoder.encodeText(text.substring(lastEndOffset, startOffset)));
  228.                 newText.append(markedUpText);
  229.                 lastEndOffset=Math.max(lastEndOffset,endOffset);
  230.             }
  231.             /**
  232.              * Test what remains of the original text beyond the point where we stopped analyzing 
  233.              * 检测一下源文件中我们没有分析到的文本。
  234.                if there is text beyond the last token considered and that text is not too large
  235.              */
  236.             if ((lastEndOffset < text.length())&&(text.length()<maxDocBytesToAnalyze))              
  237.             {
  238.                 //加到newText的后面。
  239.                 newText.append(encoder.encodeText(text.substring(lastEndOffset)));
  240.             }
  241.             currentFrag.textEndPos = newText.length();
  242.             /**
  243.              * sort the most relevant sections of the text
  244.              * 排序最关联部分。fragQueue难道有此功能?
  245.              */
  246.             for (Iterator i = docFrags.iterator(); i.hasNext();)
  247.             {
  248.                 currentFrag = (TextFragment) i.next();
  249.                 fragQueue.insert(currentFrag);
  250.             }
  251.             /**
  252.              * 出队列,保存到frag数组里。
  253.              */
  254.             TextFragment frag[] = new TextFragment[fragQueue.size()];
  255.             for (int i = frag.length - 1; i >= 0; i--)
  256.             {
  257.                 frag[i] = (TextFragment) fragQueue.pop();
  258.             }
  259.             /**
  260.              * merge any contiguous fragments to improve readability.
  261.              * 合并任何连续的碎片以提高可读性
  262.              */
  263.             if(mergeContiguousFragments)
  264.             {
  265.                 mergeContiguousFragments(frag);
  266.                 ArrayList fragTexts = new ArrayList();
  267.                 for (int i = 0; i < frag.length; i++)
  268.                 {
  269.                     if ((frag[i] != null) && (frag[i].getScore() > 0))
  270.                     {
  271.                         fragTexts.add(frag[i]);
  272.                     }
  273.                 }
  274.                 frag= (TextFragment[]) fragTexts.toArray(new TextFragment[0]);
  275.             }
  276.             return frag;
  277.         }
  278.         finally
  279.         {
  280.             if (tokenStream != null)
  281.             {
  282.                 try
  283.                 {
  284.                     tokenStream.close();
  285.                 }
  286.                 catch (Exception e)
  287.                 {
  288.                 }
  289.             }
  290.         }
  291.     }
  292.     /** 
  293.      * Improves readability of a score-sorted(评分分类) list of TextFragments by merging any fragments
  294.      * that were contiguous in the original text into one larger fragment with the correct order.
  295.      * This will leave a "null" in the array entry for the lesser scored fragment. 
  296.      * 
  297.      * @param frag An array of document fragments in descending score
  298.      */
  299.     private void mergeContiguousFragments(TextFragment[] frag)
  300.     {
  301.         boolean mergingStillBeingDone;
  302.         if (frag.length > 1)
  303.             do
  304.             {
  305.                 mergingStillBeingDone = false
  306.                 //for each fragment, scan other frags looking for contiguous blocks
  307.                 for (int i = 0; i < frag.length; i++)
  308.                 {
  309.                     if (frag[i] == null)
  310.                     {
  311.                         continue;
  312.                     }
  313.                     //合并所有连续的块。
  314.                     for (int x = 0; x < frag.length; x++)
  315.                     {
  316.                         if (frag[x] == null)
  317.                         {
  318.                             continue;
  319.                         }
  320.                         if (frag[i] == null)
  321.                         {
  322.                             break;
  323.                         }
  324.                         TextFragment frag1 = null;
  325.                         TextFragment frag2 = null;
  326.                         int frag1Num = 0;
  327.                         int frag2Num = 0;
  328.                         int bestScoringFragNum;
  329.                         int worstScoringFragNum;
  330.                         //if blocks are contiguous....
  331.                         if (frag[i].follows(frag[x]))
  332.                         {
  333.                             frag1 = frag[x];
  334.                             frag1Num = x;
  335.                             frag2 = frag[i];
  336.                             frag2Num = i;
  337.                         }
  338.                         else
  339.                             if (frag[x].follows(frag[i]))
  340.                             {
  341.                                 frag1 = frag[i];
  342.                                 frag1Num = i;
  343.                                 frag2 = frag[x];
  344.                                 frag2Num = x;
  345.                             }
  346.                         //merging required..
  347.                         if (frag1 != null)
  348.                         {
  349.                             if (frag1.getScore() > frag2.getScore())
  350.                             {
  351.                                 bestScoringFragNum = frag1Num;
  352.                                 worstScoringFragNum = frag2Num;
  353.                             }
  354.                             else
  355.                             {
  356.                                 bestScoringFragNum = frag2Num;
  357.                                 worstScoringFragNum = frag1Num;
  358.                             }
  359.                             frag1.merge(frag2);
  360.                             frag[worstScoringFragNum] = null;
  361.                             mergingStillBeingDone = true;
  362.                             frag[bestScoringFragNum] = frag1;
  363.                         }
  364.                     }
  365.                 }
  366.             }
  367.             while (mergingStillBeingDone);
  368.     }
  369.     /**
  370.      * Highlights terms in the  text , extracting the most relevant sections
  371.      * and concatenating the chosen fragments with a separator (typically "...").
  372.      * The document text is analysed in chunks to record hit statistics
  373.      * across the document. After accumulating stats, the fragments with the highest scores
  374.      * are returned in order as "separator" delimited strings.
  375.      *
  376.      * @param text        text to highlight terms in
  377.      * @param maxNumFragments  the maximum number of fragments.
  378.      * @param separator  the separator used to intersperse the document fragments (typically "...")
  379.      *
  380.      * @return highlighted text
  381.      */
  382.     public final String getBestFragments(
  383.             TokenStream tokenStream,    
  384.             String text,
  385.             int maxNumFragments,
  386.             String separator)
  387.     throws IOException
  388.     {
  389.         String sections[] = getBestFragments(tokenStream,text, maxNumFragments);
  390.         StringBuffer result = new StringBuffer();
  391.         for (int i = 0; i < sections.length; i++)
  392.         {
  393.             if (i > 0)
  394.             {
  395.                 result.append(separator);
  396.             }
  397.             result.append(sections[i]);
  398.         }
  399.         return result.toString();
  400.     }
  401.     /**
  402.      * @return the maximum number of bytes to be tokenized per doc 
  403.      */
  404.     public int getMaxDocBytesToAnalyze()
  405.     {
  406.         return maxDocBytesToAnalyze;
  407.     }
  408.     /**
  409.      * @param byteCount the maximum number of bytes to be tokenized per doc
  410.      * (This can improve performance with large documents)
  411.      */
  412.     public void setMaxDocBytesToAnalyze(int byteCount)
  413.     {
  414.         maxDocBytesToAnalyze = byteCount;
  415.     }
  416.     /**
  417.      */
  418.     public Fragmenter getTextFragmenter()
  419.     {
  420.         return textFragmenter;
  421.     }
  422.     /**
  423.      * @param fragmenter
  424.      */
  425.     public void setTextFragmenter(Fragmenter fragmenter)
  426.     {
  427.         textFragmenter = fragmenter;
  428.     }
  429.     /**
  430.      * @return Object used to score each text fragment 
  431.      */
  432.     public Scorer getFragmentScorer()
  433.     {
  434.         return fragmentScorer;
  435.     }
  436.     /**
  437.      * @param scorer
  438.      */
  439.     public void setFragmentScorer(Scorer scorer)
  440.     {
  441.         fragmentScorer = scorer;
  442.     }
  443.     public Encoder getEncoder()
  444.     {
  445.         return encoder;
  446.     }
  447.     public void setEncoder(Encoder encoder)
  448.     {
  449.         this.encoder = encoder;
  450.     }
  451. }
  452. /**
  453.  * FragmentQueue队列类的定义
  454.  */
  455. class FragmentQueue extends PriorityQueue
  456. {
  457.     public FragmentQueue(int size)
  458.     {
  459.         initialize(size);
  460.     }
  461.     public final boolean lessThan(Object a, Object b)
  462.     {
  463.         TextFragment fragA = (TextFragment) a;
  464.         TextFragment fragB = (TextFragment) b;
  465.         if (fragA.getScore() == fragB.getScore())
  466.             return fragA.fragNum > fragB.fragNum;
  467.             else
  468.                 return fragA.getScore() < fragB.getScore();
  469.     }
  470. }

还需慢慢推敲。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值