【转载】Lucene.Net无障碍学习和使用:搜索篇
在上一篇中,我们初步理解了索引的增删改查基本操作。本文着重介绍一下常用的搜索,以及搜索结果的排序和分页。本文的搜索主要是基于前一篇介绍的文本文件的索引,建议下载最后改进的demo对照着看阅读本文,同时大家可以自己动手创建一些测试文本,然后建立索引并搜索试试看。
一、初步认识搜索
先从上一篇示例代码中我们摘录一段代码看看搜索的简单实现:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private TopDocs Search(string keyword,string field) { TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { QueryParser parser = new QueryParser(field, new StandardAnalyzer()); Query query = parser.Parse(keyword);//搜索内容 contents (用QueryParser.Parse方法实例化一个查询) Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); //获取搜索结果 watch.Stop(); StringBuffer sb = "索引完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs; } |
从上面代码,我们不难看出,搜索需要用到IndexSearcher,Query,QueryParser和TopDocs(或者Hits)四个核心类:
1、 IndexSearcher
IndexSearcher会打开索引文件,它不使用Lucene.Net的锁,可以理解为只读操作。它的Search方法是我们最常用的,该方法返回我们需要的结果。
2、QueryParser
QueryParser是Query的构造器,它的Parse方法会根据Analyzer构造一个合理的Query对象来应对搜索。
3、Query
Query类作为查询表达式的载体同样至关重要,它有丰富的子类,让我们可以应对多种变化的搜索需求,简单来说,我们想到的常用搜索Lucene.Net几乎已经都给我们实现了,你只要分辨应该使用那个类来搜索比较合理。
4、TopDocs(或者Hits)
这个类我们可以简单把它理解成它就是我们要的搜索结果集,通过它我们可以知道记录集合中的各个Document的详细信息:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/// <summary> /// 显示搜索结果 /// </summary> /// <param name="queryResult"></param> private void ShowFileSearchResult(TopDocs queryResult) { if (queryResult == null || queryResult.totalHits == 0) { SetOutput("Sorry,没有搜索到你要的结果。"); return; } int counter = 1; foreach (ScoreDoc sd in queryResult.scoreDocs) { try { Document doc = searcher.Doc(sd.doc); string id = doc.Get("id");//获取id string fileName = doc.Get("filename");//获取文件名 string contents = doc.Get("contents");//获取文件内容 string result = string.Format("这是第{0}个搜索结果,Id为{1},文件名为:{2},文件内容为:{3}{4}", counter, id, fileName, Environment.NewLine, contents); SetOutput(result); } catch (Exception ex) { SetOutput(ex.Message); } counter++; } } |
毫无疑问,搜索结果的准确性和Query以及QueryParser密切相关,其实还和一个东西有莫大的关系,下面我会再次提到。搜索的过程就是对索引文件进行查找的过程。我们可以直白地这样理解:索引文件好比是数据库,查询表达式就像是T-SQL语句,最后通过Lucene.Net搜索引擎找到结果集。
二、几种常用的Query介绍
A、单个索引文件进行搜索举例
1、TermQuery
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private TopDocs TermQuery(string keyword, string field){ TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { Term t = new Term(field, keyword); Query query = new TermQuery(t); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); watch.Stop(); StringBuffer sb = "TermQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs;} |
这个Query主要用来查询关键字。我在测试TermQuery的时候,输入”jeff“是可以搜索到一条记录的,然后我输入找到的这条记录内容中的两个汉字“喜欢”,这一次却没有返回结果(当然,搜索不到也不能表示Jeff Wong还没有喜欢的人。 ^_^):

为什么呢?上面我们说搜索的准确程度和Query、QueryParser相关,根据我参考的几个文档,我怀疑第三个因素就是Analyzer(期待高人指点,下面的BooleanQuery等几个搜索,用英文“think”可以搜出结果,但用中文也搜索不到结果),这里搜索不到的原因可能就是没有指定Analyzer为StandardAnalyzer(存疑,TO DO)。
注:只要在QueryParse的Parse方法中只有一个keyword,就会自动转换成TermQuery。
2、RangeQuery
用于查询范围,看下面的示例:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
private TopDocs RangeQuery( string field,string start,string end,bool isInclude) { TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索,id范围为{0}~{1}", start,end)); try { Term beginT = new Term(field, start); Term endT = new Term(field, end); Query query = new RangeQuery(beginT, endT, isInclude); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); watch.Stop(); StringBuffer sb = "RangeQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs; } |
取id在3和5之间的满足搜索条件的结果集。
3、BooleanQuery
这个Query也经常使用,用于搜索满足多个条件的查询:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
private TopDocs BooleanQuery(string keyword, string field) { string[] words = keyword.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { BooleanQuery boolQuery = new BooleanQuery(); Term beginT = new Term("id", "3"); Term endT = new Term("id", "5"); RangeQuery rq = new RangeQuery(beginT, endT, true); //rangequery id从3到5 for (int i = 0; i < words.Length; i++) { TermQuery tq = new TermQuery(new Term(field, words[i]));//termquery boolQuery.Add(tq, BooleanClause.Occur.MUST); } boolQuery.Add(rq, BooleanClause.Occur.MUST); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(boolQuery, (Filter)null, n); watch.Stop(); StringBuffer sb = "BooleanQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs; } |
希望大家注意到输入中文搜索不到结果的问题。我测试可能的几种情况都没有搜出结果。
4、PrefixQuery
PrefixQuery用于搜索是否包含某个特定前缀,常用于分类(Catalog)的检索:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private TopDocs PrefixQuery(string keyword, string field){ TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { Term t = new Term(field, keyword); PrefixQuery query = new PrefixQuery(t); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); watch.Stop(); StringBuffer sb = "PrefixQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs;} |
本文测试的时候,输入“ja”就可以搜到包含java和javascript两项结果了。
5、FuzzyQuery
模糊查询:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private TopDocs FuzzyQuery(string keyword, string field) { TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { Term t = new Term(field, keyword); FuzzyQuery query = new FuzzyQuery(t); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); watch.Stop(); StringBuffer sb = "FuzzyQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs; } |
我还从没有在项目中用过这个Query。
6、WildcardQuery
通配符搜索:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private TopDocs WildcardQuery(string keyword, string field) { TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { Term t = new Term(field, keyword); WildcardQuery query = new WildcardQuery(t); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); watch.Stop(); StringBuffer sb = "WildcardQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs; } |
本文示例您可以试着输入“java*”,它可以把包含java和javascript两项结果取出来。感觉这个比较好使,但是必须熟悉通配符的使用语法(看上去和正则类似)。
7、PhraseQuery
查询短语,这里面主要有一个slop的概念, 也就是各个词之间的位移偏差, 这个值会影响到结果的评分,可以通过SetSlop方法进行设定:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
private TopDocs PhraseQuery(string keyword, string field,int slop) { string[] words = keyword.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { PhraseQuery query = new PhraseQuery(); query.SetSlop(slop); foreach (string word in words) { Term t = new Term(field, word); query.Add(t); } Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); watch.Stop(); StringBuffer sb = "PhraseQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs; } |
本文示例中,输入“think javascript”,并且设置slop为1,即可命中“think in javascript”那一项。
注意,旧的参考资料上说PhraseQuery对于短语的顺序是不管的(存疑,我测试的时候输入“javascript think”,就没有匹配任何结果),这点在查询时虽然提高了命中率,却会对性能产生很大的影响。
到这里,您可能会说,哇,介绍了这么多,应该已经涵盖了大部分可能的搜索情况了吧?嘿嘿,好问题,实际上万里长征我们才走到第一步。上面的代码示例基本上都是对内容(contents)或者id进行匹配搜索,这里我还要补充一种情况,就是同一索引文件下,对多个字段进行搜索,比如下面的代码就是通过MultiFieldQueryParser实现的对id和contents同时进行搜索:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
/// <summary>/// 多字段搜索(以空格,逗号等分隔符隔开)/// </summary>/// <param name="keyword"></param>/// <returns></returns>private TopDocs MulFieldsSearch(string keyword){ TopDocs docs = null; int n = 100; SetOutput("正在检索关键字:" + keyword); try { BooleanClause.Occur[] flags=new BooleanClause.Occur[]{BooleanClause.Occur.MUST,BooleanClause.Occur.MUST}; string[] fields = new string[] { "id", "contents" }; string[] values = keyword.Trim().Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); if (fields.Length != values.Length) { throw new Exception("字段和对应值不一致"); } //MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer()); //parser.SetDefaultOperator(QueryParser.Operator.OR);//或者的关系 //Query query = parser.Parse(keyword); Query query = MultiFieldQueryParser.Parse(values, fields, flags, new StandardAnalyzer()); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); //排序获取搜索结果 watch.Stop(); StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); } return docs;} |
示例中,我输入“1 喜欢”,就可以把id为1,且内容保护“喜欢”的那条记录显示出来了。搜索结果如图:

注:上面介绍的这几种Query也适用于下面B中要介绍的多个索引文件搜索。
B、多个索引文件进行搜索举例
说一下我在本地的测试:索引文件index建好后,其他多余的索引文件我们就不费事了,直接把建好的索引index文件夹复制一份,命名为index1,然后就是通过MultiSearcher类进行多索引文件操作:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
/// <summary>/// 根据多个索引文件夹搜索/// </summary>/// <param name="keyword"></param>/// <returns></returns>private TopDocs MultiSearch(string keyword, string field){ TopDocs docs = null; int n = 20;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); Searchable[] abs = new Searchable[2]; abs[0] = new IndexSearcher(INDEX_STORE_PATH); abs[1] = new IndexSearcher(INDEX_STORE_PATH1); MultiSearcher searcher = new MultiSearcher(abs);//构造MultiSearcher try { QueryParser parser = new QueryParser(field, new StandardAnalyzer()); Query query = parser.Parse(keyword); Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); //排序获取搜索结果 watch.Stop(); StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs;}/// <summary>/// 显示搜索结果/// </summary>/// <param name="queryResult"></param>private void ShowMultFileSearchResult(TopDocs queryResult){ if (queryResult == null || queryResult.totalHits == 0) { SetOutput("Sorry,没有搜索到你要的结果。"); return; } Searchable[] abs = new Searchable[2]; abs[0] = new IndexSearcher(INDEX_STORE_PATH); abs[1] = new IndexSearcher(INDEX_STORE_PATH1); MultiSearcher searcher = new MultiSearcher(abs);//构造MultiSearcher int counter = 1; foreach (ScoreDoc sd in queryResult.scoreDocs) { try { Document doc = searcher.Doc(sd.doc); string id = doc.Get("id");//获取id string fileName = doc.Get("filename");//获取文件名 string contents = doc.Get("contents");//获取文件内容 string result = string.Format("这是第{0}个搜索结果,Id为{1},文件名为:{2},文件内容为:{3}{4}", counter, id, fileName, Environment.NewLine, contents); SetOutput(result); } catch (Exception ex) { SetOutput(ex.Message); } counter++; }} |
搜索结果不出意外,通常都是匹配两份:

三、排序
排序我们通常都需要用到Sort和SortField类,顾名思义,这两个类专门是用来排序的:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/// <summary>/// 根据索引排序搜索/// </summary>/// <param name="keyword"></param>/// <returns></returns>private TopDocs SortSearch(string keyword, string field){ TopDocs docs = null; int n = 10;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { QueryParser parser = new QueryParser(field, new StandardAnalyzer());//针对内容查询 Query query = parser.Parse(keyword);//搜索内容 contents (用QueryParser.Parse方法实例化一个查询) Stopwatch watch = new Stopwatch(); bool sortDirection = true; if (chkIsSortById.Checked == true)//按照id升序 { sortDirection = false; } watch.Start(); Sort sort = new Sort(); SortField sf = new SortField("id", SortField.INT, sortDirection);//按照id字段排序,false表示升序,ture表示逆序 //SortField sf = new SortField("filename", SortField.DOC, false);//按照filename字段排序,false表示升序 sort.SetSort(sf); //多个条件排序 //Sort sort = new Sort(); //SortField f1 = new SortField("id", SortField.INT, false); //SortField f2 = new SortField("filename", SortField.DOC, false); //sort.SetSort(new SortField[] { f1, f2 }); docs = searcher.Search(query, (Filter)null, n, sort); //排序获取搜索结果 watch.Stop(); StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs;} |
可以按照多个字段进行排序,据可靠消息,排序会降低搜索效率,所以通常对于数据量较频繁或者较大的搜索,通常都会采取某一些策略,先取出来再在程序中进行排序。
下图是简单的按照id升序的排序结果:

四、分页
本文的分页搜索主要就是利用旧文介绍的一个分页控件,根据返回的结果总数(TopDocs的totalHits),利用当前页和每页记录数进行分页,而不是像数据库分页一样,一次就取出某一页的每页记录数,严格来讲,不算Lucene.Net自身的正确的分页(Lucene.Net的另一种分页需要用到缓存),待我再看看源码研究研究,过段时间再总结一下。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
/// <summary>/// 根据索引分页搜索/// </summary>/// <param name="keyword"></param>/// <returns></returns>private TopDocs PagerSearch(string keyword, string field){ TopDocs docs = null; int n = 10000;//最多返回多少个结果 SetOutput(string.Format("正在检索关键字:{0}", keyword)); try { QueryParser parser = new QueryParser(field, new StandardAnalyzer());//针对内容查询 Query query = parser.Parse(keyword);//搜索内容 contents (用QueryParser.Parse方法实例化一个查询) Stopwatch watch = new Stopwatch(); watch.Start(); docs = searcher.Search(query, (Filter)null, n); //排序获取搜索结果 watch.Stop(); StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb); } catch (Exception ex) { SetOutput(ex.Message); docs = null; } return docs;}/// <summary>/// 显示分页搜索结果/// </summary>/// <param name="queryResult"></param>private void ShowPagerSearchResult(TopDocs queryResult,int currentPage){ if (queryResult == null || queryResult.totalHits == 0) { SetOutput("Sorry,没有搜索到你要的结果。"); return; } int counter = 1; int start = (currentPage - 1) * recordsPerPage;//开始记录 int end = currentPage * recordsPerPage;//结束记录 if (end > queryResult.totalHits) { end = queryResult.totalHits; } for (int i = start; i < end; i++) { ScoreDoc sd = queryResult.scoreDocs[i]; try { Document doc = searcher.Doc(sd.doc); string id = doc.Get("id");//获取id string fileName = doc.Get("filename");//获取文件名 string contents = doc.Get("contents");//获取文件内容 string result = string.Format("这是第{0}页第{1}个搜索结果,Id为{2},文件名为:{3},文件内容为:{4}{5}",currentPage, counter, id, fileName, Environment.NewLine, contents); SetOutput(result); } catch (Exception ex) { SetOutput(ex.Message); } counter++; }}/// <summary>/// 将搜索结果分页显示/// </summary>/// <param name="currentPage"></param>private void BindPagerResults(int currentPage){ searcher = new IndexSearcher(INDEX_STORE_PATH); //构建一个索引搜索器 TopDocs queryResult = PagerSearch(this.txtPagerKeyword.Text.Trim(), "contents");//按照内容搜索 int totalCount = queryResult.totalHits;//总记录数 ShowPagerSearchResult(queryResult,currentPage); if (totalCount > 0) { this.panelPager.Visible = true; //绑定页码相关信息 PagerControl pager = new PagerControl(currentPage, recordsPerPage, totalCount, "跳转"); pager.currentPageChanged += new EventHandler(pager_currentPageChanged);//页码变化 触发的事件 this.panelPager.Controls.Add(pager);//在Panel容器中加入分页控件 }}private void pager_currentPageChanged(object sender, EventArgs e){ PagerControl pager = sender as PagerControl; if (pager == null || string.IsNullOrEmpty(this.txtPagerKeyword.Text.Trim())) { return; } SetOutput(string.Format("==========================分页搜索开始时间:{0}===========================", DateTime.Now.ToString())); currentPage = pager.CurrentPage; BindPagerResults(currentPage);//查询数据并分页绑定} |
有图有真相:

关于分页控件,请参考拙文winform下的一个分页控件。
好了,简单常见的搜索就介绍到这里,有时间我会继续总结QueryParser的常用搜索语法和技巧以及一些复杂搜索(包括令人头疼的分组问题等等)。
最后,我想说的是,没有合理的索引数据作为搜索的前期准备,好比想要在程序员中找到漂亮体贴亭亭玉立温柔贤惠善解人意的女朋友一样,大部分工作和精力可能都是徒劳的,所以,合理创建索引的工作至关重要,在实际的开发中,按照项目需要,最大程度构造和优化您的索引吧。
改进后的demo下载:LuceneNetApp
转自:http://www.cnblogs.com/jeffwongishandsome/archive/2010/12/19/1910697.html
【转载】Lucene.Net无障碍学习和使用:搜索篇的更多相关文章
- Lucene.Net无障碍学习和使用:搜索篇
一.初步认识搜索 先从上一篇示例代码中我们摘录一段代码看看搜索的简单实现: private TopDocs Search(string keyword,string field) { TopDocs ...
- Lucene.Net无障碍学习和使用:索引篇
一.简单认识索引 Lucene.Net的应用相对比较简单.一段时间以来,我最多只是在项目中写点代码,利用一下它的类库而已,对很多名词术语不是很清晰,甚至理解 可能还有偏差.从我过去的博客你也可以看出, ...
- [转载]SharePoint 2013搜索学习笔记之搜索构架简单概述
Sharepoint搜索引擎主要由6种组件构成,他们分别是爬网组件,内容处理组件,分析处理组件,索引组件,查询处理组件,搜索管理组件.可以将这6种组件分别部署到Sharepoint场内的多个服务器上, ...
- Lucene.net入门学习
Lucene.net入门学习(结合盘古分词) Lucene简介 Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全 ...
- 使用 Apache Lucene 和 Solr 4 实现下一代搜索和分析
使用 Apache Lucene 和 Solr 4 实现下一代搜索和分析 使用搜索引擎计数构建快速.高效和可扩展的数据驱动应用程序 Apache Lucene™ 和 Solr™ 是强大的开源搜索技术, ...
- Lucene.net入门学习系列(2)
Lucene.net入门学习系列(2) Lucene.net入门学习系列(1)-分词 Lucene.net入门学习系列(2)-创建索引 Lucene.net入门学习系列(3)-全文检索 在使用Luce ...
- Lucene.net入门学习系列(1)
Lucene.net入门学习系列(1) Lucene.net入门学习系列(1)-分词 Lucene.net入门学习系列(2)-创建索引 Lucene.net入门学习系列(3)-全文检索 这几天在公 ...
- 用Lucene对文档进行索引搜索
问题 现在给出很多份文档,现在对某个搜索词感兴趣,想找到相关的文档. 简单搜索 一种简单粗暴的做法是: 1.读取每个文档:2.找到其中含有搜索词的文档:3.对找到的文档中搜索词出现的次数统计:4.根据 ...
- 利用Lucene.net搜索引擎进行多条件搜索的做法
利用Lucene.net搜索引擎进行多条件搜索的做法 2018年01月09日 ⁄ 搜索技术 ⁄ 共 613字 ⁄ 字号 小 中 大 ⁄ 评论关闭 利用Lucene.net搜索引擎进行多条件搜索的做法 ...
随机推荐
- comet在asp.net中的实现
网上有关“服务器推送”的介绍非常多,其中一种实现方式就是采用comet技术,在浏览器与服务端之间建立一个http协议的“长连接”,所谓“长连接”,就是指浏览器到服务端的http请求不会马上得到服务端的 ...
- FusionCharts简单教程(三)-----FusionCharts的基本属性
通过前面两章的讲解我们可以制作出简单的图像,但是有时候我们需要对图像进行一个精确的规划,比如设置背景颜色.设置提示信息.设置间隔颜色等等,这时就需要我们对FusionCharts的细节有比 ...
- java中基本类型和包装类型实践经验
至今,小菜用java快两年了,有些事,也该有个总结. 基本类型和包装类型的概念在本文不作赘述. 如果这两种类型直接使用,倒没什么值得讨论的,无非就是自动装箱拆箱,java可以让你感觉不到他们的存在,但 ...
- SOA服务设计与实现的几个语言无关的原则速记
一.SOA定义 SOA即面向服务架构(Service-Oriented Architecture).在SOA中,一切皆服务.一个服务是通过消息交换来调用的程序,一个信息系统是共同完成一个特定任务的一组 ...
- 说说设计模式~门面模式(Facade)
返回目录 门面模式(Facade)属于结构型模式的一种,它符合面向对象的封装原则,但又不符合开闭原则,呵呵,今天我们主要说它的优点,不谈缺点. 定义 门面模式,是指提供一个统一的接口去访问多个子系统的 ...
- from表单iframe原网页嵌入
今天是巩固的from表单跟嵌入其他页面,同样的,学习到了新的知识. 温故而知新: iframe--在原页面嵌入其他页面,以窗口的样式 其中scrolling--滚动条 noresize--可调整大小 ...
- salesforce 零基础开发入门学习(十四)salesforce中工厂模式的运用
提到工厂模式,想必大家都很熟悉,工厂模式作为一种设计模式,同样在salesforce中适用. 举一个例子,笔作为基类,可以有钢笔,铅笔,圆珠笔等等.有一个笔的工厂,当你向它要钢笔,它就会生产一支钢笔; ...
- Java语言的个人理解
Java语言的个人理解(比价深层次吧) 大四的生活确实十分的奢靡,不锻炼,不读书,几乎就是当一天和尚撞一天钟的生活,太颓废了,还好自己不是这个样子,不过身体确实差了很多,昨天跑了一圈内环(4KM),今 ...
- docker快速入门+搭建javaweb环境
一.windows安装 不要安装旧的 boot2docker包,直接安装 DockerToolbox. 一路next,安装完成以后 试用 1.以管理员身份运行 docker quickstart te ...
- mongodb-$type、limit、skip、sort方法、索引、聚合
一.$type操作符 $type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果. MongoDB 中可以使用的类型如下表所示: 类型 数字 备注 Double 1 String 2 ...