前言
搜索就是搜寻,查找,百度 在IT行业中就是指输入关键字,通过相应的算法,查询并返 回所需要的信息。比较日常的话可能就是对数据库的查询搜索 SELECT .. WHERE ..之类的云云。但是比如说像百度一样的根据文本框的文字来进行拆分查找,并且数据量可以达到500W以上的话 数据库能承受得住吗,要多久呢!
1.没有通过高效的索引方式,所以查询的速度在大量数据的情况下是很慢。
2.搜索效果比较差,只能对用户输入的完整关键字首尾位进行模糊匹配。用户搜索的结果误多输入一个字符,可能就导致查询出的结果远离用户的预期 这时候就需要我们的主角来帮助我们来实现这个需求。
Lucene
Lucene、Solr、Elasticsearch关系
Lucene:底层的API,工具包
Solr:基于Lucene开发的企业级的搜索引擎产品
Elasticsearch:基于Lucene开发的企业级的搜索引擎产品
Lucene的基本使用
创建索引的流程(别说了盗图的)
文档Document:数据库中一条具体的记录
字段Field:数据库中的每个字段
目录对象Directory:物理存储位置
写出器的配置对象:需要分词器和lucene的版本
话不多说coding... (哈哈激动)
导入依赖。。。
<!-- Lucene核心 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>4.7.2</version> </dependency> <!-- Lucene搜索查询相关 查询解析器--> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>4.7.2</version> </dependency> <!-- Lucene分词器相关 基本分词器 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-common</artifactId> <version>4.7.2</version> </dependency> <!-- lucene的高亮显示 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-highlighter</artifactId> <version>4.7.2</version> </dependency> <!-- Lucene分词器相关 ik--> <!-- https://mvnrepository.com/artifact/com.janeluo/ikanalyzer --> <dependency> <groupId>com.janeluo</groupId> <artifactId>ikanalyzer</artifactId> <version>2012_u6</version> </dependency>
创建索引代码实现。。。
package com.Lucene; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.*; import org.apache.lucene.index.*; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import java.io.File; import java.io.IOException; /** * created by wudeming on 2019/10/13. */ public class LuceneIndexDemo { public static void main(String[] args) { // Lucene Document的域名 String fieldName = "nan"; String text = ""; // 建立5条索引 text = "特步男鞋运动鞋2019夏季新款网面透气休闲鞋秋季小白鞋跑步鞋男"; doIndex(fieldName, text); text = "正品#全球购#人肉代#阿玛尼生姜高光替代款!DOUBLE GLEAM火起来真的不是没有道理,上脸真的太好看啦,是很闪很有存在感的高光爆炸热款,需要的私聊,少量!"; doIndex(fieldName, text); text = "#专业代购#全球购#只做正品#爆款老前辈,阿玛尼红色气垫粉底,主打轻薄透气妆感,喜欢自然妆效的小伙伴一定要购入啊!轻薄不一定就不遮瑕,遮瑕适中,打造纹理肌肤最新时尚,你值得尝试,遮瑕也许不厉害,但妆感高级。色号齐全,可以调货,需要的私我。#只做真货#拒绝假货#"; doIndex(fieldName, text); text = "Moncler 蒙口黑色羽绒服,女款, bandama 、 黑色0码收图,其他的尺码待查"; doIndex(fieldName, text); text = "在澳洲的小伙伴赶紧联系我鸭~我需要十瓶这个伊索香芹籽精华,要澳洲专柜带发票喔,或者最近要去澳洲的小伙伴也可以(要出示购物小视频)。#香港或者国内商家勿扰,只要澳洲本地专柜人肉购入#(价格私聊)"; doIndex(fieldName, text); } /** * * @param fieldName 文本标题 * @param text 文本内容 */ private static void doIndex(String fieldName, String text) { //1 创建文档对象 // 2 创建存储目录 //3 创建分词器 //4 创建索引写入器的配置对象 //5 创建索引写入器对象 //6 将文档交给索引写入器 //7 提交 //8 关闭 // 实例化Analyzer分词器 Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47); // 实例化IKAnalyzer分词器 // Analyzer iKAnalyzer = new IKAnalyzer(); //索引目录 Directory directory = null; //写对象 IndexWriter iwriter; try { //创建文档对象 Document doc = new Document(); // 索引目录 指定索引在硬盘中的位置 创建存储目录 directory = new SimpleFSDirectory(new File("E://test/lucene_index")); // 配置IndexWriterConfig 参数:LUCENE版本 分词器 IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_47, analyzer); //设置打开方式: IndexWriterConfig.OpenMode.APPEND 会在索引库的基础上追加索引 // IndexWriterConfig.OpenMode.CREATE 会清空原来的数据在提交 这里我选择了新建或追加 iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND); // 创建索引写对象 参数:文件目录 索引配置对象 iwriter = new IndexWriter(directory, iwConfig); //当前时间戳作为id Long id = System.currentTimeMillis(); //DoubleField、FloatField、IntField、LongField、StringField、TextField这些子类一定会被创建索引, // 但是不会被分词,而且不一定会被存储到文档列表。要通过构造函数中的参数Store来指定:如果Store.YES代表存储,Store.NO代表不存储 //StoreField一定会被存储,但是一定不创建索引 //创建并添加字段信息 参数:字段的名称 字段的值 是否储存 Field.Store.YES储存 doc.add(new StringField("ID", id + "", Field.Store.YES)); //参数:文件名 文件内容 是否储存; TextField创建索引并且分词 StringField 支撑件索引不会分词 doc.add(new TextField(fieldName, text, Field.Store.YES)); //写入文档 iwriter.addDocument(doc); //提交 iwriter.commit(); //关闭流 iwriter.close(); System.out.println("建立索引成功:" + id); } catch (CorruptIndexException e) { e.printStackTrace(); } catch (LockObtainFailedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (directory != null) { try { directory.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
控制台输出
索引目录
查询索引数据代码实现...
package com.Lucene; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import java.io.File; import java.io.IOException; /** * created by wudemign on 2019/10/13. */ public class LuceneSearchDemo { public static void main(String[] args) { //1 创建读取目录对象 // 2 创建索引读取工具 // 3 创建索引搜索工具 // 4 创建查询解析器 // 5 创建查询对象 // 6 搜索数据 // 7 各种操作 // Lucene Document的域名 String fieldName = "nan"; Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47); Directory directory = null; IndexReader ireader = null; IndexSearcher isearcher; try { //索引目录 directory = new SimpleFSDirectory(new File("E://test/lucene_index")); // 配置IndexWriterConfig IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_47, analyzer); iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND); // 搜索过程********************************** // 实例化搜索器 ireader = DirectoryReader.open(directory); isearcher = new IndexSearcher(ireader); String keyword = "特步"; // 使用QueryParser查询分析器构造Query对象 QueryParser qp = new QueryParser(Version.LUCENE_47, fieldName, analyzer); qp.setDefaultOperator(QueryParser.OR_OPERATOR); // and or 跟数据库查询语法类似 Query query = qp.parse(keyword); System.out.println("Query = " + query); //查找的数量 int num = 2; // 返回的结果是 按照匹配度排名得分前num名的文档信息(包含查询到的总条数信息、所有符合条件的文档的编号信息)。 TopDocs topDocs = isearcher.search(query, num); System.out.println("命中:" + topDocs.totalHits); // 遍历输出结果 ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (int i = 0; i < topDocs.totalHits; i++) { if (i == num) break; Document targetDoc = isearcher.doc(scoreDocs[i].doc); System.out.println("内容:" + targetDoc.toString()); } } catch (ParseException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (ireader != null) { try { ireader.close(); } catch (IOException e) { e.printStackTrace(); } } if (directory != null) { try { directory.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
控制台输出
修改索引数据代码实现...
package com.Lucene; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.*; import org.apache.lucene.index.*; import org.apache.lucene.store.Directory; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import java.io.File; import java.io.IOException; /** * * 注意: * 1:Lucene修改功能底层会先删除,再把新的文档添加。 * 2:修改功能会根据Term进行匹配,所有匹配到的都会被删除。这样不好 * 3:因此,一般我们修改时,都会根据一个唯一不重复字段进行匹配修改。例如ID * 4:但是词条搜索,要求ID必须是字符串。如果不是,这个方法就不能用。 * 如果ID是数值类型,我们不能直接去修改。可以先手动删除deleteDocuments(数值范围查询锁定ID),再添加。 * created by wudemign on 2019/10/13. */ public class LuceneUpdateDemo { public static void main(String[] args) { //1 创建索引目录 //2 创建索引写入器配置对象 //3 创建索引写入器 //4 创建文档数据 //5 修改 //6 提交 //7 关闭 // 实例化IKAnalyzer分词器 Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47); Directory directory = null; IndexWriter iwriter; try { // 索引目录 directory = new SimpleFSDirectory(new File("E://test/lucene_index")); // 配置IndexWriterConfig IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_47, analyzer); iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND); iwriter = new IndexWriter(directory, iwConfig); // 写入索引 Document doc = new Document(); //索引ID String id = "1571294471827"; doc.add(new StringField("ID", id, Field.Store.YES)); doc.add(new TextField("nan", "更新文档后->安踏男鞋运动鞋2019夏季新款网面透气休闲鞋秋季小白鞋跑步鞋男", Field.Store.YES)); //先根据Term ID 删除,在建立新的索引 iwriter.updateDocument(new Term("ID", id), doc); iwriter.close(); System.out.println("更新索引成功:" + id); } catch (CorruptIndexException e) { e.printStackTrace(); } catch (LockObtainFailedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (directory != null) { try { directory.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
控制台输出
查看更新的数据能否查到
删除索引数据代码实现...
package com.Lucene; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.index.*; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.store.Directory; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import java.io.File; import java.io.IOException; /* * 演示:删除索引 * 注意: * 一般,为了进行精确删除,我们会根据唯一字段来删除。比如ID * 如果是用Term删除,要求ID也必须是字符串类型! * created by wudemign on 2019/10/13. */ public class LuceneDeleteDemo { public static void main(String[] args) { //1 创建文档对象目录 //2 创建索引写入器配置对象 //3 创建索引写入器 //4 删除 //5 提交 //6 关闭 // Lucene Document的域名 String fieldName = "nan"; Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47); Directory directory = null; IndexReader ireader = null; IndexSearcher isearcher; IndexWriter iwriter = null; try { //索引目录 directory = new SimpleFSDirectory(new File("E://test/lucene_index")); // 配置IndexWriterConfig 创建配置对象 IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_47, analyzer); iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND); ireader = DirectoryReader.open(directory); // 创建索引写出工具 iwriter = new IndexWriter(directory, iwConfig); // 根据词条进行删除 iwriter.deleteDocuments(new Term("ID", "1571294471827")); // 根据query对象删除,如果ID是数值类型,那么我们可以用数值范围查询锁定一个具体的ID // Query query = NumericRangeQuery.newLongRange("id", 1571294471827, 1571294471827, true, true); // writer.deleteDocuments(query); //使用IndexWriter进行Document删除操作时,文档并不会立即被删除,而是把这个删除动作缓存起来, // 当IndexWriter.Commit()或IndexWriter.Close()时,删除操作才会被真正执行。 iwriter.commit(); iwriter.close(); ireader.close(); } catch (Exception e) { e.printStackTrace(); } finally { if (directory != null) { try { directory.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
搜索下看在不在。。
以上是在学习中的呆萌。。。 只是其中的简单的增删改查