目录

Lucene

全文检索

全文检索的应用场景

索引

Lucene、Solr、Elasticsearch

三者关系

Lucene的基本使用

lucene工作流程

构建索引

查询索引

创建索引的详细流程

案例演示

生成索引:

中文分词索引

高亮显示

文档域加权

概述:

案例演示

查询索引的详细流程

索引的删除修改

删除索引

deleteDocBeforeMerge 此方法只标记未删除索引

修改索引

高级查询

特定项查询

综合案例演示

查询

删除

修改

添加


Lucene

  • Lucene是一套用于全文检索和搜寻的开源程序库,由Apache软件基金会支持和提供
  • Lucene提供了一个简单却强大的应用程序接口(API),能够做全文索引和搜寻,在Java开发环境里Lucene是一个成熟的免费开放源代码工具
  • Lucene并不是现成的搜索引擎产品,但可以用来制作搜索引擎产品

官网:http://lucene.apache.org

全文检索

全文检索大体分两个过程,索引创建 (Indexing)搜索索引 (Search)

  • 索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
  • 搜索索引:就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程

全文检索的应用场景

  • 搜索引擎

    • 百度、谷歌、搜索等等
  • 站内搜索
    • 论坛搜索,微博搜索,文章搜索
  • 电商搜索
    • jd,tb搜索

索引

索引一个为了提高查询速度,创建某种数据结构的集合

1、数据的分类:

1、结构化数据:格式、长度、数据类型固定

例如数据库的数据,

2、非结构化数据:格式、长度、数据类型不固定
                  word文档、pdf文档等等
2、数据的查询
        1、结构化数据的查询:sql语句
        简单、查询速度快
        2、非结构化数据:查询某个关键字
                条件复杂,查询难度大
                顺序扫描法
                字符串匹配(顺序扫描)
                使非结构化的数据变为结构化的数据便于查询

Lucene、Solr、Elasticsearch

  • 公司中使用的搜索技术是solr和Elasticsearch,而在这里介绍lucene是因为其简单对于新手来说入门容易
  • solr 分词索引的数据库(有服务器的概念) 分词索引,能够支撑大数据量的索引 ​
  • Elasticsearch分词索引的数据库(有服务器的概念) 分词索引,能够支撑更大数据量的索引 ​ 能够搭建ES集群

三者关系

Lucene:底层的API,工具包

Solr/Elasticsearch:基于Lucene开发的企业级的搜索引擎产品

Lucene的基本使用

使用Lucene的API来实现对索引的增(创建索引)、删(删除索引)、改(修改索引)、查(搜索数据)

lucene工作流程

构建索引

把数据库中的数据用IndexWriter(索引写出器类)生成Document对象,存到指定的文件夹或硬盘下,生成的文件就是Lucene文件

查询索引

先获取IndexSearcher对象,从索引文件夹快速检索出想要查询的数据===》查询索引

最终读到去TopDocs中

注:

  • IndexSearcher 借助于Query对象和IndexReader(索引读取类)对象
  • Query(查询对象,包含要查询的关键词信息) 借助于QueryParser查询解析器对象
  • QueryParser查询解析对象又要借助于Analyzer解析器
  • Analyzer解析器默认只能解析英文需要导入jar包才能解析中文

创建索引的详细流程

1、文档Document:数据库中一条具体的记录(在这里我使用的是读取本地的text文件,实际中使用的是数据库中查询出的数据添加到Document对象中)

 2、字段Field:数据库中的每个字段

一个Document中可以有很多个不同的字段,每一个字段都是一个Field类的对象。

  • DoubleField、FloatField、IntField、LongField、StringField这些子类一定会被创建索引,但是不会被分词,而且不一定会被存储到文档列表。要通过构造函数中的参数Store来指定:如果Store.YES代表存储,Store.NO代表不存储
  • TextField即创建索引,又会被分词。StringField会创建索引,但是不会被分词,如果不分词,会造成整个字段作为一个词条,除非用户完全匹配,否则搜索不到

3、目录对象Directory:指定索引要存储的位置

索引存储方式有两种

1、FSDirectory:文件系统目录,会把索引库指向本地磁盘。

2、RAMDirectory:内存目录,会把索引库保存在内存

区别:

FSDirectory: 速度略慢,但是比较安全
RAMDirectory:速度快,但是不安全

4、Analyzer(分词器类):提供分词算法,可以把文档中的数据按照算法分词

在这里使用的是StandardAnalyzer分词器,默认英文分词,不可以中文分词,需要引入中文分词依赖,才可以进行中文分词,一般我们用IK分词器

案例演示

在以下案例演示中,确保索引文件在测试之前删掉上一次生成的索引文件

主要代码:

public static void main(String[] args) {
//      索引文件将要存放的位置String indexDir = "D:\\software\\mysql\\data";
//      数据源地址String dataDir = "D:\\software\\mysql\\data\\d";IndexCreate ic = null;try {ic = new IndexCreate(indexDir);long start = System.currentTimeMillis();int num = ic.index(dataDir);long end = System.currentTimeMillis();System.out.println("检索指定路径下"+num+"个文件,一共花费了"+(end-start)+"毫秒");} catch (Exception e) {e.printStackTrace();}finally {try {ic.closeIndexWriter();} catch (Exception e) {e.printStackTrace();}}}

封装方法:

/*** 1、构造方法 实例化IndexWriter* @param indexDir* @throws Exception*/public IndexCreate(String indexDir) throws Exception{
//      获取索引文件的存放地址对象FSDirectory dir = FSDirectory.open(Paths.get(indexDir));
//      标准分词器(针对英文)Analyzer analyzer = new StandardAnalyzer();
//      索引输出流配置对象IndexWriterConfig conf = new IndexWriterConfig(analyzer); indexWriter = new IndexWriter(dir, conf);}/*** 2、关闭索引输出流* @throws Exception*/public void closeIndexWriter()  throws Exception{indexWriter.close();}/*** 3、索引指定路径下的所有文件* @param dataDir* @return* @throws Exception*/public int index(String dataDir) throws Exception{File[] files = new File(dataDir).listFiles();//检索文件夹中源文件,并已生成Document对象存储到索引文件中for (File file : files) {indexFile(file);}return indexWriter.numDocs();}/*** 4、索引指定的文件* @param file* @throws Exception*/private void indexFile(File file) throws Exception{System.out.println("被索引文件的全路径:"+file.getCanonicalPath());Document doc = getDocument(file);indexWriter.addDocument(doc);}/*** 5、获取文档(索引文件中包含的重要信息,key-value的形式)* @param file* @return* @throws Exception*/private Document getDocument(File file) throws Exception{Document doc = new Document();doc.add(new TextField("contents", new FileReader(file)));
//      Field.  Store.YES是否存储到硬盘doc.add(new TextField("fullPath", file.getCanonicalPath(),Field.Store.YES));doc.add(new TextField("fileName", file.getName(),Field.Store.YES));return doc;}

生成索引:

元数据文件:(注:此案例使用以下文件,实际中使用的是数据库查询的数据)

索引文件:

使用工具查看索引文件 luke-5.3.0-luke-release

中文分词索引

以上是默认的英文分词,也只能英文分词,不支持中文分词。实际中我们不可能只对英文分词,这样我们需要引入第三方的分词器。

在这里我使用的是SmartChineseAnalyzer 中文分词器(org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer.SmartChineseAnalyzer())

 private Integer ids[] = { 1, 2, 3 };private String citys[] = { "青岛", "南京", "上海" };// private String descs[]={// "青岛是个美丽的城市。",// "南京是个有文化的城市。",// "上海市个繁华的城市。"// };private String descs[] = { "青岛是个美丽的城市。","南京是一个文化的城市南京,简称宁,是江苏省会,地处中国东部地区,长江下游,濒江近海。全市下辖11个区,总面积6597平方公里,2013年建成区面积752.83平方公里,常住人口818.78万,其中城镇人口659.1万人。[1-4] “江南佳丽地,金陵帝王州”,南京拥有着6000多年文明史、近2600年建城史和近500年的建都史,是中国四大古都之一,有“六朝古都”、“十朝都会”之称,是中华文明的重要发祥地,历史上曾数次庇佑华夏之正朔,长期是中国南方的政治、经济、文化中心,拥有厚重的文化底蕴和丰富的历史遗存。[5-7] 南京是国家重要的科教中心,自古以来就是一座崇文重教的城市,有“天下文枢”、“东南第一学”的美誉。截至2013年,南京有高等院校75所,其中211高校8所,仅次于北京上海;国家重点实验室25所、国家重点学科169个、两院院士83人,均居中国第三。[8-10]","上海市个繁华的城市。" };private FSDirectory dir;/*** 每次都生成索引文件* * @throws Exception*/@Beforepublic void setUp() throws Exception {dir = FSDirectory.open(Paths.get("D:\\software\\mysql\\data"));IndexWriter indexWriter = getIndexWriter();for (int i = 0; i < ids.length; i++) {Document doc = new Document();doc.add(new IntField("id", ids[i], Field.Store.YES));doc.add(new StringField("city", citys[i], Field.Store.YES));doc.add(new TextField("desc", descs[i], Field.Store.YES));indexWriter.addDocument(doc);}indexWriter.close();}/*** 获取索引输出流* * @return* @throws Exception*/private IndexWriter getIndexWriter() throws Exception {
//      Analyzer analyzer = new StandardAnalyzer();Analyzer analyzer = new SmartChineseAnalyzer();IndexWriterConfig conf = new IndexWriterConfig(analyzer);return new IndexWriter(dir, conf);}/*** luke查看索引生成* * @throws Exception*/@Testpublic void testIndexCreate() throws Exception {}/*** 测试高亮* * @throws Exception*/@Testpublic void testHeight() throws Exception {IndexReader reader = DirectoryReader.open(dir);IndexSearcher searcher = new IndexSearcher(reader);SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer();QueryParser parser = new QueryParser("desc", analyzer);// Query query = parser.parse("南京文化");Query query = parser.parse("南京文明");TopDocs hits = searcher.search(query, 100);// 查询得分项QueryScorer queryScorer = new QueryScorer(query);// 得分项对应的内容片段SimpleSpanFragmenter fragmenter = new SimpleSpanFragmenter(queryScorer);// 高亮显示的样式SimpleHTMLFormatter htmlFormatter = new SimpleHTMLFormatter("<span color='red'><b>", "</b></span>");// 高亮显示对象Highlighter highlighter = new Highlighter(htmlFormatter, queryScorer);// 设置需要高亮显示对应的内容片段highlighter.setTextFragmenter(fragmenter);for (ScoreDoc scoreDoc : hits.scoreDocs) {Document doc = searcher.doc(scoreDoc.doc);String desc = doc.get("desc");if (desc != null) {// tokenstream是从doucment的域(field)中抽取的一个个分词而组成的一个数据流,用于分词。TokenStream tokenStream = analyzer.tokenStream("desc", new StringReader(desc));System.out.println("高亮显示的片段:" + highlighter.getBestFragment(tokenStream, desc));}System.out.println("所有内容:" + desc);}}

未使用之前

 使用之后

这就是中文分词器的作用,不仅仅只对中文分词,还可以进行英文分词

高亮显示

在中文分词下,有出现了一个高亮显示的一个概念。

高亮显示就是给所有关键字加上一个HTML标签

上图,搜索java入门基础知识 ,把查询出来的所有数据中的java入门设置了一个样式,这就是高亮显示

查询结果:

高亮显示的片段:城镇人口659.1万人。[1-4] “江南佳丽地,金陵帝王州”,<span color='red'><b>南京</b></span>拥有着6000多年<span color='red'><b>文明</b></span>史、近2600年建城史和近500年的建都史,是中国四大古都之一,有“六朝古都”、“十朝都会”之称,是中华<span color='red'><b>文明</b></span>的
所有内容:南京是一个文化的城市南京,简称宁,是江苏省会,地处中国东部地区,长江下游,濒江近海。全市下辖11个区,总面积6597平方公里,2013年建成区面积752.83平方公里,常住人口818.78万,其中城镇人口659.1万人。[1-4] “江南佳丽地,金陵帝王州”,南京拥有着6000多年文明史、近2600年建城史和近500年的建都史,是中国四大古都之一,有“六朝古都”、“十朝都会”之称,是中华文明的重要发祥地,历史上曾数次庇佑华夏之正朔,长期是中国南方的政治、经济、文化中心,拥有厚重的文化底蕴和丰富的历史遗存。[5-7] 南京是国家重要的科教中心,自古以来就是一座崇文重教的城市,有“天下文枢”、“东南第一学”的美誉。截至2013年,南京有高等院校75所,其中211高校8所,仅次于北京上海;国家重点实验室25所、国家重点学科169个、两院院士83人,均居中国第三。[8-10]

文档域加权

概述:

但是在这里大家有没有发现我查询的数据,前几条都是广告,那是为什么这些广告会排在前面让我们第一眼就看到这些广告呢?

大家肯定说是因为广告投的钱多,所以排在前面。

在这里,说的是没错,但是在lucene中,把这种技术叫做文档域加权

案例演示

private String ids[]={"1","2","3","4"};private String authors[]={"Jack","Marry","John","Json"};private String positions[]={"accounting","technician","salesperson","boss"};private String titles[]={"Java is a good language.","Java is a cross platform language","Java powerful","You should learn java"};private String contents[]={"If possible, use the same JRE major version at both index and search time.","When upgrading to a different JRE major version, consider re-indexing. ","Different JRE major versions may implement different versions of Unicode,","For example: with Java 1.4, `LetterTokenizer` will split around the character U+02C6,"};private Directory dir;//索引文件目录@Beforepublic void setUp()throws Exception {dir = FSDirectory.open(Paths.get("D:\\software\\mysql\\data"));IndexWriter writer = getIndexWriter();for (int i = 0; i < authors.length; i++) {Document doc = new Document();doc.add(new StringField("id", ids[i], Field.Store.YES));doc.add(new StringField("author", authors[i], Field.Store.YES));doc.add(new StringField("position", positions[i], Field.Store.YES));TextField textField = new TextField("title", titles[i], Field.Store.YES);//           Json投钱做广告,把排名刷到第一了if("boss".equals(positions[i])) {textField.setBoost(2f);//设置权重,默认为1}doc.add(textField);
//          TextField会分词,StringField不会分词doc.add(new TextField("content", contents[i], Field.Store.NO));writer.addDocument(doc);}writer.close();}private IndexWriter getIndexWriter() throws Exception{Analyzer analyzer = new StandardAnalyzer();IndexWriterConfig conf = new IndexWriterConfig(analyzer);return new IndexWriter(dir, conf);}@Testpublic void index() throws Exception{IndexReader reader = DirectoryReader.open(dir);IndexSearcher searcher = new IndexSearcher(reader);String fieldName = "title";String keyWord = "java";Term t = new Term(fieldName, keyWord);Query query = new TermQuery(t);TopDocs hits = searcher.search(query, 10);System.out.println("关键字:‘"+keyWord+"’命中了"+hits.totalHits+"次");for (ScoreDoc scoreDoc : hits.scoreDocs) {Document doc = searcher.doc(scoreDoc.doc);System.out.println(doc.get("author"));}}

if("boss".equals(positions[i])) {

//加权的代码
                textField.setBoost(2f);//设置权重,默认为1
}

在我们未设置加权时,数据排行是怎么排的呢?

lucene会根据关键字出现的顺序以及次数进行排序

代码中数据前两条title里java是开头的,而最后一个是排在最后的

在未给第四条数据

加权前

加权后

当多条数据都进行加权,这时候比较的是加权的权重

查询索引的详细流程

步骤:

  1. 创建一个Directory对象,也就是索引库存放的位置。
  2. 创建一个indexReader对象,需要指定Directory对象。
  3. 创建一个indexsearcher对象,需要指定IndexReader对象
  4. 创建一个TermQuery对象,指定查询的域和查询的关键词。
  5. 执行查询。
  6. 返回查询结果。遍历查询结果并输出。
  7. 关闭IndexReader对象

IndexSearcher搜索方法

  • 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搜索结果可通过TopDocs遍历,TopDocs类提供了少量的属性,如下:

  • totalHits : 匹配搜索条件的总记录数
  • scoreDocs  : 顶部匹配记录

注: 

  • Search方法需要指定匹配记录数量n:indexSearcher.search(query, n)
  • TopDocs.totalHits:是匹配索引库中所有记录的数量
  • TopDocs.scoreDocs:匹配相关度高的前边记录数组,scoreDocs的长度小于等于search方法指定的参数n

案例演示:

这里的索引文件是基于上面创建索引的数据进行查询的

执行代码:

public static void main(String[] args) {String indexDir = "D:\\software\\mysql\\data";String q = "EarlyTerminating-Collector";try {IndexUse.search(indexDir, q);} catch (Exception e) {e.printStackTrace();}}

主要代码:

/*** 通过关键字在索引目录中查询* @param indexDir 索引文件所在目录* @param q 关键字*/public static void search(String indexDir, String q) throws Exception{//索引文件存储地址FSDirectory indexDirectory = FSDirectory.open(Paths.get(indexDir));
//      注意:索引输入流不是new出来的,是通过目录读取工具类打开的IndexReader indexReader = DirectoryReader.open(indexDirectory);
//      获取索引搜索对象IndexSearcher indexSearcher = new IndexSearcher(indexReader);//分词器Analyzer analyzer = new StandardAnalyzer();//查询解析器对象  带有查询的关键词信息QueryParser queryParser = new QueryParser("contents", analyzer);
//      获取符合关键字的查询对象 Query query = queryParser.parse(q);long start=System.currentTimeMillis();
//      获取关键字出现的前十次TopDocs topDocs = indexSearcher.search(query , 10);long end=System.currentTimeMillis();System.out.println("匹配 "+q+" ,总共花费"+(end-start)+"毫秒"+"查询到"+topDocs.totalHits+"个记录");for (ScoreDoc scoreDoc : topDocs.scoreDocs) {int docID = scoreDoc.doc;
//          索引搜索对象通过文档下标获取文档Document doc = indexSearcher.doc(docID);System.out.println("通过索引文件:"+doc.get("fullPath")+"拿数据");}indexReader.close();}

查询到这两个文件中包含EarlyTerminatingCollector单词

索引的删除修改

案例演示

private String ids[]={"1","2","3"};private String citys[]={"qingdao","nanjing","shanghai"};private String descs[]={"Qingdao is a beautiful city.","Nanjing is a city of culture.","Shanghai is a bustling city."};private FSDirectory dir;/*** 每次都生成索引文件* @throws Exception*/@Beforepublic void setUp() throws Exception {dir  = FSDirectory.open(Paths.get("D:\\software\\mysql\\data"));IndexWriter indexWriter = getIndexWriter();for (int i = 0; i < ids.length; i++) {Document doc = new Document();doc.add(new StringField("id", ids[i], Field.Store.YES));doc.add(new StringField("city", citys[i], Field.Store.YES));doc.add(new TextField("desc", descs[i], Field.Store.NO));indexWriter.addDocument(doc);}indexWriter.close();}/*** 获取索引输出流* @return* @throws Exception*/private IndexWriter getIndexWriter()  throws Exception{Analyzer analyzer = new StandardAnalyzer();IndexWriterConfig conf = new IndexWriterConfig(analyzer);return new IndexWriter(dir, conf );}/*** 测试写了几个索引文件* @throws Exception*/@Testpublic void getWriteDocNum() throws Exception {IndexWriter indexWriter = getIndexWriter();System.out.println("索引目录下生成"+indexWriter.numDocs()+"个索引文件");}/*** 打上标记,该索引实际并未删除* @throws Exception*/@Testpublic void deleteDocBeforeMerge() throws Exception {IndexWriter indexWriter = getIndexWriter();System.out.println("最大文档数:"+indexWriter.maxDoc());indexWriter.deleteDocuments(new Term("id", "1"));indexWriter.commit();System.out.println("最大文档数:"+indexWriter.maxDoc());System.out.println("实际文档数:"+indexWriter.numDocs());indexWriter.close();}/*** 对应索引文件已经删除,但是该版本的分词会保留* @throws Exception*/@Testpublic void deleteDocAfterMerge() throws Exception {
//      https://blog.csdn.net/asdfsadfasdfsa/article/details/78820030
//      org.apache.lucene.store.LockObtainFailedException: Lock held by this virtual machine:indexWriter是单例的、线程安全的,不允许打开多个。IndexWriter indexWriter = getIndexWriter();System.out.println("最大文档数:"+indexWriter.maxDoc());indexWriter.deleteDocuments(new Term("id", "1"));indexWriter.forceMergeDeletes(); //强制删除indexWriter.commit();System.out.println("最大文档数:"+indexWriter.maxDoc());System.out.println("实际文档数:"+indexWriter.numDocs());indexWriter.close();}/*** 测试更新索引* @throws Exception*/@Testpublic void testUpdate()throws Exception{IndexWriter writer=getIndexWriter();Document doc=new Document();doc.add(new StringField("id", "1", Field.Store.YES));doc.add(new StringField("city","qingdao",Field.Store.YES));doc.add(new TextField("desc", "dsss is a city.", Field.Store.NO));writer.updateDocument(new Term("id","1"), doc);writer.close();}

删除索引

deleteDocBeforeMerge 此方法只标记未删除索引

deleteDocAfterMerge

修改索引

修改之前

 修改之后

 这里就出现了dsss数据,代表修改成功

高级查询

特定项查询

@Beforepublic void setUp() {// 索引文件将要存放的位置String indexDir = "D:\\software\\mysql\\data";// 数据源地址String dataDir = "D:\\software\\mysql\\data\\d";IndexCreate ic = null;try {ic = new IndexCreate(indexDir);long start = System.currentTimeMillis();int num = ic.index(dataDir);long end = System.currentTimeMillis();System.out.println("检索指定路径下" + num + "个文件,一共花费了" + (end - start) + "毫秒");} catch (Exception e) {e.printStackTrace();} finally {try {ic.closeIndexWriter();} catch (Exception e) {e.printStackTrace();}}}/*** 特定项搜索*/@Testpublic void testTermQuery() {String indexDir = "D:\\software\\mysql\\data";String fld = "contents";String text = "indexformattoooldexception";
//      特定项片段名和关键字Term t  = new Term(fld , text);TermQuery tq = new TermQuery(t);try {FSDirectory indexDirectory = FSDirectory.open(Paths.get(indexDir));
//          注意:索引输入流不是new出来的,是通过目录读取工具类打开的IndexReader indexReader = DirectoryReader.open(indexDirectory);
//          获取索引搜索对象IndexSearcher is = new IndexSearcher(indexReader);TopDocs hits = is.search(tq, 100);
//          System.out.println(hits.totalHits);for(ScoreDoc scoreDoc: hits.scoreDocs) {Document doc = is.doc(scoreDoc.doc);System.out.println("文件"+doc.get("fullPath")+"中含有该关键字");}} catch (IOException e) {e.printStackTrace();}}/*** 查询表达式(queryParser)*/@Testpublic void testQueryParser() {String indexDir = "D:\\software\\mysql\\data";
//      获取查询解析器(通过哪种分词器去解析哪种片段)QueryParser queryParser = new QueryParser("contents", new StandardAnalyzer());try {FSDirectory indexDirectory = FSDirectory.open(Paths.get(indexDir));
//          注意:索引输入流不是new出来的,是通过目录读取工具类打开的IndexReader indexReader = DirectoryReader.open(indexDirectory);
//          获取索引搜索对象IndexSearcher is = new IndexSearcher(indexReader);//           由解析器去解析对应的关键字TopDocs hits = is.search(queryParser.parse("indexformattoooldexception") , 100);for(ScoreDoc scoreDoc: hits.scoreDocs) {Document doc = is.doc(scoreDoc.doc);System.out.println("文件"+doc.get("fullPath")+"中含有该关键字");}} catch (IOException e) {e.printStackTrace();} catch (ParseException e) {e.printStackTrace();}}

组合查询等

private int ids[]={1,2,3};private String citys[]={"qingdao","nanjing","shanghai"};private String descs[]={"Qingdao is a beautiful city.","Nanjing is a city of culture.","Shanghai is a bustling city."};private FSDirectory dir;/*** 每次都生成索引文件* @throws Exception*/@Beforepublic void setUp() throws Exception {dir  = FSDirectory.open(Paths.get("D:\\software\\mysql\\data"));IndexWriter indexWriter = getIndexWriter();for (int i = 0; i < ids.length; i++) {Document doc = new Document();doc.add(new IntField("id", ids[i], Field.Store.YES));doc.add(new StringField("city", citys[i], Field.Store.YES));doc.add(new TextField("desc", descs[i], Field.Store.YES));indexWriter.addDocument(doc);}indexWriter.close();}/*** 获取索引输出流* @return* @throws Exception*/private IndexWriter getIndexWriter()  throws Exception{Analyzer analyzer = new StandardAnalyzer();IndexWriterConfig conf = new IndexWriterConfig(analyzer);return new IndexWriter(dir, conf );}/*** 指定数字范围查询* @throws Exception*/@Testpublic void testNumericRangeQuery()throws Exception{IndexReader reader = DirectoryReader.open(dir);IndexSearcher is = new IndexSearcher(reader);NumericRangeQuery<Integer> query=NumericRangeQuery.newIntRange("id", 1, 2, true, true);TopDocs hits=is.search(query, 10);for(ScoreDoc scoreDoc:hits.scoreDocs){Document doc=is.doc(scoreDoc.doc);System.out.println(doc.get("id"));System.out.println(doc.get("city"));System.out.println(doc.get("desc"));}        }/*** 指定字符串开头字母查询(prefixQuery)* @throws Exception*/@Testpublic void testPrefixQuery()throws Exception{IndexReader reader = DirectoryReader.open(dir);IndexSearcher is = new IndexSearcher(reader);PrefixQuery query=new PrefixQuery(new Term("city","n"));TopDocs hits=is.search(query, 10);for(ScoreDoc scoreDoc:hits.scoreDocs){Document doc=is.doc(scoreDoc.doc);System.out.println(doc.get("id"));System.out.println(doc.get("city"));System.out.println(doc.get("desc"));}  }/*** 组合查询  (范围查询和指定字符串开头字母查询)* @throws Exception*/@Testpublic void testBooleanQuery()throws Exception{//获取IndexSearcher对象IndexReader reader = DirectoryReader.open(dir);IndexSearcher is = new IndexSearcher(reader);//条件NumericRangeQuery<Integer> query1=NumericRangeQuery.newIntRange("id", 1, 2, true, true);PrefixQuery query2=new PrefixQuery(new Term("city","q"));BooleanQuery.Builder booleanQuery=new BooleanQuery.Builder();booleanQuery.add(query1,BooleanClause.Occur.MUST);booleanQuery.add(query2,BooleanClause.Occur.MUST);TopDocs hits=is.search(booleanQuery.build(), 10);for(ScoreDoc scoreDoc:hits.scoreDocs){Document doc=is.doc(scoreDoc.doc);System.out.println(doc.get("id"));System.out.println(doc.get("city"));System.out.println(doc.get("desc"));} }

综合案例演示

查询

查询关键字为空则根据数据库进行查询,当关键字不为空,则根据关键字在建立的索引文件进行查询,但是在这查询需要有有分词的情况下进行查询,才能查询到。

例如: java入门

分词为: java - 入门

但是 当我们查询 公司有限责任

当我们查询限责就有可能会查询不到

删除

再次查询之后就没有了,这里的删除是需要把数据库中的数据和索引的对应的数据都进行删除

修改

添加

因为加权了,所以显示在最前面

在此我还特意写了一个索引的刷新,为了防止索引文件丢失之后还需要手动去维护

后端代码

private String title;private BlogDao blogDao = new BlogDao();private HttpServletRequest request = ServletActionContext.getRequest();public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}@SuppressWarnings({ "deprecation", "rawtypes" })public String refresh(){IndexWriterConfig conf = new IndexWriterConfig(new SmartChineseAnalyzer());Directory d;IndexWriter indexWriter = null;try {d = FSDirectory.open(Paths.get(PropertiesUtil.getValue("indexPath")));indexWriter = new IndexWriter(d , conf );//删除之前的索引indexWriter.deleteAll();//          为数据库中的所有数据构建索引List<Map<String, Object>> list = blogDao.list(null, null);//重新构建索引for (Map<String, Object> map : list) {Document doc = new Document();doc.add(new StringField("id", (String) map.get("id"), Field.Store.YES));
//              TextField用于对一句话分词处理 java培训机构TextField textField = new TextField("title", (String) map.get("title"), Field.Store.YES);HttpSession session = request.getSession();@SuppressWarnings("unchecked")ArrayList<String> attribute = (ArrayList)session.getAttribute("list");if(attribute!=null) {for (String id : attribute) {if(id.equals((String)map.get("id"))) {textField.setBoost(2f);//加权}}}doc.add(textField);doc.add(new StringField("url", (String) map.get("url"), Field.Store.YES));indexWriter.addDocument(doc);}ServletActionContext.getResponse().getWriter().print("123");} catch (IOException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}finally {try {if(indexWriter!= null) {indexWriter.close();}} catch (IOException e) {e.printStackTrace();}}return null;}public String list() {try {if (StringUtils.isBlank(title)) {List<Map<String, Object>> blogList = this.blogDao.list(title, null);request.setAttribute("blogList", blogList);}else {Directory directory = LuceneUtil.getDirectory(PropertiesUtil.getValue("indexPath"));DirectoryReader reader = LuceneUtil.getDirectoryReader(directory);IndexSearcher searcher = LuceneUtil.getIndexSearcher(reader);//中文分词器SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer();
//              拿一句话到索引目中的索引文件中的词库进行关键词碰撞Query query = new QueryParser("title", analyzer).parse(title);Highlighter highlighter = LuceneUtil.getHighlighter(query, "title");TopDocs topDocs = searcher.search(query ,100);//处理得分命中的文档List<Map<String, Object>> blogList = new ArrayList<>();Map<String, Object> map = null;ScoreDoc[] scoreDocs = topDocs.scoreDocs;for (ScoreDoc scoreDoc : scoreDocs) {map = new HashMap<>();Document doc = searcher.doc(scoreDoc.doc);map.put("id", doc.get("id"));String titleHighlighter = doc.get("title");if(StringUtils.isNotBlank(titleHighlighter)) {titleHighlighter = highlighter.getBestFragment(analyzer, "title", titleHighlighter);}map.put("title", titleHighlighter);map.put("url", doc.get("url"));blogList.add(map);}request.setAttribute("blogList", blogList);}} catch (Exception e) {e.printStackTrace();}return "blogList";}//新增索引public String insertBk() {try {HttpSession session = request.getSession();//获取参数Map<String, String[]> map = request.getParameterMap();String parameter = request.getParameter("Boost");//数据库添加int save = blogDao.save(map);if(save>0) {//索引建立IndexWriterConfig conf = new IndexWriterConfig(new SmartChineseAnalyzer());Directory d=FSDirectory.open(Paths.get(PropertiesUtil.getValue("indexPath")));IndexWriter indexWriter = new IndexWriter(d ,conf);Document doc = new Document();String id = request.getParameter("id");System.out.println("索引ID:"+id);doc.add(new StringField("id", id, Field.Store.YES));doc.add(new StringField("url", request.getParameter("url"), Field.Store.NO));TextField textField = new TextField("title",request.getParameter("title"), Field.Store.YES);//加权判断    得添加到数据库中,在这里使用的是session版if("on".equals(parameter)) {ArrayList<String> list = (ArrayList)session.getAttribute("list");if(list==null) {list=new ArrayList<>();System.out.println("加权ID:"+id);}list.add(id);session.setAttribute("list", list);list.forEach(e->{System.out.println("遍历ID:"+e);});textField.setBoost(2f);//设置权重,默认为1}doc.add(textField);indexWriter.addDocument(doc);indexWriter.close();}} catch (Exception e) {e.printStackTrace();}return "edit";}public String delete() {try {//判断数据库删除是否成功if(blogDao.delete(request.getParameterMap())>0) {IndexWriterConfig conf = new IndexWriterConfig(new SmartChineseAnalyzer());Directory d=FSDirectory.open(Paths.get(PropertiesUtil.getValue("indexPath")));IndexWriter indexWriter = new IndexWriter(d ,conf);indexWriter.deleteDocuments(new Term("id", request.getParameter("id")));indexWriter.forceMergeDeletes(); //强制删除indexWriter.commit();indexWriter.close(); }  } catch (Exception e) {e.printStackTrace();}return "edit";}public String  updateBk() {Map<String, String[]> paMap = request.getParameterMap();try {int edit = blogDao.edit(paMap);if(edit>0) {String id = request.getParameter("id");//修改Analyzer analyzer = new StandardAnalyzer();IndexWriterConfig conf = new IndexWriterConfig(analyzer);Directory dir=FSDirectory.open(Paths.get(PropertiesUtil.getValue("indexPath")));IndexWriter writer = new IndexWriter(dir, conf );Document doc=new Document();doc.add(new StringField("id", id, Field.Store.YES));doc.add(new TextField("title",request.getParameter("title"), Field.Store.YES));writer.updateDocument(new Term("id",id), doc);writer.close();}} catch (Exception e) {e.printStackTrace();}return "edit";}

代码及数据 提取码:ux0f

至此,Lucene介绍与使用介绍完毕,由于作者水平有限难免有疏漏,欢迎留言纠错。

Lucene介绍与使用相关推荐

  1. Elasticsearch(包括Lucene介绍)

    Lucene 简介 Lucene 是一种高性能.可伸缩的信息搜索(IR)库,在 2000 年开源,最初由鼎鼎大名的 Doug Cutting 开发,是基于 Java 实现的高性能的开源项目. Luce ...

  2. Lucene介绍及视频教程

    视频教程:http://pan.baidu.com/s/1xOOgD Lucene是一套用于 全文检索和 搜寻的开源程式库,由 Apache软件基金会支持和提供.Lucene提供了一个简单却强大的应用 ...

  3. Lucene(1):Lucene介绍

    Lucene官网: http://lucene.apache.org/ 1 搜索技术理论基础 1.1 lucene优势 原来的方式实现搜索功能,我们的搜索流程如下图: 上图就是原始搜索引擎技术,如果用 ...

  4. lucene入门介绍篇

    一 基本概念 1.lucene介绍 (1)简介 lucene是最受欢迎的java开源全文搜索引擎开发工具包.lucene使用反向索引,提供了完整的查询引擎和索引引擎,部分文本分词引擎.Lucene的目 ...

  5. 深入 Lucene 索引机制

    简介: Lucene 是一个基于 Java 的全文检索工具包,你可以利用它来为你的应用程序加入索引和检索功能.Lucene 目前是著名的 Apache Jakarta 家族中的一个开源项目,下面我们即 ...

  6. lucene 第一天

    Lucene/Solr   第一天 1. 课程计划 Lucene介绍 全文检索流程介绍 a) 索引流程 b) 搜索流程 Lucene入门程序 a) 索引实现 b) 搜索实现 分词器 a) 分词介绍 b ...

  7. Lucene5.5.4入门以及基于Lucene实现博客搜索功能

    前言 一直以来个人博客的搜索功能很蹩脚,只是自己简单用数据库的like %keyword%来实现的,所以导致经常搜不到想要找的内容,而且高亮显示.摘要截取等也不好实现,所以决定采用Lucene改写博客 ...

  8. 【转载保存】lucene优秀文章整理

    Lucene解析 - 基本概念:   https://www.jianshu.com/p/fbb171e73721?from=timeline&isappinstalled=0       L ...

  9. 全文检索工具包Lucene

    什么是全文检索 数据的分类 结构化数据:指的是格式固定.长度固定.数据类型固定的数据,例如数据库中的数据. 非结构化数据:指的是格式不固定.长度不固定.数据类型不固定的数据,例如 word 文档.pd ...

最新文章

  1. xcode 编译静态库所要注意
  2. MDT2010新功能(15)——完成部署后操作
  3. 快速判断list是否为空
  4. linux 内核协助的探测
  5. 2108年计算机二级考试时间,计算机二级office考前10天复习应该做哪些安排逢考必过...
  6. 详解用65行javascript代码做Flappy Bird
  7. Java面向对象---重写(Override)与重载(Overload)
  8. springBoot配置,贴个图
  9. set python用法_Python set()用法及代码示例
  10. chart放入panel_使用JPanel将ChartPanel添加到JTabbedPane
  11. 设计师学习HTML/CSS之路-08
  12. 软件测试 集成测试
  13. 杜比服务器系统安装,小编教你给Win10系统安装杜比音效驱动的方法
  14. linux第八周实验
  15. 中国联通517活动-沃福卡-技术分解实现方案
  16. 分享几个音乐下载网站
  17. 【spark2】【源码学习】【环境编译】在idea导入spark源码,执行example并debug
  18. 会员管理有哪些功能呢?
  19. [原][彩]情诗两首[下]--期对酒于襄阳,待重归于长江。
  20. Java实现第九届蓝桥杯螺旋折线

热门文章

  1. cv_8uc3是什么意思?
  2. offes给excel增加下拉选项_EXCEL小技巧
  3. 电子商务网站设计方案
  4. makefile自动变量与隐晦规则推导
  5. Java基础之流程控制语句
  6. c语言冷门小知识,这些C/C++冷门知识点你知道多少呢?来看一下
  7. java对mysql的简单操作——删除数据
  8. 【电脑】QQ/TIM群投票页面打不开解决办法
  9. 如何用Python实现照片马赛克效果?
  10. SSD-Mobilenet 结果准确率检验