jfinal elasticsearch5.5.1 搜索引擎插件

一、版本依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-to-slf4j</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
</dependency>

二、插件

import com.alibaba.fastjson.JSON;
import com.jfinal.kit.LogKit;
import com.jfinal.plugin.IPlugin;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;

/**
 * elasticsearch 插件
 * @author linkzz
 * @create 2018-01-02 16:22
 */
public class ElasticSearchPlugin implements IPlugin {
    private String ip;
    private int port;
    private String clusterName;
    private static TransportClient client;

    public ElasticSearchPlugin(String ip,int port,String clusterName){
        this.ip = ip;
        this.port = port;
        this.clusterName = clusterName;
    }

    public static TransportClient getClient() {
        return client;
    }

    @Override
    public boolean start() {
        Settings settings = Settings.builder()
                //设置ES实例的名称
                .put("cluster.name", this.clusterName)
                //自动嗅探整个集群的状态,把集群中其他ES节点的ip添加到本地的客户端列表中
                .put("client.transport.sniff", true)
                .build();

        /**
         * 这里的连接方式指的是没有安装x-pack插件,如果安装了x-pack则参考{@link ElasticsearchXPackClient}
         * 1. java客户端的方式是以tcp协议在9300端口上进行通信
         * 2. http客户端的方式是以http协议在9200端口上进行通信
         */
        try {
            client = new PreBuiltTransportClient(settings).addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(this.ip), this.port));
            List<DiscoveryNode> nodes = client.connectedNodes();
            if (nodes.isEmpty()) {
                LogKit.info("No NODES Connected");
            }else {
                for (DiscoveryNode node : nodes){
                    LogKit.info("节点信息:"+node.getHostName()+node.getName()+node.getHostAddress());
                }
            }
            LogKit.info("ElasticsearchClient 连接成功,节点包括:"+ JSON.toJSON(client.listedNodes()));
        } catch (UnknownHostException e) {
            LogKit.info(e.getMessage());
        }
        return true;
    }

    @Override
    public boolean stop() {
        if (client != null){
            client.close();
        }
        return true;
    }
}

三、启动插件

配置信息
elasticsearch_ip=127.0.0.1
elasticsearch_port=9300
cluster_name=elasticsearch

@Override
public void configPlugin(Plugins plugins) {
    String ip = PropKit.get("elasticsearch_ip");
    int port = PropKit.getInt("elasticsearch_port");
    String cluster_name = PropKit.get("cluster_name");
    plugins.add(new ElasticSearchPlugin(ip,port,cluster_name));
}

四、搜索接口服务

import com.alibaba.fastjson.JSON;
import com.jfinal.ext.kit.DateKit;
import com.jfinal.kit.LogKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Page;
import com.link.search.model.Article;
import com.link.search.model.SCPage;
import com.link.search.plugin.elasticsearch.ElasticSearchPlugin;
import com.link.search.service.SearchServiceI;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;

import java.util.*;

/**
 * 搜索接口服务
 * @author linkzz
 * @create 2018-01-04 17:56
 */
public class SearchServiceImpl implements SearchServiceI {

    /**
     * 搜索
     * @author linkzz
     * @create 2018-01-10 10:11
     */
    @Override
    public SCPage<Article> searche(String keyword, Integer pageSize, Integer pageNo, String index, String type, String field) {
        long start = System.currentTimeMillis();

        LogKit.info("client信息:"+ JSON.toJSON(ElasticSearchPlugin.getClient().listedNodes()).toString());

        QueryBuilder queryBuilders=null;

        keyword = null == keyword ? "" : keyword;
        pageSize = null == pageSize ? 10 : pageSize;
        pageNo = null == pageNo ? 1 : pageNo;

        HighlightBuilder hiBuilder = this.hiBuilder();
        if ("All".equals(type)){
            if (StrKit.notBlank(keyword)){
                queryBuilders = QueryBuilders.queryStringQuery(keyword);
                hiBuilder.field(field);
            }else {
                queryBuilders = QueryBuilders.matchAllQuery();
            }
        }


        SearchResponse response = ElasticSearchPlugin.getClient().prepareSearch(index)
                .setQuery(queryBuilders)
                .setFrom((pageNo - 1) * pageSize)
                .setSize(pageSize)
                .highlighter(hiBuilder)
                // 设置是否按查询匹配度排序
                .setExplain(true)
                .execute().actionGet();

        SearchHits hits = response.getHits();

        int total = (int) hits.getTotalHits();
        int totalPage = total / pageSize;
        if (total % pageSize == 0){
            totalPage += 1;
        }
        List<Article> articles = new ArrayList<>();

        for (SearchHit searchHit : hits){
            Map source = searchHit.getSource();
            Article article = JSON.parseObject(JSON.toJSONString(source),Article.class);
            //获取高亮域
            Map<String,HighlightField> result=searchHit.getHighlightFields();
            //查询所有
            if("All".equals(type)){
                //从高亮域中取得指定域
                HighlightField titlesField=result.get(field);
                if(titlesField!=null){
                    //取得定于的高亮标签
                    Text[] nameTexts=titlesField.getFragments();

                    //为name串值增加自定义的高亮标签
                    String titles = "";
                    for(Text text:nameTexts){
                        titles += text;
                    }
                    LogKit.info("标题名称:"+titles.toString());
                    article.setTitle(titles.toString());
                }
            }
            articles.add(article);
        }


        long end = System.currentTimeMillis();
        QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        SearchResponse searchResponse = ElasticSearchPlugin.getClient().prepareSearch(index).setQuery(queryBuilder).execute().actionGet();
        SearchHits hi = searchResponse.getHits();

        int totals=(int) hi.getTotalHits();
        LogKit.info("在" + totals + "条记录中,搜索"+keyword+",共用时间 -->> " + (end - start) + " 毫秒");
        SCPage<Article> page = new SCPage<>(articles,pageNo,pageSize,totalPage,total,totals,end - start);
        return page;
    }

    /**
     * 高亮处理
     * @author linkzz
     * @create 2018-01-10 10:10
     */
    private HighlightBuilder hiBuilder(){
        HighlightBuilder hiBuilder=new HighlightBuilder().field("*").requireFieldMatch(false);
        hiBuilder.preTags("<span style=\"color:red\">");
        hiBuilder.postTags("</span>");
        //高亮内容长度
        hiBuilder.fragmentSize(10000);
        hiBuilder.requireFieldMatch(false);
        return hiBuilder;
    }

 
    /**
     * 测试索引
     * @author linkzz
     * @create 2018-01-10 10:11
     */
    @Override
    public boolean index() {
        Map<String, Object> json = new HashMap<>();
        json.put("title","测试index");
        json.put("source","手动创建");
        json.put("time", DateKit.toStr(new Date(),DateKit.timeStampPattern));
        json.put("url","http://localhost:80");

        LogKit.info("client信息:"+ JSON.toJSON(ElasticSearchPlugin.getClient().listedNodes()).toString());
        IndexResponse response = ElasticSearchPlugin.getClient().prepareIndex("articl","content",String.valueOf(1))
                .setSource(json)
                .get();
        LogKit.info("版本号:"+response.getVersion());
        return false;
    }
}

五、调用服务

import com.jfinal.core.Controller;
import com.jfinal.kit.LogKit;
import com.link.search.model.Article;
import com.link.search.model.SCPage;
import com.link.search.service.SearchServiceI;
import com.link.search.service.impl.SearchServiceImpl;

/**
 * 搜索管理
 * @author linkzz
 * @create 2018-01-04 9:13
 */
public class SearchController extends Controller {
    SearchServiceI searchService = enhance(SearchServiceImpl.class);

    public void index(){
        String keyboard = getPara("keyboard");
        int pageNo = getParaToInt("pageNo") == null ? 1 : getParaToInt("pageNo");
        try {
            SCPage<Article> page = searchService.searche(keyboard,10,pageNo,"articl","All","title");
            setAttr("page",page);
            setAttr("keyboard",keyboard);
        } catch (Exception e) {
            e.printStackTrace();
        }
        LogKit.info("调用搜索控制器,关键字参数为:"+keyboard);
        render("result.html");
    }
}

六、效果

图片.png

评论区

lyh061619

2018-01-10 10:47

谢谢分享,有后面的朋友需求,就以你这个插件推荐给他们用。^_^!!

江南红衣

2018-01-10 17:21

看着不错哦

打酱油滴

2018-01-11 13:52

getParaToInt("pageNo",1)可以代替你上面的判断

gubingo

2018-01-12 16:20

感谢分享。学习中

linkzz

2018-01-12 16:42

@打酱油滴 好的 谢谢提醒!

jimmyyn

2018-01-15 09:13

只能传一个关键词?

linkzz

2018-01-18 15:22

@jimmyyn 可以传多个的

祭司18

2019-01-24 12:01

SCPage的源码可以分享一下吗

可爱的狼

2019-04-24 16:45

SearchServiceI 这个东西里面的代码是什么

linkzz

2019-04-26 08:39

@可爱的狼 接口定义,具体实现在SearchServiceImpl

热门分享

扫码入社