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();//条件1Query query = NumericRangeQuery.newLongRange("fileSize", 0L, 500L, true, true);//条件2Query 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对象}}