lucene+springboot+ik Analyzer实现mysql数据库高亮查询

首先我们要了解什么是ik分词器( 摘自百度百科 )
ik 分词器简介:
    从2006年12月推出1.0版开始,IKAnalyzer已经推出 了3个大版本。最初,它是以开源项目Lucene为应用主体的,结合词典分词和文法分析算法的中文分词组件。新版本的IKAnalyzer3.0则发展为 面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。
运行环境 :
    授权协议: LGPL
    开发语言: Java
    操作系统: 跨平台
最新版本 :
    当前最新版本为IKAnalyzer2012 (2012年停更)
1. 采用了特有的“正向迭代最细粒度切分算法“,支持细粒度和智能分词两种切分模式;
2.在系统环境:Core2 i7 3.4G双核,4G内存,window 7 64位, Sun JDK 1.6_29 64位 普通pc环境测试,IK2012具有160万字/秒(3000KB/S)的高速处理能力
3.2012版本的智能分词模式支持简单的分词排歧义处理和数量词合并输出。
4.采用了多子处理器分析模式,支持:英文字母、数字、中文词汇等分词处理,兼容韩文、日文字符
5. 优化的词典存储,更小的内存占用。支持用户词典扩展定义。特别的,在2012版本,词典支持中文,英文,数字混合词语。
相关特性 :
采用了特有的“正向迭代最细粒度切分算法“,具有60万字/秒的高速处理能力。
采用了多子处理器分析模式,支持:英文字母(IP地址、Email、URL)、数字(日期,常用中文数量词,罗马数字,科学计数法),中文词汇(姓名、地名处理)等分词处理。
对中英联合支持不是很好,在这方面的处理比较麻烦.需再做一次查询,同时是支持个人词条的优化的词典存储,更小的内存占用。
支持用户词典扩展定义。
针对Lucene全文检索优化的查询分析器IKQueryParser;采用歧义分析算法优化查询关键字的搜索排列组合,能极大的提高Lucene检索的命中率。
实现数据库连接查询:
测试表结构:

lucene+springboot+ik Analyzer实现mysql数据库高亮查询_第1张图片

创建一个maven项目,大致结构如下

lucene+springboot+ik Analyzer实现mysql数据库高亮查询_第2张图片

1. pom.xml配置

<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.0modelVersion>
    <groupId>cn.etgroupId>
    <artifactId>databaseartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <packaging>warpackaging>
    <properties>
        <webVersion>3.1webVersion>
    properties>
    
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>1.5.9.RELEASEversion>
    parent>

    <dependencies>
        
        <dependency>
            <groupId>com.janeluogroupId>
            <artifactId>ikanalyzerartifactId>
            <version>2012_u6version>
        dependency>
        
        <dependency>
            <groupId>org.apache.lucenegroupId>
            <artifactId>lucene-highlighterartifactId>
            <version>4.7.2version>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        
        <dependency>
            <groupId>MysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.38version>
        dependency>
    dependencies>
    <build />
project>

2. 数据库四要素 application.properties 配置

spring.datasource.url=jdbc:mysql://localhost/food
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#解决乱码问题
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8

3 然后我们需要连接数据库,对其进行查询。
因为数据库中的数据并不像自己测试般那么一点数据,所以我们必须要批量查询

DataBaseDao.java / DataBaseDaoImpl.java

package cn.et.database.dao.impl;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import cn.et.database.dao.DataBaseDao;
@Repository
public class DataBaseDaoImpl implements DataBaseDao{
    @Autowired
    JdbcTemplate jdbc;
    //查询数据库总行数
    public int foodcount() {
        String sql="select count(*) as foodCount from food";
        return Integer.parseInt(jdbc.queryForObject(sql, java.lang.String.class));
    }
    //每页显示行数
    public List> queryFood(int start,int rws) {
        String sql="select * from food limit " + start +"," + rws ;
        return jdbc.queryForList(sql);
    }
}

4.查询到的数据需要使用分词器来对其进行分词并存储,搜索时还要实现高亮等。
创建一个工具类,用于定义一个存储路径和分词器,搜索实现高亮。

IndexDemo.java

package cn.et.database.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
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.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.TextFragment;
import org.apache.lucene.search.highlight.TokenSources;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.springframework.stereotype.Component;
import org.wltea.analyzer.lucene.IKAnalyzer;
@Component
public class IndexDemo {
    //Lucene索引文件路径
    static String dir="E:\\lucence";
    //定义分词器
    static Analyzer analyzer = new IKAnalyzer();
    /**
     * 封裝一个方法,用于将数据库中的数据解析为一个个关键字词存储到索引文件中
     * @param doc
     */
    public void write(Document doc){
        try {
            //索引库的存储目录
            Directory directory = FSDirectory.open(new File(dir));
            //关联当前lucence版本和分值器
            IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_47, analyzer);
            //传入目录和分词器
            IndexWriter iwriter = new IndexWriter(directory, config);
            //写入到目录文件中
            iwriter.addDocument(doc);
            //提交事务
            iwriter.commit();
            //关闭流
            iwriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //搜索
    public List search(String field,String value) throws Exception{

            //索引库的存储目录
            Directory directory = FSDirectory.open(new File(dir));
            //读取索引库的存储目录
            DirectoryReader ireader = DirectoryReader.open(directory);
            //搜索类
            IndexSearcher isearcher = new IndexSearcher(ireader);
            //lucence查询解析器,用于指定查询的属性名和分词器
            QueryParser parser = new QueryParser(Version.LUCENE_47, field, analyzer);
            //搜索
            Query query = parser.parse(value);
            //最终被分词后添加的前缀和后缀处理器,默认是粗体
            SimpleHTMLFormatter htmlFormatter = new SimpleHTMLFormatter("","");
            //高亮搜索的词添加到高亮处理器中
            Highlighter highlighter = new Highlighter(htmlFormatter, new QueryScorer(query));

            //获取搜索的结果,指定返回document返回的个数
            ScoreDoc[] hits = isearcher.search(query, null, 5).scoreDocs;
            List list=new ArrayList();
            //遍历,输出
            for (int i = 0; i < hits.length; i++) {
                int id = hits[i].doc;
                Document hitDoc = isearcher.doc(hits[i].doc);
                Map map=new HashMap();
                map.put("foodid", hitDoc.get("foodid"));

                //获取到foodname
                String foodname=hitDoc.get("foodname");
                //将查询的词和搜索词匹配,匹配到添加前缀和后缀
                TokenStream tokenStream = TokenSources.getAnyTokenStream(isearcher.getIndexReader(), id, "foodname", analyzer);
                //传入的第二个参数是查询的值
                TextFragment[] frag = highlighter.getBestTextFragments(tokenStream, foodname, false, 10);
                String foodValue="";
                for (int j = 0; j < frag.length; j++) {
                  if ((frag[j] != null) && (frag[j].getScore() > 0)) {
                      //获取 foodname 的值
                      foodValue=((frag[j].toString()));
                  }
                }
                map.put("foodname", foodValue);

                map.put("price", hitDoc.get("price"));
                map.put("imagepath", hitDoc.get("imagepath"));
                list.add(map);
            }
            ireader.close();
            directory.close();
            return list;
    }
}

5.查询数据,调用工具类,将数据存储,分词,实现高亮。
DataBase.java

package cn.et.database.controller;

import java.util.List;
import java.util.Map;

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.RestController;

import cn.et.database.dao.DataBaseDao;
import cn.et.database.util.IndexDemo;

@RestController
public class DataBase {
    @Autowired
    DataBaseDao dao;
    @Autowired
    IndexDemo indexDemo;

    @GetMapping("/createIndex")
    public String createIndex(){
        //查询数据库,必须要批量查询
        int fc = dao.foodcount();//查询总行数    8
        int start = 0;  //开始位置
        int rows = 5;   //每页行数
        /*
         * 0---4  5
         * 5---9  5
         * 10---14  5
         */
        while(start<=rows){
            //每拉取一次数据
            List> queryFood=dao.queryFood(start, rows);
            //获取字段
            for(int i=0;i
                //获取每行数据
                Map lineData = queryFood.get(i);
                //创建Document对象
                Document doc = new Document();
                //获取每列数据
                Field foodid=new Field("foodid",lineData.get("foodid").toString(),TextField.TYPE_STORED);
                Field foodname=new Field("foodname",lineData.get("foodname").toString(),TextField.TYPE_STORED);
                Field price=new Field("price",lineData.get("price").toString(),TextField.TYPE_STORED);
                Field imagepath=new Field("imagepath",lineData.get("imagepath").toString(),TextField.TYPE_STORED);
                //添加到Document中
                doc.add(foodid);
                doc.add(foodname);
                doc.add(price);
                doc.add(imagepath);
                //调用,创建索引库
                indexDemo.write(doc);
            }
            start=rows+1;
        }
        return "成功";
    }
    //搜索,实现高亮
    @GetMapping("/searchFood")
    public List getFood(String keyWord) throws Exception{
        return indexDemo.search("foodname", keyWord);
    }
}

6.启动 TestMain.java
访问 http//localhost:8080/createIndex 将数据分词,存储(返回成功 ok )

package cn.et.database;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestMain {

    public static void main(String[] args) {
        SpringApplication.run(TestMain.class, args);
    }
}

7.接下来我们要实现一个简单的前端搜索页面,便于我们判断,使用 juqery+html
将 jquery-3.1.0.min.js 文件丢到webapp下
创建一个html文件

search.html


<html>
  <head>
    <meta name="keywords" content="keyword1,keyword2,keyword3">
    <meta name="description" content="this is my page">
    <meta name="content-type" content="text/html; charset=UTF-8">
    <script type="text/javascript" src="jquery-3.1.0.min.js">script>
    <script type="text/javascript">
        $(function(){
            $("#searchBtn").click(function(){
                $.ajax({
                    url:'/searchFood',
                    data:'keyWord='+$("input[name='keyWord']").val(),
                    success:function(jd){
                        $("div[name='c']").remove();
                        for(var i=0;ivar html=
                            "
"+ "

"+jd[i].foodname+"

"
+ ""+jd[i].foodname+"价格是:"+jd[i].price+""+ ""+ "
"
$("#foodDiv").append(html); } } }) }) }) script> head> <body> <div align="center"> <img src="log.png" style="height:55px;width: 190px;"><br> <table> <input type="text" name="keyWord" style="width:450px; height:30px;"> <input type="button" id="searchBtn" value="搜索一下" style="height: 37px;background-color: #38f"> table> <div id="foodDiv" align="justify"> div> div> body> html>

启动,访问 http//localhost:8080/searchFood 搜索

你可能感兴趣的:(lucene)