Lucene创建及使用方法
1.1 Lucene简介
Lucene.NET是一个全文搜索框架,,lucene的功能很单一,说到底,就是你给它若干个字符串,然后它为你提供一个全文搜索服务,告诉你你要搜索的关键词出现在哪里。知道了这个本质,你就可以发挥想象做任何符合这个条件的事情了。你可以把站内新闻都索引了,做个资料库;你可以把一个数据库表的若干个字段索引起来,那就不用再担心因为“%like%”而锁表了。
1.2 lucene的工作方式
lucene提供的服务实际包含两部分:一入一出。所谓入是写入,即将你提供的源(本质是字符串)写入索引或者将其从索引中删除;所谓出是读出,即向用户提供全文搜索服务,让用户可以通过关键词定位源。
用通俗的话讲就是:先创建索引,再从索引读取想要的数据。
A. 写入流程:即创建索引
一、 首先实例化一个构造器
IndexWriter writer
=
new
IndexWriter(
"
D:/index/
"
,
new
PanGuAnalyzer(),
true
);
//
索引的存储位置
这个构造函数具有三个参数:
1. path:索引文件存放的路径。如:String path = "E:\\index";
2. a:分词工具,因为lucene为外国人所开发,所以对中文分词不是很友好,但人类的智慧是无穷的,这里引入盘古分词,专门针对中文的分词。如:a= new PanGuAnalyzer(),盘古分词官网http://pangusegment.codeplex.com/
3. create:它是一个boolean型变量,如果为true,表示要重写指定的存放索引目录下的索引文件;如果为false,表示在指定存放索引目录下已经存在的索引文件的基础上,向其中继续追加新的索引文件。
二、 创建索引
第一步并未创建索引,只是实例化了一个索引器,建立索引的过程是在一个IndexWriter索引器实例存在的前提下,通过为其添加Document,这样才能真正添加索引。
代码如下:
View Code
foreach
(var item
in
productlist)
{
Document doc
=
new
Document();
doc.Add(
new
Field(
"
id
"
, item.id.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED));
//
其中ID、Name、Add都是数据库中的字段名,这个应该可以看明白的吧
doc.Add(
new
Field(
"
productname
"
, item.productname, Field.Store.YES, Field.Index.TOKENIZED));
doc.Add(
new
Field(
"
productdes
"
, item.productdes, Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.Add(
new
Field(
"
tradename
"
, item.tradename, Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.Add(
new
Field(
"
companyname
"
, item.companyname, Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.Add(
new
Field(
"
fhdes
"
, item.fhdes, Field.Store.YES, Field.Index.TOKENIZED));
doc.Add(
new
Field(
"
pic
"
, item.pic, Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.Add(
new
Field(
"
areaname
"
, item.areaname, Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.Add(
new
Field(
"
tradeid
"
, item.tradeid.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED));
writer.AddDocument(doc);
}
看完上面的代码先引入几个概念
1. Field:可以理解成索引文件中一个个的字段块,占用空间按字段长度分配。
2. Store:一个内部类,它是static的,主要为了设置Field的存储属性.
public
static final Store COMPRESS =
new Store(
"
COMPRESS
");
//
在索引中压缩存储Field的值
public
static final Store YES =
new Store(
"
YES
");
//
在索引中存储Field的值
public
static final Store NO =
new Store(
"
NO
");
//
在索引中不存储Field的值
3. Index: 通过Index设置索引方式,,不对Field进行索引,所以这个Field就不能被检索到(一般来说,建立索引而使它不被检索,这是没有意义的),如果对该Field还设置了Field.Store为Field.Store.YES或Field.Store.COMPRESS,则可以检索
public
static final Index TOKENIZED =
new Index(
"
TOKENIZED
");
//
对Field进行索引,同时还要对其进行分词(由Analyzer来管理如何分词)
public
static final Index UN_TOKENIZED =
new Index(
"
UN_TOKENIZED
");
//
对Field进行索引,但不对其进行分词
public
static final Index NO_NORMS =
new Index(
"
NO_NORMS
");
//
对Field进行索引,但是不使用Analyzer
三、 优化索引,关闭写入
writer.Optimize();
//
添加完所有document,我们对索引进行优化,优化主要是将多个索引文件合并到一个,有利于提高索引速度。
writer.Close();
//
随后将writer关闭,这点很重要。
B. 读出流程(即使用索引)
1. 创建一个容器来存放你从索引文件中读取到的数据,这里我们使用Table
private
DataTable dt()
{
DataTable mytab
=
new
DataTable();
mytab.Columns.Add(
"
ID
"
);
mytab.Columns.Add(
"
TRADENAME
"
);
mytab.Columns.Add(
"
AREANAME
"
);
mytab.Columns.Add(
"
COMPANYNAME
"
);
mytab.Columns.Add(
"
FHDES
"
);
mytab.Columns.Add(
"
PRODUCTNAME
"
);
mytab.Columns.Add(
"
PIC
"
);
mytab.Clear();
return
mytab;
}
2. 读取索引文件中的数据
private
IndexSearcher LuceneSource()
{
string
INDEX_STORE_PATH
=
"
D:/index/
"
;
//
INDEX_STORE_PATH 为索引存储目录
return
new
IndexSearcher(INDEX_STORE_PATH);
}
3. 这一步我们需要得到过滤后数据,即查询条件,你可以理解成SQL里的where条件
lucene的搜索相当强大,它提供了很多辅助查询类,每个类都继承自Query类,各自完成一种特殊的查询,你可以像搭积木一样将它们任意组合使用,完成一些复杂操作;另外lucene还提供了Sort类对结果进行排序,提供了Filter类对查询条件进行限制。你或许会不自觉地拿它跟SQL语句进行比较:“lucene能执行and、or、order by、where、like‘%xx%’操作吗?”回答是:“当然没问题!”
a. TermQuery
首先介绍最基本的查询,如果你想执行一个这样的查询:“在content域中包含‘lucene’的document”,那么你可以用TermQuery:
Term t
=
new
Term(
"
content
"
,
"
lucene
"
);Query query
=
new
TermQuery(t);
b. BooleanQuery
如果你想让产品名称或者产品发货说明匹配关键字,那么你可以用:
strkeyword = Common.ProductAbout.GetKeyWordsSplitBySpace(strkeyword,
new PanGuTokenizer());
QueryParser companynameparser =
new QueryParser(
"
companyname
",
new PanGuAnalyzer(
true));
Query companynamequery = companynameparser.Parse(strkeyword);
QueryParser productnameparser =
new QueryParser(
"
productname
",
new PanGuAnalyzer(
true));
Query productdesquery = productnameparser.Parse(strkeyword);
bq.Add(productdesquery, BooleanClause.Occur.SHOULD);
bq.Add(companynamequery, BooleanClause.Occur.SHOULD);
Tip:清单此处的BooleanClause.Occur,此类有2个重要的属性,SHOULD和MUST,SHOULD你就理解成SQL里’OR’,MUST理解成SQL里的’AND’,此处表示要同时满足productdesquery和idquery
c. WildcardQuery
如果你想对某单词进行通配符查询,你可以用WildcardQuery,通配符包括’?’匹配一个任意字符和’*’匹配零个或多个任意字符,例如你搜索’use*’,你可能找到’useful’或者’useless’:
Query query
=
new
WildcardQuery(
new
Term(
"
content
"
,
"
use*
"
);
d. PhraseQuery
你可能对中日关系比较感兴趣,想查找‘中’和‘日’挨得比较近(5个字的距离内)的文章,超过这个距离的不予考虑,你可以:
PhraseQuery query
=
new
PhraseQuery();
query.setSlop(
5
);query.add(
new
Term(
"
content
"
,
"
中
"
));
query.add(
new
Term(
"
content
"
,
"
日
"
));
那么它可能搜到“中日合作……”、“中方和日方……”,但是搜不到“中国某高层领导说日本欠扁”。
e. PrefixQuery
如果你想搜以‘中’开头的词语,你可以用PrefixQuery:
PrefixQuery query
=
new
PrefixQuery(
new
Term(
"
content
"
,
"
中
"
);
f. FuzzyQuery
FuzzyQuery用来搜索相似的term,使用Levenshtein算法。假设你想搜索跟‘wuzza’相似的词语,你可以:
Query query
=
new
FuzzyQuery(
new
Term(
"
content
"
,
"
wuzza
"
);
你可能得到‘fuzzy’和‘wuzzy’。
g. RangeQuery
另一个常用的Query是RangeQuery,你也许想搜索时间域从20060101到20060130之间的document,你可以用RangeQuery:
RangeQuery query
=
new
RangeQuery(
new
Term(“time”, “
20060101
”),
new
Term(“time”, “
20060130
”),
true
);
最后的true表示含边界。
4. 取得从索引文件中过滤后的数据
private
Hits LuceneFilteridSource(BooleanQuery bq)
{
IndexSearcher mysearch
=
LuceneSource();
Sort sort
=
new
Sort(
new
SortField(
"
ID
"
, SortField.DOC,
false
));
//
排序
return
mysearch.Search(bq, sort);
}
Sort是对数据进行排序,比如这里对ID
进行排序.
注意,LUCENE不支持关键词为空的情况,所以如果你想把索引文件中所有的数据都调用出来,那可以用如下方法
for
(
int
i
=
0
; i
<
mysearch.MaxDoc(); i
++
)
{
Document doc
=
mysearch.Doc(i);
FillingTable(mytab, doc);
}
5. 把过滤后的数据并扔入Table数据源
View Code
private
void
FillingTable(DataTable dt, Document doc)
{
DataRow myrow;
myrow
=
dt.NewRow();
myrow[
0
]
=
doc.Get(
"
id
"
).ToString();
myrow[
1
]
=
doc.Get(
"
tradename
"
).ToString();
myrow[
2
]
=
doc.Get(
"
areaname
"
).ToString();
myrow[
3
]
=
doc.Get(
"
companyname
"
).ToString();
myrow[
4
]
=
doc.Get(
"
fhdes
"
).ToString();
myrow[
5
]
=
doc.Get(
"
productname
"
).ToString();
myrow[
6
]
=
doc.Get(
"
pic
"
).ToString();
dt.Rows.Add(myrow);
myrow.AcceptChanges();
}
6. OK,得到了table数据源,你可以在此基础上任意使用这些数据了。