如何在Lucene.Net中一个Document使用不同的分词(二)

昨天说了索引时,如何在Lucene.Net中一个Document使用不同的分词。

 

前文地址:如何在Lucene.Net中一个Document使用不同的分词(一)

 

今天讲的搜索时使用不同分词。

 

 

方法一、使用两个Parser就可以使用两个分词

 

            IndexSearcher searcher = new IndexSearcher("C:\\LuceneIndex");
            BooleanQuery bquery 
= new BooleanQuery();
            QueryParser titlequery 
= new QueryParser("title",new Lucene.Net.Analysis.Standard.StandardAnalyzer());
            QueryParser contentquery 
= new QueryParser("content"new Lucene.Net.Analysis.SimpleAnalyzer());

            bquery.Add(titlequery.Parse(
"标题"), BooleanClause.Occur.MUST);
            bquery.Add(titlequery.Parse(
"内容"), BooleanClause.Occur.MUST);

            Hits hits 
= searcher.Search(bquery);

            searcher.Close();

 

分析下表达式就是 +title:"标题" +content:"内容".

 

方法二、而如果要使用MultiFieldQueryParser来解析呢。

 

简单的做法就是用MultiFieldQueryParser类的静态Parser方法得到Query,形式上和用QueryParser 差不多。

而如果要 用MultiFieldQueryParser.Parse(new string[] { }, new string[] { }, new Lucene.Net.Analysis.Standard.StandardAnalyzer());这种实例化,那么又要进行框架改造了。

 

MultiFieldQueryParser是继承自QueryParser 类的,显然我们不需要在QueryParser 使用多种分词。但是MultiFieldQueryParser实例的Parse方法却是使用的父类的。那就不得不改造父类了。

 

分词处理是在QueryParser 类470行,由GetFieldQuery方法的TokenStream source = analyzer.TokenStream(field, new System.IO.StringReader(queryText));完成的。

 

这个地方改造就要注意技巧了,因为这个动作执行的方法被很多地方都用到了,改造成本比较高,而且一不小心就出错了。

重载一个,或者干脆直接换的方法的参数并不友好。因为QueryParser 类本身还有可能被使用,你换掉了,用MultiFieldQueryParser没问题,但是用QueryParser 就可能会出问题。

 

其实这个时候你的眼睛已经被代码迷惑,呵呵,GetFieldQuery方法在MultiFieldQueryParser被循环调用了。

MultiFieldQueryParser 的GetFieldQuery方法,循环调用了Query q = base.GetFieldQuery(fields[i], queryText);

 

是不是想到了什么呢?

 

首先给MultiFieldQueryParser类增加一个字段

private Analysis.Analyzer[] analyzers;

然后增加一个构造函数

        public MultiFieldQueryParser(Analysis.Analyzer[] analyzers, System.String[] fields)
            : this(fields, null)
        {
            if (fields.Length != analyzers.Length)
                throw new Exception("赋予的字段跟分词器数量不匹配");
            this.analyzers = analyzers;
        }

 

为什么要把新构造函数的参数调换位置?是因为this(fields, null)引用另外一个构造函数时候,如果不调换,会导致无法识别用的是哪个方法。

 

接着在GetFieldQuery方法Query q = base.GetFieldQuery(fields[i], queryText);之前切换掉分词器:

this.analyzer = this.analyzers[i];

 

代码就是

        public override Query GetFieldQuery(System.String field, System.String queryText, int slop)
        {
            
if (field == null)
            {
                System.Collections.ArrayList clauses 
= System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
                
for (int i = 0; i < fields.Length; i++)
                {
                    
//切换分词器
                    this.analyzer = this.analyzers[i];
                    Query q 
= base.GetFieldQuery(fields[i], queryText);
                    
if (q != null)
                    {
                        
//If the user passes a map of boosts
                        if (boosts != null)
                        {
                            
//Get the boost from the map and apply them
                            System.Single boost = (System.Single) boosts[fields[i]];
                            
//if (boost != null)      // {{Aroush-2.1 there is no null for 'boost'
                            if (boosts[fields[i]] != null)      // {{Aroush-2.1 will this line do?
                            {
                                q.SetBoost((
float) boost);
                            }
                        }
                        
if (q is PhraseQuery)
                        {
                            ((PhraseQuery) q).SetSlop(slop);
                        }
                        
if (q is MultiPhraseQuery)
                        {
                            ((MultiPhraseQuery) q).SetSlop(slop);
                        }
                        clauses.Add(
new BooleanClause(q, BooleanClause.Occur.SHOULD));
                    }
                }
                
if (clauses.Count == 0)
                
// happens for stopwords
                    return null;
                
return GetBooleanQuery(clauses, true);
            }
            
return base.GetFieldQuery(field, queryText);
        }

 

至此,就改造完毕了。

 

就可以像下面一样调用了:

 

 

            IndexSearcher searcher = new IndexSearcher("C:\\LuceneIndex");
            
//BooleanQuery bquery = new BooleanQuery();
            
//QueryParser titlequery = new QueryParser("title",new Lucene.Net.Analysis.Standard.StandardAnalyzer());
            
//QueryParser contentquery = new QueryParser("content", new Lucene.Net.Analysis.SimpleAnalyzer());
            MultiFieldQueryParser multiparser = new MultiFieldQueryParser(new Lucene.Net.Analysis.Analyzer[] { new Lucene.Net.Analysis.Standard.StandardAnalyzer(), new Lucene.Net.Analysis.SimpleAnalyzer() }, new string[] { "title","content" });
            
//bquery.Add(titlequery.Parse("标题"), BooleanClause.Occur.MUST);
            
//bquery.Add(contentquery.Parse("内容"), BooleanClause.Occur.MUST);
            Hits hits = searcher.Search(multiparser.Parse("标题"));

            searcher.Close();

 

 

全文完。 by yurow @http://www.cnblogs.com/birdshover/

posted @ 2008-07-22 20:36  Birdshover  阅读(3548)  评论(13编辑  收藏  举报