[转载]Lucene.Net:构造搜索表达式简化搜索

[转载]Lucene.Net:构造搜索表达式简化搜索 – 努力,天赋,笑容,自信 – 博客园.

我们知道利用Lucene.Net的不同的Query(常见如BooleanQuery,RangeQuery等等),可以有针对性地进行各种不同 类型的搜索。利用QueryParser(或MultiFieldQueryParser),配合构造好的搜索关键字(搜索表达式),也可以实现不同类型 的搜索。本文重点就是简单介绍一下搜索表达式和不同类型的Query之间的简单对比。本文最后的demo,QueryApp工程下有文章里贴出的大部分示 例代码,代码自己会说话,有时候它可能更好地表达了文章作者的思路。您可以下载对照着本文进行阅读。

一、与或非

1、与

举例:搜索contents既包含“jeffreyzhao”,又有“ 老赵”的记录:

1 public static void NormalQueryParserTest(Analyzer analyzer, string field, string keyword)
2 {
3 QueryParser parser = new QueryParser(Version.LUCENE_29, field, analyzer);
4 Query query = parser.Parse(keyword);
5 ShowQueryExpression(analyzer, query, keyword);
6 SearchToShow(query);
7 Console.WriteLine();
8 }

调用的时候,我们构造一个搜索关键词“+jeffreyzhao +老赵”:

1 string field = "contents";//搜索的对应字段
2 keyword = "+jeffreyzhao +老赵";
3 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//+contents:jeffreyzhao +contents:"老 赵"

搜索结果中我们可以看到,通过加号(+)可以表达与(AND)的关系(+contents:jeffreyzhao +contents:”老 赵” )。

特点:不同关键字越多,匹配的结果可能越少。

2、或

输入多个关键字,任何包含其中一个关键字的记录都被搜索出来:

1 string keyword = "jeffreyzhao 老赵";//搜索输入关键词
2 string field = "contents";//搜索的对应字段
3 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword); //contents:jeffreyzhao contents:"老 赵"

特点:不同关键字越多,匹配的结果可能越多。

3、非(!)

1 keyword = "+jeffreyzhao -老赵";
2 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//+contents:jeffreyzhao -contents:"老 赵"
3
4 keyword = "+jeffreyzhao !老赵";
5 LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//+contents:jeffreyzhao -contents:"老 赵"

上面的两种写法,转换成表达式都是+contents:jeffreyzhao -contents:”老 赵” 。

根据我们的测试结果,与或非的关系可以总结如下:

a & b  =>   +a +b
a || b  =>   a    b
a  !b   =>   +a  -b

这种与或非的关系,我们还可以通过BooleanQuery表达同样的搜索:

01 public static void BooleanQueryTest(Analyzer analyzer, string field, string keyword, BooleanClause.Occur[] flags)
02 {
03 Console.WriteLine("====BooleanQuery====");
04 string[] arrKeywords = keyword.Trim().Split(new char[] { ' ', ',', ',', '、' }, StringSplitOptions.RemoveEmptyEntries);
05 QueryParser parser = new QueryParser(Version.LUCENE_29, field, analyzer);
06 BooleanQuery bq = new BooleanQuery();
07 int counter = 0;
08 foreach (string item in arrKeywords)
09 {
10 Query query = parser.Parse(item);
11 bq.Add(query, flags[counter]);
12 counter++;
13 }
14 ShowQueryExpression(analyzer, bq, keyword);
15 SearchToShow(bq);
16 Console.WriteLine();
17 }

其中BooleanClause.Occur(MUST:+  MUST_NOT:-   SHOULD:无符号)的选择至关重要:

01 string field = "contents";//搜索的对应字段
02 IList<Analyzer> listAnalyzer =LuceneAnalyzer. BuildAnalyzers();
03 BooleanClause.Occur[] occurs = new BooleanClause.Occur[] { BooleanClause.Occur.MUST, BooleanClause.Occur.MUST };
04 foreach (Analyzer analyzer in listAnalyzer)
05 {
06
07 //NormalQueryTest(analyzer);
08 //LuceneSearch.NormalQueryParserTest(analyzer, field, keyword);//直接通过QueryParser配合构造好的查询表达式搜索
09
10 //LuceneSearch.TermQueryTest(analyzer, field, "高手");//contents:高手
11
12 LuceneSearch.BooleanQueryTest(analyzer, field, "jeffreyzhao 老赵", occurs);//+contents:jeffreyzhao +contents:"老 赵"
13
14 //LuceneSearch.RangeQueryTest(analyzer, rangeField, start, end); // createdate:[20101010 TO 20110101]  createdate:[20101010 TO 20110101}
15
16 //LuceneSearch.PrefixQueryTest(analyzer, field, "hell"); // contents:hell*  (可以找到hello world那一项)
17
18 //LuceneSearch.WildcardQueryTest(analyzer, field, "高手"); //contents:高手
19
20 //LuceneSearch.FuzzyQueryTest(analyzer, field, "牛"); //contents:牛~0.5
21
22 //LuceneSearch.PhraseQueryTest(analyzer, field, "hello world", 1); //contents:"hello world"~1
23
24 //LuceneSearch.MulFieldsSearchTest(analyzer, fieldArr, "博  园", occurs); //+(contents:博 contents:园) +(title:博 title:园)
25 }

二、范围

01 string rangeField = "createdate";//范围搜索对应字段
02 string start = "20101010";
03 string end = "20110101";
04 IList<Analyzer> listAnalyzer =LuceneAnalyzer. BuildAnalyzers();
05 foreach (Analyzer analyzer in listAnalyzer)
06 {
07
08 LuceneSearch.RangeQueryTest(analyzer, rangeField, start, end); // createdate:[20101010 TO 20110101]  createdate:[20101010 TO 20110101}
09
10 }

同样道理,RangeQuery(或者TermRangeQuery)也可以实现范围搜索。

三、多字段组合搜索

搜索时,对两个或多个字段进行匹配的时候,可以用下面的方法:

01 public static void MulFieldsSearchTest(Analyzer analyzer, string[] fields, string keyword, BooleanClause.Occur[] flags)
02 {
03 Console.WriteLine("====MultiFieldQueryParser====");
04 MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_29, fields, analyzer);
05 //Query query = parser.Parse(keyword);
06 Query query = MultiFieldQueryParser.Parse(Version.LUCENE_29, keyword, fields, flags, analyzer);
07 ShowQueryExpression(analyzer, query, keyword);
08 SearchToShow(query);
09 Console.WriteLine();
10 }

简单调用如下:

1 string[] fieldArr = new string[] { field, "title" };//两个字段
2 IList<Analyzer> listAnalyzer =LuceneAnalyzer. BuildAnalyzers();
3 foreach (Analyzer analyzer in listAnalyzer)
4 {
5 LuceneSearch.MulFieldsSearchTest(analyzer, fieldArr, "博  园", occurs); //+(contents:博 contents:园) +(title:博 title:园)
6 }

如果我们把搜索关键字改为“博 -园”,则表达式就是“+(contents:博 -contents:园) +(title:博 -title:园)”,这也符合单个字段搜索。

注意:如你所知,与或非和范围不是搜索关系的全部。实际上,通过Lucene,你可以根据 +-!():^[]{}~*? 这几种符号,合理构造出表达真实意图的复杂表达式来代替不同类型的Query。我在示例代码中做了几个针对StandardAnalyzer的简单尝试,测试结果符合预期。

我在参考网上不少文章的时候,发现很多提到的问题都没有重现,再看他们的lucene的版本都低于2.0,我大胆猜测Lucene.Net的类库已经改进了不少,一开始还以为自己的测试不到位,囧。

四、分词效果

Analyzer选择不同,搜索结果也不同,尤其是对于中文。用下面的函数可以测试分词效果:

01 /// <summary>
02 /// 测试不同的Analyzer分词效果
03 /// </summary>
04 /// <param name="listAnalyzer"></param>
05 /// <param name="input"></param>
06 public static void TestAnalyzer(IList<Analyzer> listAnalyzer, string input)
07 {
08 foreach (Analyzer analyzer in listAnalyzer)
09 {
10 Console.WriteLine(string.Format("{0}:", analyzer.ToString()));
11
12 using (TextReader reader = new StringReader(input))
13 {
14 TokenStream stream = analyzer.ReusableTokenStream(string.Empty, reader);
15 Lucene.Net.Analysis.Token token = null;
16 while ((token = stream.Next()) != null)
17 {
18 Console.WriteLine(token.TermText());
19 }
20 }
21
22 Console.WriteLine();
23 }
24 }

不同的Analyzer,分词效果可以总结如下:

StandardAnalyzer       对中文单字拆分;

WhitespaceAnalyzer  按空格拆分,对中文的支持不好;

KeywordAnalyzer       输入什么,分词就是什么;

SimpleAnalyzer           按标点和空格拆分,对中文的支持不好

StopAnalyzer               和SimpleAnalyzer类似;

选来选去,StandardAnalyzer 的效果还是很不错的,一般的应用差不多就够用了。 您可以使用不同的Analyzer,然后对比它们的搜索表达式并找出它们的不同之处。

demo下载:LuceneNetApp

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏