lucene

全文检索


Lucene实现全文检索的流程

    创建索引

    查询索引

配置开发环境

    创建索引库

    查询索引库

分析器的分析过程

    测试分析器的分词效果

    第三方中文分析器

索引库的维护

    添加文档

    删除文档

    修改文档

Lucene的高级查询Lucene的查询

    使用Query的子类查询

        MatchAllDocsQuery

        TermQuery

        NumericRangeQuery

        BooleanQuery

    使用QueryParser

        QueryParser

        MulitFieldQueryParser


MAVEN 导入 jar包



org.apache.lucene
lucene-core
5.3.1



org.apache.lucene
lucene-analyzers-common
5.3.1



org.apache.lucene
lucene-analyzers-smartcn
5.3.1



org.apache.lucene
lucene-queryparser
5.3.1



org.apache.lucene
lucene-highlighter
5.3.1



commons-io
commons-io
2.4


    创建索引库

使用indexwriter对象创建索引

    创建一个java工程,并导入jar包。

    创建一个indexwriter对象。

    指定索引库的存放位置Directory对象

    指定一个分析器,对文档内容进行分析。

    创建document对象。

    创建field对象,将field添加到document对象中。

    使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。

    关闭IndexWriter对象。

package com.stevezong.lucene;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
public class LuceneTest {
public static void main(String[] args) throws Exception {
//指定索引库的存放位置Directory对象
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
//指定一个分析器,对文档内容进行分析。
Analyzer analyzer = new StandardAnalyzer();
// 配置对象
IndexWriterConfig indexWriterConfig =  new IndexWriterConfig(analyzer);
//创建一个indexwriter对象。
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//创建field对象,将field添加到document对象中。
File file = new File("F:\\5DWIFI最新字典\\新字典");
File[] files =file.listFiles();
for (File subFile : files) {
//文件名
String fileName = subFile.getName();
//文件大小
long fileSize = FileUtils.sizeOf(subFile);
//文件路径
String filePath = subFile.getAbsolutePath();
//文件内容
String fileContent = FileUtils.readFileToString(subFile);
//创建文件名域
//第一个参数:域的名称
//第二个参数:域的内容
//第三个参数:是否存储
//文件名域
Field fileNameField = new TextField("fileName", fileName, Store.YES);
//文件内容域
Field fileContentField = new TextField("fileContent", fileContent, Store.YES);
//文件路径域
Field filePathField = new StoredField("filePath", filePath);
//文件大小域
Field fileSizeField = new LongField("fileSize", fileSize, Store.YES);
//1.3创建document对象。
Document document = new Document();
document.add(fileSizeField);
document.add(filePathField);
document.add(fileContentField);
document.add(fileNameField);
//使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。
indexWriter.addDocument(document);
}
indexWriter.close();
//关闭IndexWriter对象。
}
}


Field类 (4类)                                                                      

=======================================================================================================================================================

StringField(FieldName, FieldValue,Store.YES))

数据类型:字符串

Analyzed是否分析:N

Indexed 是否索引:Y

Stored 是否存储:Y或N

说明:这个Field用来构建一个字符串Field,但是不会进行分析,会将整个串存储在索引中,比如(订单号,姓名等)是否存储在文档中用Store.YES或Store.NO决定

*********************************************************************************************************************************************************

LongField(FieldName, FieldValue,Store.YES)    

数据类型:Long型

Analyzed是否分析:Y

Indexed 是否索引:Y

Stored 是否存储:Y或N

说明:这个Field用来构建一个Long数字型Field,进行分析和索引,比如(价格)是否存储在文档中用Store.YES或Store.NO决定

*********************************************************************************************************************************************************

StoredField(FieldName, FieldValue)

数据类型:重载方法,支持多种类型

Analyzed是否分析:N

Indexed 是否索引:N

Stored 是否存储:Y

说明:这个Field用来构建不同类型Field不分析,不索引,但要Field存储在文档中

*********************************************************************************************************************************************************

TextField(FieldName, FieldValue, Store.NO)或TextField(FieldName, reader)

数据类型:字符串或流

Analyzed是否分析:Y

Indexed 是否索引:Y

Stored 是否存储:Y或N

说明:如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略.

=======================================================================================================================================================

使用 对应版本的luke 就可读取

https://github.com/DmitryKey/luke/releases?after=luke-5.2.0


查询索引库

1:创建一个Directory对象,也就是索引库存放的位置。

2:创建一个indexReader对象,需要指定Directory对象。

3:创建一个indexsearcher对象,需要指定IndexReader对象

4:创建一个TermQuery对象,指定查询的域和查询的关键词。

5:执行查询。

6:回查询结果。遍历查询结果并输出。

7:关闭IndexReader对象



package com.stevezong.lucene;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
public class LuceneTest2 {
public static void main(String[] args) throws Exception {
//1:创建一个Directory对象,也就是索引库存放的位置。
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
//2:创建一个indexReader对象,需要指定Directory对象。
IndexReader indexReader = DirectoryReader.open(directory);
//3:创建一个indexsearcher对象,需要指定IndexReader对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//4:创建一个TermQuery对象,指定查询的域和查询的关键词。
Term term = new Term("fileName","2015");
Query query = new TermQuery(term);
//5:执行查询。
TopDocs topDocs = indexSearcher.search(query, 2);
//6:回查询结果。遍历查询结果并输出。
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;
Document document = indexSearcher.doc(doc);
String fileName = document.get("fileName");
String fileContent = document.get("fileContent");
String filePath = document.get("filePath");
String fileSize = document.get("fileSize");
System.out.println(fileName+":"+filePath+":"+fileSize+":"+fileContent);
}
indexReader.close();
//7:关闭IndexReader对象
}
}


indexSearcher.search(query, n)根据Query搜索,返回评分最高的n条记录

indexSearcher.search(query, filter, n)根据Query搜索,添加过滤策略,返回评分最高的n条记录

indexSearcher.search(query, n, sort)根据Query搜索,添加排序策略,返回评分最高的n条记录

indexSearcher.search(booleanQuery, filter, n, sort)根据Query搜索,添加过滤策略,添加排序策略,返回评分最高的n条记录

Lucene自带中文分词器

StandardAnalyzer:

单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”,

效果:“我”、“爱”、“中”、“国”。

CJKAnalyzer

二分法分词:按两个字进行切分。如:“我是中国人”,效果:“我是”、“是中”、“中国”“国人”。

上边两个分词器无法满足需求。

SmartChineseAnalyzer

对中文支持较好,但扩展性差,扩展词库,禁用词库和同义词库等不好处理


package com.stevezong.lucene;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;
public class LuceneTest3 {
    @Test
    // 全删
    public void testDel() throws Exception {
        // 1.2.1)指定索引库的存放位置Directory对象
        Path path = Paths.get("d:\\szTemp");
        Directory directory = FSDirectory.open(path);
        // 1.2.2)指定一个分析器,对文档内容进行分析。
        Analyzer analyzer = new StandardAnalyzer();
        // 1.2.3) 配置对象
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        // 1.2创建一个indexwriter对象。
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
        // 全删
        indexWriter.deleteAll();
        indexWriter.close();
    }
@Test
// 条件删
public void testDelBySome() throws Exception {
// 1.2.1)指定索引库的存放位置Directory对象
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
// 1.2.2)指定一个分析器,对文档内容进行分析。
Analyzer analyzer = new StandardAnalyzer();
// 1.2.3) 配置对象
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
// 1.2创建一个indexwriter对象。
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
// 创建删除条件
Term term = new Term("fileName", "2015");
Query query = new TermQuery(term);
// 条件删
indexWriter.deleteDocuments(query);
indexWriter.close();
}
@Test
// 修改
public void testUpdate() throws Exception {
// 1.2.1)指定索引库的存放位置Directory对象
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
// 1.2.2)指定一个分析器,对文档内容进行分析。
Analyzer analyzer = new StandardAnalyzer();
// 1.2.3) 配置对象
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
// 1.2创建一个indexwriter对象。
Document doc = new Document();
TextField fileNameField = new TextField("fileName", "测试修改功能文件名", Store.YES);
TextField fileContentField = new TextField("fileContent", "测试修改功能文件内容", Store.YES);
doc.add(fileNameField);
doc.add(fileContentField);
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
indexWriter.updateDocument(new Term("fileName", "50"), doc);
indexWriter.close();
}
@Test
// 查询所有
public void testSelectAll() throws Exception {
// 1:创建一个Directory对象,也就是索引库存放的位置。
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
// 2:创建一个indexReader对象,需要指定Directory对象。
IndexReader indexReader = DirectoryReader.open(directory);
// 3:创建一个indexsearcher对象,需要指定IndexReader对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//实现类换了
Query query = new MatchAllDocsQuery();
TopDocs docs = indexSearcher.search(query, 20);
ScoreDoc[] scoreDocs = docs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;
Document document = indexSearcher.doc(doc);
String fileName = document.get("fileName");
String fileContent = document.get("fileContent");
String filePath = document.get("filePath");
String fileSize = document.get("fileSize");
System.out.println(fileName+":"+filePath+":"+fileSize);
}
indexReader.close();
//7:关闭IndexReader对象
}
@Test
// 查询 根据数值范围查询
public void testSelectByFileSize() throws Exception {
// 1:创建一个Directory对象,也就是索引库存放的位置。
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
// 2:创建一个indexReader对象,需要指定Directory对象。
IndexReader indexReader = DirectoryReader.open(directory);
// 3:创建一个indexsearcher对象,需要指定IndexReader对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//实现类换了
Query query = NumericRangeQuery.newLongRange("fileSize", 0L, 500L, true, true);
TopDocs docs = indexSearcher.search(query, 20);
ScoreDoc[] scoreDocs = docs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;
Document document = indexSearcher.doc(doc);
String fileName = document.get("fileName");
String fileContent = document.get("fileContent");
String filePath = document.get("filePath");
String fileSize = document.get("fileSize");
System.out.println(fileName+":"+filePath+":"+fileSize);
}
indexReader.close();
//7:关闭IndexReader对象
}
@Test
// 查询 组合查询
public void testSelectBooleanQuery() throws Exception {
// 1:创建一个Directory对象,也就是索引库存放的位置。
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
// 2:创建一个indexReader对象,需要指定Directory对象。
IndexReader indexReader = DirectoryReader.open(directory);
// 3:创建一个indexsearcher对象,需要指定IndexReader对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//实现类换了
BooleanQuery booleanQuery =new BooleanQuery();
//条件1
Query query = NumericRangeQuery.newLongRange("fileSize", 0L, 500L, true, true);
//条件2
Query fileNameQuery = new TermQuery( new Term("fileName","2015"));
//将 两个条件 添加到 对象中 并设定 连接条件 必须 不必须 可能 过滤
booleanQuery.add(query,Occur.MUST);
booleanQuery.add(fileNameQuery,Occur.SHOULD);
TopDocs docs = indexSearcher.search(booleanQuery, 20);
ScoreDoc[] scoreDocs = docs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;
Document document = indexSearcher.doc(doc);
String fileName = document.get("fileName");
String fileContent = document.get("fileContent");
String filePath = document.get("filePath");
String fileSize = document.get("fileSize");
System.out.println(fileName+":"+filePath+":"+fileSize);
}
indexReader.close();
//7:关闭IndexReader对象
}
@Test
//条件解析的对象查询
//需要导包
public void testQueryParser() throws Exception {
// 1:创建一个Directory对象,也就是索引库存放的位置。
Path path = Paths.get("d:\\szTemp");
Directory directory = FSDirectory.open(path);
// 2:创建一个indexReader对象,需要指定Directory对象。
IndexReader indexReader = DirectoryReader.open(directory);
// 3:创建一个indexsearcher对象,需要指定IndexReader对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//实现类换了默认域分词器
QueryParser queryParser =new QueryParser("fileName",new StandardAnalyzer());
//查询所有 域:值
//Query query = queryParser.parse("*:*");
//fileName:2015
//Query query = queryParser.parse("2015");
//fileContent:cheese
//Query query = queryParser.parse("fileContent:cheese");
// cheese is a java 88888888 经过分词器 后再去查找 
Query query = queryParser.parse("fileContent:cheese is a java 88888888");
TopDocs docs = indexSearcher.search(query, 20);
ScoreDoc[] scoreDocs = docs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;
Document document = indexSearcher.doc(doc);
String fileName = document.get("fileName");
String fileContent = document.get("fileContent");
String filePath = document.get("filePath");
String fileSize = document.get("fileSize");
System.out.println(fileName+":"+filePath+":"+fileSize);
}
indexReader.close();
//7:关闭IndexReader对象
}
}