elasticsearch在JAVA中的应用

来源:互联网 发布:中美2535亿大单 知乎 编辑:程序博客网 时间:2024/06/03 19:41

本人花了一段时间研究了elasticsearch在JAVA中的使用,在此分享一些其中碰到的问题以及解决办法。由于是第一次写博客,可以改进的地方大家都可以提出来,欢迎交流。

首先,介绍一下elasticsearch。ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。在这里要提到另外一个也非常热门的搜索框架就是solr。这两者的区别和一些特点我总结如下:

使用区别:

1.如果需要实时查询搜索的话,建议使用elk架构。因为solr在实时建立索引时,会产生io阻塞。而单纯对已有数据搜索,solr更快。

功能和管理上区别:

1.Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;

2.Solr 支持更多格式的数据(HTML、PDF、微软 Office 系列软件格式以及 JSON、XML、CSV 等纯文本格式),而 Elasticsearch 仅支持json文件格式;

3.Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供。

关于elasticsearch如何在java中使用百度上已经有很多的教程了。在这,我就不介绍了,相信对于爱钻研的各位找到对应的资料非常简单。在这里我就说一下我在使用过程中碰到到的一些点,希望可以在以后你们使用的时候能有帮助。在这之前,也希望读者们能事先先看看网上关于旧版本使用的实例,这样有助于理解。

首先,一个很关键的问题,elasticsearch版本的问题,不同的版本在使用的时候都会有蛮多地方的不同,而网上很难找到最新版本的使用说明,只能通过自己去查api去使用,而这个对于像我这样的初学者,是非常耗时间和精力的。写这篇博客的目的也有部分是为了巩固下所学的。我一开始使用的就是网上教程的版本,下载安装,编码,调试,其实也没碰到什么问题。但是后来我们的组长说,最好用比较新的版本,后来我就去下了最新的版本,结果很多问题就来了。首先是加载client时的不同,在旧的版本中的client初始化代码:

Client client = new TransportClient()        .addTransportAddress(new InetSocketTransportAddress("host1", 9300))        .addTransportAddress(new InetSocketTransportAddress("host2", 9300));
如果需要进行些初始化设置的话可以通过Settings这个类去设置,例如修改集群名,默认是elasticsearch,等等。
Settings settings = ImmutableSettings.settingsBuilder()        .put("cluster.name", "myClusterName").build();Client client =    new TransportClient(settings);
而在2.X版本中又有改变:
Client client = TransportClient.builder().build()        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host1"), 9300))        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host2"), 9300));

Settings settings = Settings.settingsBuilder()        .put("cluster.name", "myClusterName").build();Client client = TransportClient.builder().settings(settings).build();
而在最新的5.X版本中:
TransportClient client = new PreBuiltTransportClient(Settings.EMPTY)        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host1"), 9300))        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("host2"), 9300));

Settings settings = Settings.builder()        .put("cluster.name", "myClusterName").build();TransportClient client = new PreBuiltTransportClient(settings);
在这里也大概介绍下整个流程,首先是新建个client,其次就是创建生成索引的方法,之后就是写search方法。这里提供下我的代码供参考:

public class ElasticSearchHandler {    private TransportClient  client;    //client初始化    public ElasticSearchHandler(){        try {                client=new PreBuiltTransportClient(Settings.EMPTY)                        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9300));        }catch (Exception e){            e.printStackTrace();        }    }    //创建索引库    public void createIndexResponse(String indexname, String type, List<String> jsondata){        IndexRequestBuilder requestBuilder = client.prepareIndex(indexname, type);        for(int i=0; i<jsondata.size(); i++){            requestBuilder.setSource(jsondata.get(i)).execute().actionGet();        }    }    //搜索方法    public List<User>  searcher(QueryBuilder queryBuilder, String indexname, String type){        List<User> list = new ArrayList<User>();        SearchResponse searchResponse = client.prepareSearch(indexname).setTypes(type)                .setQuery(queryBuilder)                .execute()                .actionGet();        SearchHits hits = searchResponse.getHits();        System.out.println("查询到记录数=" + hits.getTotalHits());        SearchHit[] searchHists = hits.getHits();        if(searchHists.length>0){            for(SearchHit hit:searchHists){                String name =  (String) hit.getSource().get("myname");                int age = (Integer)hit.getSource().get("age");                String namep=(String)hit.getSource().get("mynamep");                list.add(new User(name,age,namep));            }        }        return list;    }    //执行方法    public List<User> dosearch(String indexname,String type,String searchdata){        ElasticSearchHandler esHandler = new ElasticSearchHandler();        //查询条件        QueryBuilder queryBuilder1=QueryBuilders.matchPhraseQuery("myname", searchdata);        QueryBuilder queryBuilder2=QueryBuilders.prefixQuery("mynamep",searchdata);        QueryBuilder queryBuilder=QueryBuilders.disMaxQuery().add(queryBuilder1).add(queryBuilder2);        /*QueryBuilder queryBuilder = QueryBuilders.boolQuery()          .must(QueryBuilders.termQuery("id", 1));*/        List<User> result = esHandler.searcher(queryBuilder, indexname, type);        return result;    }
这里面的User就是我的实体类。然后我实现功能是联想模糊查询。比如我要查询myname为“新年快乐”的所有User,而我输入“新年”,“xinnian”即可返回对应的User。

这里有几点要特别说明下

1.首先是搜索方法中的SearchResponse中的.setQuery方法参数问题。在旧的版本中是不支持传入QueryBuilder类型的,具体支持的参数可以看源码或者API。其实也没什么大碍,只要转换一下就行。

2.执行方法中的QueryBuilders类。这个类创建的实例在不同的版本中提供的方法区别非常的大,简单来说就是在不停的更新和完善(其实每个框架版本更新都是这样的= =)。这里就说几点我觉得会常用,

(1)首先是matchQuery这个方法,这个方法本身就支持中文的分词查询(什么是分词查询-->www.baidu.com)。当我们将数据库的对象都存入elasticsearch服务器的时候,其中有我们的自己实体类,当我们想通过实体类的属性去查询时(在开发中,实际上的查询大部分都是这样),就可以通过这个方法,第一个参数是你实体类的属性名,后面的对应的value。而在老版本中会有一个特殊的方法去支持:fieldQuery();而在新的版本中这个方法已经删去了。而在我的代码中的matchPhraseQuery方法跟matchQuery的区别就是前者是完全匹配,而后者只要匹配上一个字即可。这个要根据具体的需求去选择。打个比方,比如我有两个文档其中的myname属性的值分别是“哈哈哈”,”嘿嘿嘿”。如果我是使用matchQuery(“myname”,“哈嘿”),这样这两个文档就都会被查询出来。而用matchPhraseQuery(“myname”,“哈嘿”),则一条都没。必须精确匹配到才会返回查询结果。顺便提一点,matchPhraseQuery本身也支持模糊查询。(即mysql的like)。

(2)其次就是我用到的另一个prefixQuery。这个方法其实我是之前根据方法名直接预判他的功能的,而实际达到的效果也跟我预想的一样(再一次证明了方法名取的要有意义这句话的重要性)。就是根据前缀取查询,比如我有个字段为“abcde”,当你输入“a”,"ab","abc"...都是可以查询得到的。一旦出现不匹配就不会返回该文档。

(3)dixMaxQuery这个方法其实是我研究了很久才发现能实现我的需求的方法,简单来说就是多条查询条件的OR组合查询。之前我了解到一些类似功能的方法,比如multimatch();boolquery()。前者同属性多对应值的查询,后者是多条件的递进查询。比如你想实现多条查询条件的AND组合就可以使用这个,而这个方法下的must,should,filter,mustnot方法也是大有学问在里面。感兴趣的同学可以去研究研究。(最好能再告诉我^_^)

(4)还有termQuery这个方法,这个方法其实跟matchQuery有点类似,只不过前者是不支持分词查询,后者是支持分次查询。而且matchPhraseQuery中还有个slop调节因子,即可以设定少匹配一个字,默认是完全匹配。


关于elasticsearch分布式搜索的强大搜索功能还有很多很多,值得大家慢慢去探索和研究。有想法的欢迎互相交流


0 0