之前写的有关搜索的dmeo,记录一下,如果有问题不负责
创建一个springboot的项目
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>searchdemo</artifactId>
<version>0.0.1</version>
<name>searchdemo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<lucene.version>7.7.3</lucene.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--Druid数据连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<!-- mysql connector数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-puls-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 实现web功能依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lucene -->
<!-- 添加lucene支持 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>${lucene.version}</version>
</dependency>
<!-- lucene-->
<!-- ik分词·-->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>IKAnalyzer</artifactId>
<version>2017_6_6_0</version>
</dependency>
<!--支持拼音-->
<dependency>
<groupId>com.github.open-android</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.0</version>
</dependency>
<!--工具封装类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.7</version>
</dependency>
<dependency>
<groupId>com.github.open-android</groupId>
<artifactId>pinyinAnalyzer</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>com.github.open-android</groupId>
<artifactId>pinyinTokenFilter</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--dto-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
#解决乱码问题
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8
package com.example.searchdemo.search.controller;
import java.util.List;
import com.example.searchdemo.search.IndexDemo;
import com.example.searchdemo.search.model.PersonTable;
import com.example.searchdemo.search.api.CommonResult;
import com.example.searchdemo.search.service.PersonTableService;
import com.example.searchdemo.search.utils.GenerateIdCard;
import com.example.searchdemo.search.utils.RandInfo;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
/**
* @author l
* @version 1.0
* @PACKAGE_NAME: com.example.searchdemo.search
* @date 2021/4/28 15:21 周三
*/
@RestController
public class DataBaseController {
@Autowired
PersonTableService personTableService;
@Autowired
IndexDemo indexDemo;
@GetMapping("/createIndex")
public String createIndex() {
//查询数据库,必须要批量查询
//每拉取一次数据
List<PersonTable> queryFood = personTableService.lists();
//获取字段
for (int i = 0; i < queryFood.size(); i++) {
//获取每行数据
PersonTable personTable = new PersonTable();
personTable.setPersonId(queryFood.get(i).getPersonId());
personTable.setPersonName(queryFood.get(i).getPersonName());
personTable.setIdentityNo(queryFood.get(i).getIdentityNo());
//创建Document对象
Document doc = new Document();
//获取每列数据
Field foodid = new Field("personId", personTable.getPersonId().toString(), TextField.TYPE_STORED);
Field foodname = new Field("personName", personTable.getPersonName(), TextField.TYPE_STORED);
Field price = new Field("identityNo", personTable.getIdentityNo().toString(), TextField.TYPE_STORED);
//添加到Document中
doc.add(foodid);
doc.add(foodname);
doc.add(price);
//调用,创建索引库
indexDemo.write(doc);
}
return "成功";
}
/**
* 搜索
* @param keyWord
* @return
*/
@GetMapping("/searchFood")
public CommonResult getFood(String keyWord) {
return indexDemo.searchssss(keyWord);
}
/**
* 随机生成人员信息
*/
@GetMapping("/addPon")
public void addPon() {
RandInfo rand = new RandInfo();
PersonTable person = new PersonTable();
GenerateIdCard g = new GenerateIdCard();
for (int i = 1; i <= 5000; i++) {
//在此控制生成的数量(如10个),按实际情况来
//为了防止有空格,取代空格
String idnum = g.generate().replace(" ", "");
String familyName = rand.getFamilyName();
String[] nameAndSex = rand.getNameAndSex(rand.getSex());
String name = nameAndSex[0];
String sex = nameAndSex[1];
int age = rand.getAge();
person.setPersonName(familyName.concat(name));
person.setSex(sex);
//过滤掉位数不够18位的
if (idnum.length() == 18) {
person.setIdentityNo(idnum);
}
person.setAge(age);
personTableService.addPon(person);
// System.out.println(person);
}
}
@RequestMapping("/searchs")
public ModelAndView searchs() {
ModelAndView view = new ModelAndView();
view.setViewName("search");
return view;
}
}
package com.example.searchdemo.search.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.searchdemo.search.model.PersonTable;
import java.util.List;
/**
* @author l
* @version 1.0
* @PACKAGE_NAME: com.dlax.dfms.base.service
* @date 2021/4/16 14:18 周五
*/
public interface PersonTableService extends IService<PersonTable> {
List<PersonTable> lists();
void addPon(PersonTable person);
}
package com.example.searchdemo.search.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.searchdemo.search.model.PersonTable;
import com.example.searchdemo.search.dao.PersonTableMapper;
import com.example.searchdemo.search.service.PersonTableService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author l
* @version 1.0
* @PACKAGE_NAME: com.dlax.dfms.base.service.impl.access
* @date 2021/4/16 14:27 周五
*/
@Slf4j
@Service
@Transactional(rollbackFor = RuntimeException.class)
public class PersonTableServiceImpl extends ServiceImpl<PersonTableMapper, PersonTable> implements PersonTableService {
@Autowired
PersonTableMapper personTableMapper;
@Override
public List<PersonTable> lists() {
return personTableMapper.lists();
}
@Override
public void addPon(PersonTable person) {
personTableMapper.insertpon(person);
}
}
package com.example.searchdemo.search.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.searchdemo.search.model.PersonTable;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author
* @since 2021-04-16
*/
@Repository
public interface PersonTableMapper extends BaseMapper<PersonTable> {
List<PersonTable> lists();
void insertpon(PersonTable person);
}
package com.example.searchdemo.search.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("db.persontable")
public class PersonTable implements Serializable {
private static final long serialVersionUID = 1L;
@TableField("person_id")
private Integer personId;
@TableField("person_name")
private String personName;
@TableField("identity_no")
private String identityNo;
private String sex;
private Integer age;
}
自定义分词Analyzer:中文分词+拼音分词
public class MyIkAnalyzer extends Analyzer {
private final boolean userSmart;
public MyIkAnalyzer(boolean userSmart){
this.userSmart = userSmart;
}
@Override
protected TokenStreamComponents createComponents(String s) {
//ik userSmart=false 关闭智能分词 设置细颗粒分词
Tokenizer tokenizer = new IKTokenizer(userSmart);
//useSingle 支持数字 useItself 本身
TokenStream tokenStream = new PinyinTokenFilter(new IKTokenFilter(tokenizer, true, true), true, true, 1);
return new TokenStreamComponents(tokenizer, tokenStream);
}
}
package com.example.searchdemo.search;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import com.example.searchdemo.search.api.CommonResult;
import com.example.searchdemo.search.model.PersonTable;
import com.example.searchdemo.search.myAnalyse.MyIkAnalyzer;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.stereotype.Component;
import org.wltea.analyzer.lucene.IKAnalyzer;
/**
* @author l
* @version 1.0
* @PACKAGE_NAME: com.example.searchdemo.search
* @date 2021/4/28 15:03 周三
*/
@Component
public class IndexDemo {
//Lucene索引文件路径
static String dir = "C:\\lucence";
//定义分词器
static Analyzer analyzer = new IKAnalyzer();
/**
* 封裝一个方法,用于将数据库中的数据解析为一个个关键字词存储到索引文件中
*
* @param doc
*/
public void write(Document doc) {
try {
//索引库的存储目录
Directory directory = FSDirectory.open(Paths.get(dir));
//关联当前lucence版本和分值器
//创建分词
Analyzer analyzer = new MyIkAnalyzer(false);
IndexWriterConfig icw = new IndexWriterConfig(analyzer);
// IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_7_7_3, analyzer);
//传入目录和分词器
IndexWriter iwriter = new IndexWriter(directory, icw);
//写入到目录文件中
iwriter.addDocument(doc);
//提交事务
iwriter.commit();
//关闭流
iwriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 查询索引
*
* @param q 参数
* @return 搜索结果
*/
public CommonResult searchssss(String q) {
long startTime = System.currentTimeMillis();
Directory dir = null;
IndexReader reader = null;
try {
String field = "personName";
//索引库的存储目录
Path indexPath = Paths.get("C:\\lucence");
dir = FSDirectory.open(indexPath);
reader = DirectoryReader.open(dir);
IndexSearcher searcher = new IndexSearcher(reader);
Analyzer analyzer = new IKAnalyzer(false, true, true);
QueryParser parser = new QueryParser(field, analyzer);
parser.setDefaultOperator(QueryParser.OR_OPERATOR);
//去除空格,特殊字符处理
String params = QueryParser.escape(q.replaceAll("\\s*", ""));
//查询参数
Query queryA = null;
Query query = parser.parse(params);
BooleanClause bc3 = new BooleanClause(query, BooleanClause.Occur.SHOULD);
queryA = new BooleanQuery.Builder().add(bc3).build();
System.out.println("Query:" + queryA);
//获取上一页的最后一个元素
ScoreDoc lastScoreDoc = getLastScoreDoc(1, 5, queryA, searcher);
//通过最后一个元素搜索下页的pageSize个元素
List<PersonTable> personTableList = new ArrayList<>();
TopDocs topDocs = searcher.searchAfter(lastScoreDoc, queryA, 5);
long totalHits = topDocs.totalHits;
System.out.println("查询到的条数:" + totalHits);
for (ScoreDoc sd : topDocs.scoreDocs) {
Document doc = searcher.doc(sd.doc);
PersonTable personTable = new PersonTable();
personTable.setPersonId(Integer.valueOf(doc.get("personId")));
personTable.setPersonName(doc.get("personName"));
personTable.setIdentityNo(doc.get("identityNo"));
personTableList.add(personTable);
System.out.println(doc);
}
return CommonResult.success(personTableList, "查询成功", totalHits);
} catch (Exception e) {
return CommonResult.failed("查询异常");
} finally {
long endTime = System.currentTimeMillis();
try {
dir.close();
reader.close();
} catch (IOException e) {
System.out.println("关流异常:" + e.getMessage());
}
}
}
/**
* 根据页码和分页大小获取上一次最后一个ScoreDoc
*
* @param query 查询参数
* @param indexSearcher 搜索
* @return 最后一个ScoreDoc
* @throws IOException io异常
*/
private ScoreDoc getLastScoreDoc(int pageIndex, int pageSize, Query query, IndexSearcher indexSearcher) throws IOException {
if (pageIndex == 1) {
//如果是第一页返回空
return null;
}
//获取上一页的数量
int num = pageSize * (pageIndex - 1);
TopDocs tds = indexSearcher.search(query, num);
long nums = num - 1;
if (nums < tds.totalHits) {
return tds.scoreDocs[num - 1];
}
return null;
}
}