luecene一些问题
luecene简介
lucene是一款嵌入web后台的搜索引擎。就我们通常的认知,baidu的原理是把全网的东西爬到自己的数据库中,然后根据数据库建立一个索引库,一些查询算法就是建立在索引库的基础上来检索索引库的内容。lucene在其中的作用类似上边的创建和维护索引库。这样的好处是减轻了数据库的压力,并且比mysql like查询要节约很多的时间,只是建立和维护索引需要花费不少的时间。最近想要把lucene嵌入到自己的web端。
具体嵌入步骤
1.简单lucene建立和维护索引测试
首先是pom文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.Itheima</groupId> <artifactId>luceneDemo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <skipTests>true</skipTests> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version> </parent> <dependencies> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-core --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>8.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-analyzers-common --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-common</artifactId> <version>8.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-queryparser --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>8.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--ik-analyzer 中文分词器--> <dependency> <groupId>cn.bestwu</groupId> <artifactId>ik-analyzers</artifactId> <version>5.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.2.6.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency> </dependencies> </project>
/** * 创建索引库 */ @Test public void createIndexTest() throws IOException { //1.采集数据 NewsDAO newsDAO = new NewsDAOImpl(); List<News> news = newsDAO.queryNewsList(); List<Document> docs = new ArrayList<>(); for(News news1:news){ //2.创建文档对象 Document doc = new Document(); //创建域对象 并放入文档 /** * 是否分词:否,主键分词后无意义 * 是否索引:是,需要查询 必须索引 * 是否存储:是,主键id比较特殊,可以根据此修改数据库 */ doc.add(new StringField("id",String.valueOf(news1.getId()), Field.Store.YES)); /** * 是否分词:是,对title分词可以搜寻部分标题对应的Doc,名称字段需要查询并且分词有意义 * 是否索引:是,需要查询,必须索引 * 是否存储,是,需要显示标题 */ doc.add(new TextField("title",news1.getTitle(), Field.Store.YES)); /** * 是否分词:否 需要查询,并且需要展示 (如果是根据价格范围查询,必须切分词) * 是否索引:是 将日期转化为String添加到索引中,方便查询区间 * 是否存储:是 */ doc.add(new StringField("pubDate", CommonUtil.dateToString(news1.getPubDate()), Field.Store.YES)); /** * 是否分词:y 需要查询,显示,所以是TextField * 是否索引: y * 是否存储: y */ doc.add(new TextField("infoSoure",news1.getInfoSoure(), Field.Store.YES)); /** * 是否分词: y * 是否索引: y * 是否存储: y */ doc.add(new TextField("summary",news1.getSummary(), Field.Store.YES)); /** * 是否分词: n * 是否索引: n * 是否存储: y */ doc.add(new StoredField("sourceUrl",news1.getSourceUrl())); /** * 是否分词: n * 是否索引: y * 是否存储: y */ doc.add(new StringField("updateTime",CommonUtil.dateToString(news1.getUpdateTime()), Field.Store.YES)); docs.add(doc); //System.out.println(news1.toString()); } //3.创建分词器对象 Analyzer analyzer = new StandardAnalyzer(); //4.创建Directory对象,索引库的位置 Directory dir = FSDirectory.open(Paths.get(DirPath)); //5.创建indexWriterConfig对象,指定切分词使用的分词器 IndexWriterConfig config = new IndexWriterConfig(analyzer); //6.创建indexWriter输出流对象,指定输出的位置和config对象 IndexWriter indexWriter = new IndexWriter(dir,config); //7.写入文档到索引库 for(Document d:docs){ indexWriter.addDocument(d); } //8.释放资源 indexWriter.close(); }
/** * 修改索引库 */ @Test public void updateIndexTest() throws IOException { //需要变更的内容 Document doc = new Document(); doc.add(new StringField("id","567", Field.Store.YES)); doc.add(new TextField("title","XXXXXX unknown", Field.Store.YES)); doc.add(new StringField("pubDate", "20200406", Field.Store.YES)); doc.add(new TextField("infoSoure","XXXX", Field.Store.YES)); doc.add(new TextField("summary","null", Field.Store.YES)); doc.add(new StoredField("sourceUrl","update to null")); doc.add(new StringField("updateTime","20200406", Field.Store.YES)); //3.创建分词器对象 Analyzer analyzer = new StandardAnalyzer(); //4.创建Directory对象,索引库的位置 Directory dir = FSDirectory.open(Paths.get(DirPath)); //5.创建indexWriterConfig对象,指定切分词使用的分词器 IndexWriterConfig config = new IndexWriterConfig(analyzer); //6.创建indexWriter输出流对象,指定输出的位置和config对象 IndexWriter indexWriter = new IndexWriter(dir,config); //第一个参数为修改的条件 indexWriter.updateDocument(new Term("id","567"),doc); //8.释放资源 indexWriter.close(); }
创建,删除和维护索引都类似于上边的操作,只是搜索索引可能会有一些麻烦,整形以及其他数字类型的区间搜索直接使用 IntRange 等即可,对于数据库字段是日期的搜索,这个花费了不少时间,解决方法如下:首先使用 DateTools.dateToString 把日期转化为String类型存储到域中,其次使用TermRangeQuery 来构建query,并且指定查询条件,代码如下:
@Test public void searchTest() throws IOException { //1.创建分词器,切分搜索的关键词 Analyzer analyzer = new StandardAnalyzer(); //注意:分词器要和创建的分词器一样 //2.创建查询对象 para: 1.默认查询域 2.analyser; info: 如果关键词带域名,则从此域名查询,否则使用默认域名 QueryParser queryParser = new QueryParser("pubDate",analyzer); //3.设置搜索关键词 // Query query = queryParser.parse("美国"); TermRangeQuery query = TermRangeQuery.newStringRange("pubDate","20200403","20200406",true,true); //4.创建Dir 指定索引库的位置 Directory dir = FSDirectory.open(Paths.get("F:\\IJ-idea\\Lucene-learn\\dir")); //5.创建输入流对象 IndexReader indexReader = DirectoryReader.open(dir); //6.创建搜索对象 IndexSearcher indexSearcher = new IndexSearcher(indexReader); //7.搜索,返回结果集 TopDocs topDocs = indexSearcher.search(query, 10); System.out.println("***********count********"+topDocs.totalHits); //8.获取结果集 ScoreDoc[] scoreDocs = topDocs.scoreDocs; //9.遍历结果集 if(scoreDocs!=null){ for(ScoreDoc scoreDoc:scoreDocs){ //获取文档ID int docID = scoreDoc.doc; //通过文档ID读取文档 Document doc = indexSearcher.doc(docID); System.out.println("========================="); System.out.println("--id--"+doc.get("id")+"--title--"+doc.get("title")+ "--infoSoure--"+doc.get("infoSoure")+"--pubDate--"+doc.get("pubDate")); } } //10.关闭流 indexReader.close(); }
但是对于上述查询,DateTools.dateToString会把你使用的日期提前一天,及如果你是20200201可能保存到域中结果会变成20200131,这时候你需要把你的日期拉后一天,代码如下:
public static String dateToString(Date date){ Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.add(Calendar.DAY_OF_MONTH,1); date = calendar.getTime(); String d = DateTools.dateToString(date, DateTools.Resolution.DAY); return d; }
2.嵌入web端:springBoot+mybatis+druid+lucene+mysql
持续更新0-0