搜索引擎嘛当然最重要的功能在于检索,在前一篇博文中已经介绍了什么是Lucene,然后介绍了怎么样去创建索引。及在建索引中应该注意的一些问题。然后在这一篇博文中将要介绍一下建立好索引之后如何利用Lucene要索引文件中去快速的查找到数据。其中重点就是如何去组织检索表达式。可以这样理解如果是直接跟数据库去交互那么这个"SQL语句"或者说是“存储过程”应该怎样去拼接呢。
在前面已经建好索引,所以在搜索的时候需要知道索引存放的位置。下面是搜索前的一些准备工作。
#region 搜索准备工作
//
分析器
private
Analyzer m_Analyzer
=
new
PanGuAnalyzer();
//
索引路径
private
static
string
IndexPath
=
ConfigurationManager.AppSettings[
"
IndexFloder
"
].Trim();
private
string
m_IndexPath
=
System.Web.HttpContext.Current.Server.MapPath(IndexPath);
//
声明高亮显示的对象,将检索词在检索出来的数据中高亮显示
Highlighter highlighter
=
null
;
int
pageSize
=
20
; //分页显示记录条数,建议放有web.Config中或者直接放在数据库中,可以让用户自己设置每个显示记录数
#endregion
分析器还是建立索引时候的盘古分析器,建索引是经过盘古分析器建的索引,当然检索的时候也是需要同一个分析器,用同一种分析器说明是检索表达式与索引之间建立了一种特定的规则。也可以说类似于加密,解密的这样一个过程。
下面的代码来构建一个检索表达式。假设现在只有一个搜索入口,类似于谷歌,百度
这里只输入了一个词,但是你的数据里面有很多个字段,大部分字段里面都很有可能包含这个检索词。比如:标题,搞要,别名,摘要……都有可能包含检索词。我们平时写SQL的时候就会直接title like '%Lucene.NET%' or summary like...........这样就行了。Lucene中构建检索表达式其本质也是一样的。
1
2
///
<summary>
3
///
构建表达式
4
///
</summary>
5
///
<param name="s_Analyzer"></param>
6
///
<param name="search"></param>
7
///
<returns></returns>
8
public
BooleanQuery CreateQuery(Analyzer s_Analyzer,
string
search)
9 {
11
//
构造布尔查询,用于拼装各查询逻辑
12
BooleanQuery query
=
new
BooleanQuery();
13
query.Add(
new
MultiFieldQueryParser(
new
string
[] {
"
Field1
"
,"Field2"
,"Field3","Field4","Field5"}, s_Analyzer).Parse(search), BooleanClause.Occur.SHOULD);
14
15
highlighter
=
new
Highlighter(
new
SimpleHTMLFormatter(
"
<font color=\"red\">
"
,
"
</font>
"
),
new
QueryScorer(query));
//
使用高亮关键字显示替代关键字
16
highlighter.SetTextFragmenter(
new
SimpleFragmenter(
100
));
17
Session[
"
query
"
]
=
query;
18
return query;
19 }
BooleanQuery我们可以把它看成一个容器,像C#中的IList<T>,BooleanQuery里面Add的是一个一个的子查询语句。
然后BooleanClause用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST,BooleanClause.Occur.MUST_NOT,BooleanClause.Occur.SHOULD。必须包含,不能包含,可以包含三种.有以下6种组合:
1.MUST和MUST:取得连个查询子句的交集。
2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
3.SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。
4.SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。
5.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。
6.MUST_NOT和MUST_NOT:无意义,检索无结果。
所以在上面代码中的BooleanClause.Occur.SHOULD我们就完全可以理解为OR关系,于是数据中加在检索表达式中所有字段中只要包含检索词的数据都会检索出来
如果说是高级检索,用一个表单来提交数据,各个字段指向明确么我们就需要用到 BooleanClause.Occur.MUST关系了,也就是AND
如下面示例代码
1 public BooleanQuery CreateQuery(string name,string title,string summary,string content,int type)
2
{
3
//
构造布尔查询,用于拼装各查询的逻辑
4
BooleanQuery query
=
new
BooleanQuery();
5
6
if
(
!
(
string
.IsNullOrEmpty(name)))
7
{
8
query.Add(
new
MultiFieldQueryParser(
new
string
[] {
"
Name
"
}, m_Analyzer).Parse(name.ToString()), BooleanClause.Occur.MUST);
9
}
10
....................//此处重复代码就不写了
77
highlighter
=
new
Highlighter(
new
SimpleHTMLFormatter(
"
<font color=\"red\">
"
,
"
</font>
"
),
new
QueryScorer(query));
//
使用高亮关键字显示替代关键字,也可以将颜色换成自己喜欢的,此处为red
78
highlighter.SetTextFragmenter(
new
SimpleFragmenter(
100
));
79
Session[
"
query
"
]
=
query; //将检索表达式存入session中,这样是为了进行二次检索或多次检索。也就是在结果中过滤
80
return
query;
81
}
在只有一个入口的检索表达式构建中,那样笼统的去用OR关系检索出来的结果肯定是不太精确的。用的时候还可以对检索词做一下分析。比如用空格隔开的各检索词中若包含了数字,或者日期格式的,就可以明确一点直接指向时间或者相关数字类型的字段,这样检索出来的结果肯定会要更加精确一些。
检索表达式构建好了,下面就可以执行语句进行检索了。
1
2
3
///
<summary>
4
///
检索
5
///
</summary>
6
///
<param name="query">检索表达式</param>
8
///
<param name="page">页面索引值</param>
9
///
<param name="pageSize"></param>
10
///
<returns></returns> //此处用T替换了之前的类名
11
public
IList
<
T
>
Search(BooleanQuery query,
int
?
page,
int
pageSize)
12
{
13
//
读取索引文件
14
IndexSearcher searcher
=
new
IndexSearcher(m_IndexPath);
15
Hits hits
=
searcher.Search(query);
16
return
PrintHospitalResult(hits, ReturnPage(page), pageSize);
17
}
我们在创建索引时是用的IndexWriter这个类,那在检索时用到IndexSearcher这个类也就很容易理解了。同样要检索也是在索引文件当中,便将索引文件路径做为参数传入
Hits保存检索出来的结果,Hits可以理解为在这次检索中有多少个Document被命中,即被检索出来。
以下代码是分页显示数据,在页面中会需要用到一个分页控件,这里就不提供了。分页控件网上很多,也可以自己写一个,挺容易的。
1
2
3
///
<summary>
4
///
分页显示
5
///
</summary>
6
///
<param name="hits"></param>
7
///
<param name="pageNo"></param>
8
///
<param name="pageSize"></param>
9
///
<returns></returns>
10
private
IList
<
T
>
PrintHospitalResult(Hits hits,
int
pageNo,
int
pageSize)
11
{
12
IList
<
T
>
list
=
new
List
<
T
>
();
13
14
//
总数
15
int
recCount
= hits.Length();
17
//
分页
18
int
start
=
(pageNo
-
1
)
*
pageSize
>=
recCount
?
recCount : (pageNo
-
1
)
*
pageSize;
19
int
end
=
(start
+
pageSize)
>=
recCount
?
recCount : (start
+
pageSize);
20
21
for
(
int
i
=
start; i
<
end; i
++
)
22
{
23
Document doc
=
hits.Doc(i);
24
T p
=
new
T();
25
p.ID
=
Convert.ToInt32(doc.GetField(
"
ID
"
).StringValue());
26
HightLight(doc, p);
27
list.Add(p);
28
}
29
return
list;
30
}
我是用的MVC,其他框架得到了一个数据源再要去分页显示就很容易了。
以上只是实现了一次检索,不能在搜索出来的结果中再去检索,也就是二次检索。二次检索将在下一篇博文中介绍,然后后续可能会说得比较细一点,如分词,排序等的一些原理。敬请关注。也希望各位高手,牛人们能多给我提提意见,指导指导。在此感激不尽。