lucene

353 阅读9分钟
引入
  • 如果我们磁盘有一堆文本文件,我们需要搜索哪个文件里面有"java"这个关键字
    • 一般做法时遍历读取每个文件的内容,然后判断关键字,这样子效率很低
    • luncene可以帮我们解决这个问题

lucene
  • Lucene是Apache开源的全文检索的工具包【创建索引】【查询索引】
    • 外国人写的,对中文检索不友好

数据库搜索
  • 数据库之所以能够快速通过关键字查询是因为数据库存放的数据是结构化数据
数据分类A.结构化数据
  • 指具有固定格式或有限长度的数据.如数据库,元数据
B.非结构化数据
  • 指不定长或无固定格式的数据.如邮件,word文档等磁盘上的文件
非结构化数据查询方法1.顺序扫描法(Serial Scanning)
  • 一个一个文件打开看,然后找关键字,效率非常慢
2.全文检索(Full-text Search)
  • 根据内容实现抽取出关键字(索引),然后通过关键字定位到要找的文件
    • 如字典
  • 这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)
    • 创建索引的过程非常耗时,但是创建完成后是的搜索过程非常快

全文检索使用场景
  • 电子商务搜索,论坛搜索站内搜索
Lucene实现全文检索的流程
  • 先创建索引,再对索引进行搜索的过程————全文检索
一:创建索引库
  • 获取文档:通过流的形式读取文档
  • 构建文档对象:将文档内容封装成Document对象
  • 分析文档内容,创建索引库
  • 原始文档和索引都会放到索引库里
二:通过索引库搜索
  • 页面输入关键字
  • 创建查询对象
  • 执行搜索
1.创建文件对象
  • 索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field)域中存储内容
  • 这里我们可以将磁盘上的一个文件当成一个Document,Document中包括一些Field(file_name文件名称,file_path文件路径,file_size文件大小,file_content文件内容)
  • 每个文档都有一个唯一的编号,就是文档id
2.分析文档
  • 包含域(Field)的文档(document),需要再对域中的内容进行分析,分析的过程是经过对原始文档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个一个的单词
  • 每个单词叫做一个Term,不同的域中拆分出来的相同的单词是不同的term
3.创建索引
  • 对所有文档分析得出的语汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)
  • 注意:创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构
4.创建查询
  • 用户输入查询关键字执行搜索之前需要先构建一个查询对象,查询对象中可以指定查询要搜索的Field文档域、查询关键字等,查询对象会生成具体的查询语法
配置开发环境lucene下载
  • 官方网站:
    http://lucene.apache.org/
  • Jdk要求:1.7以上
使用jar包
  • lucene包
  • lucene-core-4.10.3.jar # 核心包
  • lucene-analyzers-common-4.10.3.jar # 分词包
  • lucene-queryparser-4.10.3.jar # 搜索包
  • 其他包
  • commons-io-2.4.jar
  • junit-4.9.jar
创建索引库
  • 创建一个java工程,并导入jar包.
  • 创建一个indexwriter对象.
    • 指定索引库的存放位置Directory对象
    • 指定一个分析器,对文档内容进行分析.
  • 创建document对象.
  • 创建field对象,将field添加到document对象中.
  • 使用indexwriter对象将document对象写入索引库,此过程进行索引创建.并将索引和document对象写入索引库.
  • 关闭IndexWriter对象
Field域实现类

Field域实现类Field类数据类型是否分析是否索引是否存储说明
StringField字符串NYY或Neg:订单号
LongFieldLongYYY或Neg:价格
StoredField重载方法,支持多类型NNYeg:文件地址
TextField字符串或流YYY或N内容
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public void createIndex() throws Exception {
"创建IndexWriter"
Directory directory = FSDirectory.open(new File("C:\\Users\\82545\\Desktop\\index"));
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
IndexWriter indexWriter = new IndexWriter(directory, config);
"创建Document & Field"
File dir = new File("C:\\Users\\82545\\Desktop\\j2ee\\searchsourceFileDir");
File[] files = dir.listFiles();
for (File file : files) {
String fileName = file.getName();
long fileSize = file.length();
String fileContent = FileUtils.readFileToString(file);
String filePath = file.getPath();
"-----------------"
Field file_name = new TextField("file_name", fileName, Store.YES);
Field file_content = new TextField("file_content", fileContent, Store.YES);
Field file_path = new StoredField("file_path", filePath);
Field file_size = new LongField("file_size", fileSize, Store.YES);
"-----------------"
Document document = new Document();
document.add(file_name);
document.add(file_content);
document.add(file_path);
document.add(file_size);
"创建索引,并写入索引库"
indexWriter.addDocument(document);
}
indexWriter.close();
}

  • 运行完成后会创建很多文件,我们打不开,可以使用工具查看
    • luke这个工具可以打开

查询索引
  • 创建一个Directory对象,也就是索引库存放的位置.
  • 创建一个indexReader对象,需要指定Directory对象.
  • 创建一个indexsearcher对象,需要指定IndexReader对象
  • 创建一个TermQuery对象,指定查询的域和查询的关键词.
  • 执行查询.
  • 返回查询结果.遍历查询结果并输出.
  • 关闭IndexReader对象
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public void search() throws Exception {
"创建IndexReader & IndexSearcher"
Directory directory = FSDirectory.open(new File("C:\\Users\\82545\\Desktop\\index"));
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
Query query = new TermQuery(new Term("file_name", "apache"));
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("查询结果的总条数:"+ topDocs.totalHits);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println(document.get("file_name"));
//System.out.println(document.get("file_content"));
System.out.println(document.get("file_path"));
System.out.println(document.get("file_size"));
}
indexReader.close();
}


支持中文分析器(Analyzer)
  • 自带的分词器不好用,对中文格式不友好,同时文件名后缀没处理
  • 使用第三方的 : IK-analyzer
使用方法
  • 把jar包添加到工程中(IKAnalyzer2012FF_ul.jar)
  • 把配置文件和扩展词典和停用词词典添加到classpath下
    • ext_stopword.dic
    • IKAnalyzer.cfg.xml

  • 注意:mydict.dic和ext_stopword.dic文件的格式为UTF-8,注意是无BOM的UTF-8编码
  • 注意:搜索使用的分析器要和索引使用的分析器一致
索引库的维护索引库的添加
  • 向索引库中添加document对象
    • 先创建一个indexwriter对象
    • 创建一个document对象
    • 把document对象写入索引库
    • 关闭indexwriter

[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
public void addDocument() throws Exception {
Directory directory = FSDirectory.open(new File("D:\\temp\\0108\\index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, config);
Document document = new Document();
document.add(new TextField("filename", "新添加的文档", Store.YES));
document.add(new TextField("content", "新添加的文档的内容", Store.NO));
document.add(new TextField("content", "新添加的文档的内容第二个content", Store.YES));
document.add(new TextField("content1", "新添加的文档的内容要能看到", Store.YES));
indexWriter.addDocument(document);
indexWriter.close();
}


索引库删除(全部删除)

[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public void deleteAllIndex() throws Exception {
Directory directory = FSDirectory.open(new File("D:\\temp\\0108\\index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, config);
indexWriter.deleteAll();
indexWriter.close ();
}
public void deleteAllIndex() throws Exception {
Directory directory = FSDirectory.open(new File("D:\\temp\\0108\\index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, config);
Query query = new TermQuery(new Term("fileName","apache"));
indexWriter.deleteDocuments(query)
indexWriter.close();
}




索引库删除(指定查询条件删除)
  • 先删掉一个,再添加一个,就是修改
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
public void updateIndex() throws Exception {
IndexWriter indexWriter = getIndexWriter();
Document document = new Document();
document.add(new TextField("filename", "要更新的文档", Store.YES));
String text = "2013年11月18日 - Lucene 简介 Lucene 是一个基于 Java的全文信息检索工具包,";
text += "它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能.";
document.add(new TextField("content", text, Store.YES));
indexWriter.updateDocument(new Term("content", "java"), document);
indexWriter.close();
}

ucene索引库查询(2种方式)(重点)
  • 两种查询方式,一个按对象查,一个按语法查,得到的结果都是一样的
  • 对要搜索的信息创建Query查询对象,Lucene会根据Query对象生成查询语法,类似关系数据库Sql语法一样lucene自己的语法
一: 使用query的子类查询
  • MatchAllDocsQuery(查询所有)
    • 使用MatchAllDocsQuery查询索引目录中的所有文档

[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public void testMatchAllDocsQuery() throws Exception {
Directory directory = FSDirectory.open(new File("C:\\Users\\82545\\Desktop\\index"));
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
Query query = new MatchAllDocsQuery();
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("查询结果的总条数:"+ topDocs.totalHits);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println(document.get("file_name"));
//System.out.println(document.get("file_content"));
System.out.println(document.get("file_path"));
System.out.println(document.get("file_size"));
}
indexReader.close();
}


TermQuery(精准查询)
  • TermQuery,通过项查询,TermQuery不使用分析器所以建议匹配不分词的Field域查询,比如订单号、分类ID号等
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
public void testTermQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
Query query = new TermQuery(new Term("file_content", "lucene"));
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("查询结果总数量:" + topDocs.totalHits);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = indexSearcher.doc(scoreDoc.doc);
System.out.println(document.get("file_name"));
//System.out.println(document.get("file_content"));
System.out.println(document.get("file_path"));
System.out.println(document.get("file_size"));
}
indexSearcher.getIndexReader().close();
}


NumericRangeQuery(按区间查)
  • 可以根据数值范围查询
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
public void testNumericRangeQuery() throws Exception {
Directory directory = FSDirectory.open(new File("C:\\Users\\82545\\Desktop\\index"));
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
Query query = NumericRangeQuery.newLongRange("size", 1l, 1000l, true, true);
printResult(query, indexSearcher);
}


BooleanQuery(组合查询)
  • 可以组合查询条件
[Java]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
public void testBooleanQuery() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
BooleanQuery query = new BooleanQuery();
Query query1 = new TermQuery(new Term("filename", "apache"));
Query query2 = new TermQuery(new Term("content", "apache"));
query.add(query1, Occur.MUST);//and
query.add(query2, Occur.MUST);//and
printResult(query, indexSearcher);
}


二: 使用queryparser查询(用到分析器)
  • 需要加入queryParser依赖的jar包
[Java]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
9
public void testQueryParser() throws Exception {
IndexSearcher indexSearcher = getIndexSearcher();
QueryParser queryParser = new QueryParser("content", new IKAnalyzer());//默认的域,分词器
//Query query = queryParser.parse("fileName:apache");
Query query = queryParser.parse("Lucene是java开发的");
printResult(query, indexSearcher);
}



更多技术资讯可关注:gzitcast