如何在Lucene.Net中一个Document使用不同的分词(二)
昨天说了索引时,如何在Lucene.Net中一个Document使用不同的分词。
前文地址:如何在Lucene.Net中一个Document使用不同的分词(一)
今天讲的搜索时使用不同分词。
方法一、使用两个Parser就可以使用两个分词
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];
代码就是
{
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);
}
至此,就改造完毕了。
就可以像下面一样调用了:
//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/