草庐IT

【ES使用】Java API操作ES宝典(7.x版本及其以下)

老坛聊开发 2023-04-08 原文

大家好,我是老坛。

本篇文章全部代码资源请关注同名公众号:老坛聊开发

回复:"7.x模板" 即可获取

Elasticsearch是一个分布式的RESTful 风格的搜索和数据分析引擎,它使用方便,查询速度快,因此也被越来越多的开发人员使用。

在Java项目中,使用ES的场景也十分常见。除了作为某些特定资源的存储之外也可以作为像ELK这样的日志收集系统里的存储引擎。总之,对于非关系型而查找需求较多的场景,ES的表现还是非常不错的。

那今天老坛就带大家看一看如何使用Java API来操作ES。

当前文档只限于ES的版本在7.x及其以下的情况,如果是8.x的可以去看我的另一篇文档:

【ES使用】Java API操作ES宝典(8.x版本)https://blog.csdn.net/qq_34263207/article/details/127847033

目录

1.准备工作

1.1引入依赖

1.2配置文件

2. 简单操作

2.1 插入数据

2.2 查询数据

2.3 修改数据

2.4 删除数据

3 复杂查询

3.1 整体介绍

3.2 QueryBuilder说明

3.2.1 MatchQueryBuilder

3.2.2 MatchPhraseQueryBuilder

3.2.3 MultiMatchQueryBuilder

3.2.4 TermQueryBuilder

3.2.5 TermsQueryBuilder

3.2.6 FuzzyQueryBuilder

3.2.7 RangeQueryBuilder

3.3 bool查询

3.4 排序和分页

4.代码模板

5. 总结


1.准备工作

在真正使用es api之前,还有一些准备工作要去做,分别是引入依赖和写好配置文件

1.1引入依赖

对于7.x及其以下的版本,spring data这边是支持的,所以可以根据自己ES的具体版本来引入匹配版本的spring data starter,具体如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

关于如何去查看spring data与ES的版本匹配关系,大家可以去看我的这篇文章,这里便不再赘述了。

【ES知识】es版本与java api版本对照一览https://blog.csdn.net/qq_34263207/article/details/127790216

1.2配置文件

接下来就是去写配置文件了。我们要连接ES所要用到的ip和port,用户名和密码这些信息建议大家都写在自己的yml里,方便维护:

es:
  address: 127.0.0.1
  port: 9200
  scheme: http
  username: admin
  password: admin

接下来我们要写一个config文件来使用这些配置,具体代码如下:

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticSearchConfig {

    @Value("${es.address}")
    String address;

    @Value("${es.port}")
    Integer port;

    @Value("${es.scheme}")
    String scheme;

    @Value("${es.username}")
    String username;

    @Value("${es.password}")
    String password;

    @Bean
    public RestHighLevelClient esRestClient(){
        RestClientBuilder builder = null;
        builder = RestClient.builder(new HttpHost(address, port, scheme));
        RestHighLevelClient client =  new RestHighLevelClient(builder);
        return client;
    }
}

这个是无用户名和密码版本的配置文件,大家可以直接拿走使用。

下面贴一下需要配置用户名和密码的config文件:

import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticSearchConfig {

    @Value("${es.address}")
    String address;

    @Value("${es.port}")
    Integer port;

    @Value("${es.scheme}")
    String scheme;

    @Value("${es.username}")
    String username;

    @Value("${es.password}")
    String password;

    @Bean
    public RestHighLevelClient esRestClientWithCred(){
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(address, port, scheme))
                .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                    @Override
                    public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
                        return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                    }
                });
        RestHighLevelClient esClient = new RestHighLevelClient(restClientBuilder);
        return esClient;
    }
}

按需来取即可。

2. 简单操作

也就是最基本的CRUD了,但是这部分介绍的都是只针对一条数据的CRUD操作,都是通过id来去进行的,比较简单。而操作这些所使用到的类也比较有规律,这里我给大家画一张图方便理解:

 RestHighLevelClient是我们所有操作的核心,它也是在我们config里面写好的,等着被注入就可以了。对于每一种操作都有其相应的request和response,我们在request里面填充需要做的操作,用response接收结果。下面举例来介绍这些操作。

更多优质文章资源请关注同名公众号:老坛聊开发

先介绍一下我用到的实体对象:

@Data
public class TextBook {

    String bookName;

    String author;

    Integer num;
}

对应的索引数据为:

{
    "_index": "textbook",
    "_id": "kIwXeYQB8iTYJNkI986Y",
    "_source": {
        "bookName": "This is a test doc",
        "author": "老坛",
        "num": 20
    }
}

2.1 插入数据

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {

    @Resource
    RestHighLevelClient restHighLevelClient;

    String index = "index";

    @Test
    public void insertSingle(String id) throws IOException {
        IndexRequest request = new IndexRequest(index);
        request.id(id);
        // 要插入的实体
        TextBook textBook = new TextBook();
        textBook.setBookName("老坛聊开发");
        textBook.setAuthor("老坛");
        textBook.setNum(20);
        request.source(JSON.toJSONString(textBook), XContentType.JSON);
        IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        log.info("返回状态:" + indexResponse.status().getStatus());
    }
}

我们需要将RestHighLevelClient注入进来,然后就可以操作了。

2.2 查询数据

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {

    @Resource
    RestHighLevelClient restHighLevelClient;

    String index = "index";

    @Test
    public void grepSingle(String id) throws IOException {
        GetRequest getRequest = new GetRequest(index);
        getRequest.id(id);
        GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        String json = getResponse.getSourceAsString();
        TextBook textBook = JSONObject.parseObject(json, TextBook.class);
        log.info("返回结果为:" + JSON.toJSONString(textBook));
    }
}

这里是使用id查询单条数据,相当于sql的getByPrimaryKey。

2.3 修改数据

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {

    @Resource
    RestHighLevelClient restHighLevelClient;

    String index = "index";

    @Test
    public void updateSingle(String id) throws IOException {
        GetRequest getRequest = new GetRequest(index);
        getRequest.id(id);
        // 先查看是否包含该数据,以防报错
        boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        if(exists) {
            UpdateRequest updateRequest = new UpdateRequest(index, id);
            TextBook textBook = new TextBook();
            // 要更新的实体
            textBook.setBookName("老坛聊开发");
            textBook.setAuthor("老坛");
            updateRequest.doc(JSON.toJSONString(textBook), XContentType.JSON);
            UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
            log.info("返回状态:" + updateResponse.status().getStatus());
        }
    }
}

这里先用exist方法查询了一下该条数据是否存在,存在的话再去修改数据。

2.4 删除数据

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {

    @Resource
    RestHighLevelClient restHighLevelClient;

    String index = "index";

    @Test
    public void deleteSingle(String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(index);
        deleteRequest.id(id);
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        log.info("返回状态:" + deleteResponse.status().getStatus());
    }
}

3 复杂查询

3.1 整体介绍

下面介绍复杂查询,也是我们比较常用的ES查询方式,这里如果大家有一定的ES语法基础会更方便的理解下面的内容,想了解ES基础语法可以去看我的这篇文章:

【ES知识】ES基础查询语法一览https://blog.csdn.net/qq_34263207/article/details/127849806

老规矩,先上图:

 该图描绘了我们在复杂查询时会涉及到的类:一样是使用RestHighLevelClient,然后通过SearchRequest进行操作,SearchRequest又需要SearchSourceBuilder,而SearchSourceBuilder需要通过QueryBuilder完成构建,图中最下面一行列出了几种常用的QueryBuilder,基本上和我们ES查询的语法是直接相关的。最后SearchResponse返回结果。

更多优质文章资源请关注同名公众号:老坛聊开发

下面给出查询的模板代码:

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {

    @Resource
    RestHighLevelClient restHighLevelClient;

    String index = "index";

    @Test
    public List<TextBook> grepTextBook() throws IOException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "老坛");
        searchSourceBuilder.query(matchQueryBuilder);
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(index);
        searchRequest.source(searchSourceBuilder);
        List<TextBook> textBookList = new ArrayList<>();
        //执行操作
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        if (Objects.nonNull(searchResponse)) {
            SearchHits searchHits = searchResponse.getHits();
            for (int i = 0; i < searchHits.getHits().length; i++) {
                String str = searchHits.getHits()[i].getSourceAsString();
                System.out.println(str);
                textBookList.add(JSON.parseObject(str, TextBook.class));
            }
        }
        return textBookList;
    }
}

整个查询代码的核心实际上只有这一行:

MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "老坛");

因为只有QueryBuilder决定了你要怎么去进行查询,其它的都是模板,可以不动。

3.2 QueryBuilder说明

下面我们来一一介绍几种常用的QueryBuilder:

3.2.1 MatchQueryBuilder

MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "老坛");

对应了ES的match查询,它等价的ES语法就是:

GET textbook/_search
{
  "query": {
    "match": {
      "author":"老坛"
    }
  }
}

3.2.2 MatchPhraseQueryBuilder

MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("bookName", "老坛");

对应了ES的match_phrase查询,它等价的ES语法就是:

GET textbook/_search
{
  "query": {
    "match_phrase": {
      "bookName":"老坛"
    }
  }
}

3.2.3 MultiMatchQueryBuilder

MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("老坛", "author","bookName");

对应了ES的multi_match查询,它等价的ES语法就是:

GET textbook/_search
{
	"query": {
		"multi_match": {
			"query": "老坛",
			"fields": ["author","bookName"]
		}
	}
}

3.2.4 TermQueryBuilder

TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("author", "老坛");

对应了ES的term查询,它等价的ES语法就是:

GET textbook/_search
{
	"query": {
		"term": {
			"author": {
				"value": "老坛"
			}
		}
	}
}

3.2.5 TermsQueryBuilder

TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("author", "老","坛");

对应了ES的terms查询,它等价的ES语法就是:

GET textbook/_search
{
	"query": {
		"terms": {
			"author": ["老","坛"]
		}
	}
}

3.2.6 FuzzyQueryBuilder

FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("bookName", "老坛");

对应了ES的fuzzy查询,它等价的ES语法就是:

GET textbook/_search
{
    "query":{
        "fuzzy":{
            "bookName":"老坛"
        }
    }
}

我们只要根据自己需要选择不同的builder去查询就可以了

3.2.7 RangeQueryBuilder

RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("num")
                .gt(10)
                .lt(20);

对应了ES的range查询,它等价的ES语法就是:

GET textbook/_search
{
    "query":{
        "range":{
            "num":{
                "lt":20,
                "gt":10
            }
        }
    }
}

更多优质文章资源请关注同名公众号:老坛聊开发

3.3 bool查询

bool查询也是我们使用es中比较常见的,当我们需要查询多个条件时,就会需要:

MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("bookName", "老坛");
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("author", "老坛");
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
    .must(matchQueryBuilder)
    .should(termQueryBuilder);

所对应的ES语法为:

GET textbook/_search
{
    "query":{
        "bool":{
            "must":{
                "match":{
                    "bookName":"老坛"
                }
            },
            "should":{
                "term":{
                    "author":"老坛"
                }
            }
        }
    }
}

3.4 排序和分页

还用最开始的match举例,这里在此基础上添加了排序和分页两个限制:

@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ESTest {

    @Resource
    RestHighLevelClient restHighLevelClient;

    String index = "index";

    @Test
    public List<TextBook> grepTextBook() throws IOException {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("bookName", "老坛");
        searchSourceBuilder.query(matchQueryBuilder);
        // 分页参数
        searchSourceBuilder.sort("num", SortOrder.DESC);
        searchSourceBuilder.from(1);
        searchSourceBuilder.size(100);
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(index);
        searchRequest.source(searchSourceBuilder);
        List<TextBook> textBookList = new ArrayList<>();
        //执行操作
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        if (Objects.nonNull(searchResponse)) {
            SearchHits searchHits = searchResponse.getHits();
            for (int i = 0; i < searchHits.getHits().length; i++) {
                String str = searchHits.getHits()[i].getSourceAsString();
                System.out.println(str);
                textBookList.add(JSON.parseObject(str, TextBook.class));
            }
        }
        return textBookList;
    }
}

排序和分页主要体现在这几行:

// 分页参数
searchSourceBuilder.sort("num", SortOrder.DESC);
searchSourceBuilder.from(1);
searchSourceBuilder.size(100);

可以看到这是一个按照num字段的降序搜索,并且是按照页容量为100进行分页,取第二页。

所对应的ES语法为:

GET textbook/_search
{
    "query":{
        "match":{
            "bookName":"老坛"
        }
    },
    "from":0,
    "size":100,
    "sort":{
        "num":{
            "order":"desc"
        }
    }
}

4.代码模板

本篇文章中介绍的全部es操作老坛已整理成模板项目了:

请关注同名公众号:老坛聊开发

并回复:"7.x模板" 获取代码模板

只要大家根据自己的实际环境修改配置即可直接跑起来啦,亲测有效!

5. 总结

到这里Java对ES的操作基本上聊的差不多了,题主这里介绍的未必详尽,只是一些我们通常会用到的操作,如果还想详细了解更多的内容请阅读官方文档:

https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/7.17/java-client-javadoc.html

另外,题主这里只是为了给大家讲明白如何使用举了几个例子,并不一定效率最高或者使用常见最为恰当,还是需要大家学习一下ES的语法根据自己的实际业务场景去选用,谢谢大家~

有关【ES使用】Java API操作ES宝典(7.x版本及其以下)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐